From 5de8e59e481820f81acd0de3d9788c04bdd400b0 Mon Sep 17 00:00:00 2001 From: Ryan Lanciaux Date: Sat, 13 Feb 2016 00:36:41 -0500 Subject: [PATCH 01/16] Functional componentize column --- package.json | 3 +- src/column.js | 119 ++++++++++++++++++++----------------------------- src/griddle.js | 8 ++++ 3 files changed, 59 insertions(+), 71 deletions(-) diff --git a/package.json b/package.json index 1f37722..7c48a1f 100644 --- a/package.json +++ b/package.json @@ -36,6 +36,7 @@ "dependencies": { "classnames": "^2.2.3", "object-assign": "*", - "react": "^0.14.0" + "react": "^0.14.0", + "recompose": "^0.15.0" } } diff --git a/src/column.js b/src/column.js index 8a31bcf..0919e27 100644 --- a/src/column.js +++ b/src/column.js @@ -1,89 +1,68 @@ -import React from 'react'; +import React, { PropTypes } from 'react'; import classnames from 'classnames'; +import { compose, shouldUpdate, getContext, mapProps } from 'recompose'; import { getStyleProperties } from './utils/styleHelper'; -class Column extends React.Component { - constructor(props, context) { - super(props, context); - } - shouldComponentUpdate(nextProps) { - if(this.props.forceUpdate) { return true; } - if(this.props.value === nextProps.value) { - return false; - } +const Column = compose( + //Only update if forceUpdate is true or the values don't match + shouldUpdate(({ value }, nextProps) => (nextProps.value !== value || nextProps.forceUpdate)), - return true; - } + //We are using the following contexts: + getContext({ yo: PropTypes.string}), - render() { - //TODO: this is temporary -- we'll need to merge styles or something - // why not use the getStyle from defaultStyles? - const styles = this._getStyles(); + //Build new props in addition to the ones that are passed in + mapProps( props => ({ + classNames: classnames(getStyleProperties(props, 'column'), props.cssClassName), - const { className } = getStyleProperties(this.props, 'column'); - const classNames = classnames(className, this.props.cssClassName); - - return ( - - {this.props.hasOwnProperty('customComponent') ? - : - this.props.value} - - ); - } - - //TODO: Figure out a way to get this hooked up with the normal styles methods - //maybe a merge styles property or something along those lines - _getStyles = () => { - const style = this.props.styles.getStyle({ - styles: this.props.styles.inlineStyles, + //This is the inline styles object to use + columnStyles: props.styles.getStyle({ + styles: props.styles.inlineStyles, styleName: 'column', - //todo: make this nicer mergeStyles: { - ...((this.props.width || this.props.alignment || this.props.styles) ? - Object.assign({ width: this.props.width || null, textAlign: this.props.alignment }) : {}), - ...this.props.style + ...((props.width || props.alignment || props.styles) ? + Object.assign({ width: props.width || null, + textAlign: props.alignment }) : {}) } - }); + }), - return style; - } + //Click callback + handleClick: (e) => { + if (props.onClick) { props.onClick(e) }; - _handleClick = (e) => { - if (this.props.onClick) this.props.onClick(e); + props.events.columnClick(props.dataKey, props.value, props.rowIndex, props.rowData); + }, - this.props.events.columnClick(this.props.dataKey, this.props.value, this.props.rowIndex, this.props.rowData); - } + //hover callback + handleHover: (e) => { + props.events.columnHover(props.dataKey, props.value, props.rowIndex, props.rowData); + }, - _handleHover = (e) => { - this.props.events.columnHover(this.props.dataKey, this.props.value, this.props.rowIndex, this.props.rowData); - } -} + columnValue: (props.hasOwnProperty('customComponent') ? + : + props.value), -Column.defaultProps = { - columnProperties: { - cssClassName: '' - } -}; + //Return all the props + ...props + })) +)(({ columnValue, handleHover, handleClick, classNames, columnStyles, dataKey, rowIndex }) => ( + + {columnValue} + -Column.propTypes = { - alignment: React.PropTypes.oneOf(['left', 'right', 'center']), - columnHover: React.PropTypes.func, - columnClick: React.PropTypes.func -}; +)) export default Column; diff --git a/src/griddle.js b/src/griddle.js index 18b4d16..2d87447 100644 --- a/src/griddle.js +++ b/src/griddle.js @@ -7,6 +7,10 @@ import * as defaultSettings from './defaultSettings'; import { arraysEqual } from './utils/arrayHelper'; export default class Griddle extends React.Component { + static childContextTypes = { + yo: React.PropTypes.string + } + constructor(props, context) { super(props, context); @@ -14,6 +18,10 @@ export default class Griddle extends React.Component { this.state = { showSettings: false }; } + getChildContext() { + return { yo: "hi" }; + } + wireUpSettings = (props) => { this.settings = Object.assign({}, defaultSettings, props.settings); this.components = Object.assign({}, defaultModules, props.components); From 79de061c5ab8cfc9b7391be3e60404ac9165f390 Mon Sep 17 00:00:00 2001 From: Ryan Lanciaux Date: Sat, 13 Feb 2016 00:44:34 -0500 Subject: [PATCH 02/16] Move style helper to context --- src/column.js | 7 ++----- src/griddle.js | 9 +++++++-- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/src/column.js b/src/column.js index 0919e27..58ea039 100644 --- a/src/column.js +++ b/src/column.js @@ -2,19 +2,16 @@ import React, { PropTypes } from 'react'; import classnames from 'classnames'; import { compose, shouldUpdate, getContext, mapProps } from 'recompose'; -import { getStyleProperties } from './utils/styleHelper'; - - const Column = compose( //Only update if forceUpdate is true or the values don't match shouldUpdate(({ value }, nextProps) => (nextProps.value !== value || nextProps.forceUpdate)), //We are using the following contexts: - getContext({ yo: PropTypes.string}), + getContext({ utils: PropTypes.object }), //Build new props in addition to the ones that are passed in mapProps( props => ({ - classNames: classnames(getStyleProperties(props, 'column'), props.cssClassName), + classNames: classnames(props.utils.getStyleProperties(props, 'column'), props.cssClassName), //This is the inline styles object to use columnStyles: props.styles.getStyle({ diff --git a/src/griddle.js b/src/griddle.js index 2d87447..467c4c3 100644 --- a/src/griddle.js +++ b/src/griddle.js @@ -5,10 +5,11 @@ import * as defaultModules from './defaultModules'; import { getAssignedStyles } from './defaultStyles'; import * as defaultSettings from './defaultSettings'; import { arraysEqual } from './utils/arrayHelper'; +import { getStyleProperties } from './utils/styleHelper'; export default class Griddle extends React.Component { static childContextTypes = { - yo: React.PropTypes.string + utils: React.PropTypes.object } constructor(props, context) { @@ -19,7 +20,11 @@ export default class Griddle extends React.Component { } getChildContext() { - return { yo: "hi" }; + return { + utils: { + getStyleProperties + } + }; } wireUpSettings = (props) => { From 1f50ed369197ca0ad33dbf8c068630aa11179e10 Mon Sep 17 00:00:00 2001 From: Ryan Lanciaux Date: Sat, 13 Feb 2016 01:13:25 -0500 Subject: [PATCH 03/16] Recompose filter component --- src/defaultSettings.js | 3 +- src/filter.js | 67 +++++++++++++++++++++++------------------- 2 files changed, 38 insertions(+), 32 deletions(-) diff --git a/src/defaultSettings.js b/src/defaultSettings.js index 25a32d4..0dac162 100644 --- a/src/defaultSettings.js +++ b/src/defaultSettings.js @@ -2,5 +2,6 @@ export default { useGriddleStyles: true, useGriddleClassNames: false, useFixedTable: true, - pageSize: 10 + pageSize: 10, + filterPlaceholderText: "Filter" } diff --git a/src/filter.js b/src/filter.js index dfe1587..ce2d61b 100644 --- a/src/filter.js +++ b/src/filter.js @@ -1,36 +1,41 @@ -import React from 'react'; +import React, { PropTypes } from 'react'; import { getStyleProperties } from './utils/styleHelper'; -class Filter extends React.Component { - constructor(props, context) { - super(props, context); - - this._handleChange = this._handleChange.bind(this); - } - - shouldComponentUpdate(props) { - return false; - } - - render() { - const {style, className } = getStyleProperties(this.props, 'filter'); - - return ( - - ); - } - - _handleChange = (e) => { - //TODO: debounce this - this.props.events.setFilter(e.target.value); - } -} +import { compose, shouldUpdate, getContext, mapProps } from 'recompose'; + +const Filter = compose( + //this should not update + shouldUpdate(() => (false)), + + //we are using the utils object from context + getContext({ utils: PropTypes.object }), + + //Get styleProperties for the next mapProps + mapProps((props) => ({ + styleProperties: props.utils.getStyleProperties(props, 'filter'), + + ...props + })), + + mapProps((props) => ({ + handleChange: ((e) => { props.events.setFilter(e.target.value) }), + + style: props.styleProperties.style, + + className: props.styleProperties.className, + + placeholder: props.settings.filterPlaceholderText || "Filter", + })) +)(({className, style, handleChange, placeholder}) => ( + +)); Filter.propTypes = { setFilter: React.PropTypes.func From c7245af10303b32e1595ab4570dee17af6a97087 Mon Sep 17 00:00:00 2001 From: Ryan Lanciaux Date: Sat, 13 Feb 2016 15:25:27 -0500 Subject: [PATCH 04/16] Switch row to functional component --- src/griddle.js | 5 ++- src/row.js | 101 +++++++++++++++++++++---------------------------- 2 files changed, 47 insertions(+), 59 deletions(-) diff --git a/src/griddle.js b/src/griddle.js index 467c4c3..16a05f2 100644 --- a/src/griddle.js +++ b/src/griddle.js @@ -6,6 +6,7 @@ import { getAssignedStyles } from './defaultStyles'; import * as defaultSettings from './defaultSettings'; import { arraysEqual } from './utils/arrayHelper'; import { getStyleProperties } from './utils/styleHelper'; +import ColumnHelper from './utils/column-helper'; export default class Griddle extends React.Component { static childContextTypes = { @@ -22,7 +23,9 @@ export default class Griddle extends React.Component { getChildContext() { return { utils: { - getStyleProperties + getStyleProperties, + isColumnVisible: ColumnHelper.isColumnVisible, + getColumnPropertyObject: ColumnHelper.getColumnPropertyObject } }; } diff --git a/src/row.js b/src/row.js index 4e10dd6..18803ba 100644 --- a/src/row.js +++ b/src/row.js @@ -1,71 +1,56 @@ -import React from 'react'; -import ColumnHelper from './utils/column-helper'; -import { getStyleProperties } from './utils/styleHelper'; - -class Row extends React.Component { - constructor(props, context) { - super(props, context); - } - - render() { - //TODO: Refactor this -- the logic to show / hide columns is kind of rough - // Also, it seems that if we moved this operation to a store, it could be a bit faster - let columns = []; - const { columnProperties, - ignoredColumns, - tableProperties, - rowData, - events, - originalRowData, - rowIndex, - absoluteRowIndex } = this.props; - const { griddleKey } = rowData.__metadata; - - //render just the columns that are contained in the metdata - for (var column in rowData) { - //get the additional properties defined in the creation of the object - let columnProperty = ColumnHelper.getColumnPropertyObject(columnProperties, column); - //render the column if there are no properties, there are properties and the column is in the collection OR there are properties and no column properties. - if(ColumnHelper.isColumnVisible(column, {columnProperties, ignoredColumns: ignoredColumns || []})) { - columns.push( ({ + styleProperties: props.utils.getStyleProperties(props, 'row'), + + ...props + })), + + mapProps(props => ({ + onClick: () => { props.events.rowClick(props.rowData, props.originalRowData); }, + + //this is the set of columns to render. we need to determine if there are + //any columns that should be treated as metadata and ignore them. + columns: (Object.keys(props.rowData) + .filter(column => (props.utils.isColumnVisible(column, + {columnProperties: props.columnProperties, ignoredColumns: props.ignoredColumns || []} + ))) + .map(column => { + //get the additional column properties + const columnProperty = props.utils.getColumnPropertyObject(props.columnProperties, column) + return ( + ); - } - } - - const { style, className } = getStyleProperties(this.props, 'row'); - return ( + /> + ); + })), + style: props.styleProperties.style, + className: props.styleProperties.className, + ...props + })) +)(({ className, style, onClick, columns, griddleKey }) => ( {columns} - ); - } - - //TODO: this can go -- double confirm that nothing is using it - _handleHover = (e) => { - this.props.events.rowHover(this.props.rowIndex, this.props.rowData); - } - - //TODO: this can go -- double confirm that nothing is using it - _handleSelect = (e) => { - this.props.events.rowSelect(this.props.rowIndex, this.props.rowData); - } - _onClick = () => { - this.props.events.rowClick(this.props.rowData, this.props.originalRowData) - } -} +)); export default Row; From 3b20aaef4da0d8417cefac84f5fd2919951a6357 Mon Sep 17 00:00:00 2001 From: Ryan Lanciaux Date: Sun, 14 Feb 2016 00:45:00 -0500 Subject: [PATCH 05/16] Switch table heading to a functional component --- src/column.js | 4 +- src/griddle.js | 3 +- src/table-heading-cell.js | 1 - src/table-heading.js | 128 ++++++++++++++++++++------------------ 4 files changed, 70 insertions(+), 66 deletions(-) diff --git a/src/column.js b/src/column.js index 58ea039..546e2eb 100644 --- a/src/column.js +++ b/src/column.js @@ -4,13 +4,13 @@ import { compose, shouldUpdate, getContext, mapProps } from 'recompose'; const Column = compose( //Only update if forceUpdate is true or the values don't match - shouldUpdate(({ value }, nextProps) => (nextProps.value !== value || nextProps.forceUpdate)), + shouldUpdate(({ value }, nextProps) => (nextProps.value !== value || nextProps.forceUpdate === true )), //We are using the following contexts: getContext({ utils: PropTypes.object }), //Build new props in addition to the ones that are passed in - mapProps( props => ({ + mapProps(props => ({ classNames: classnames(props.utils.getStyleProperties(props, 'column'), props.cssClassName), //This is the inline styles object to use diff --git a/src/griddle.js b/src/griddle.js index 16a05f2..6edfda1 100644 --- a/src/griddle.js +++ b/src/griddle.js @@ -25,7 +25,8 @@ export default class Griddle extends React.Component { utils: { getStyleProperties, isColumnVisible: ColumnHelper.isColumnVisible, - getColumnPropertyObject: ColumnHelper.getColumnPropertyObject + getColumnPropertyObject: ColumnHelper.getColumnPropertyObject, + arraysEqual: arraysEqual } }; } diff --git a/src/table-heading-cell.js b/src/table-heading-cell.js index 3f8bf5d..578ec1f 100644 --- a/src/table-heading-cell.js +++ b/src/table-heading-cell.js @@ -5,7 +5,6 @@ import { getStyleProperties } from './utils/styleHelper'; class TableHeadingCell extends React.Component { constructor(props, context) { super(props, context); - this._handleClick = this._handleClick.bind(this); this._handleHover = this._handleHover.bind(this); } diff --git a/src/table-heading.js b/src/table-heading.js index ae31386..e866d43 100644 --- a/src/table-heading.js +++ b/src/table-heading.js @@ -1,76 +1,80 @@ -import React from 'react'; +import React, { PropTypes } from 'react'; import ColumnHelper from './utils/column-helper'; import { getStyleProperties } from './utils/styleHelper'; -import { arraysEqual } from './utils/arrayHelper';; - class TableHeading extends React.Component { - constructor(props, context) { - super(props, context); +import { compose, getContext, mapProps, shouldUpdate } from 'recompose'; - this.state = {}; - } +function getColumnTitle(column, props) { + const initial = props.columnTitles[column] ? + props.columnTitles[column] : + column; - shouldComponentUpdate(nextProps) { - //TODO: Make this nicer - shouldn't be reminiscent of clojure level paran usage - return (!arraysEqual(this.props.columns, nextProps.columns) || - ((this.props.pageProperties && nextProps.pageProperties) && - (!arraysEqual(this.props.pageProperties.sortColumns, nextProps.pageProperties.sortColumns) || - this.props.pageProperties.sortAscending !== nextProps.pageProperties.sortAscending) + return props.renderProperties.columnProperties[column] && + props.renderProperties.columnProperties[column].hasOwnProperty('displayName') ? + props.renderProperties.columnProperties[column].displayName : + initial +} + +const TableHeading = compose( + getContext({ utils: PropTypes.object }), + + shouldUpdate((props, nextProps) => ( + (!props.utils.arraysEqual(props.columns, nextProps.columns) || + ((props.pageProperties && nextProps.pageProperties) && + (!props.utils.arraysEqual(props.pageProperties.sortColumns, nextProps.pageProperties.sortColumns) || + props.pageProperties.sortAscending !== nextProps.pageProperties.sortAscending) ) - ); - } + ) + )), - getColumnTitle(column) { - const initial = this.props.columnTitles[column] ? - this.props.columnTitles[column] : - column; + mapProps((props) => ({ + styleProperties: props.utils.getStyleProperties(props, 'tableHeading'), + headingClick: props.events.headingClick, + headingHover: props.events.headingHover, + ...props + })), - return this.props.renderProperties.columnProperties[column] && this.props.renderProperties.columnProperties[column].hasOwnProperty('displayName') ? - this.props.renderProperties.columnProperties[column].displayName : - initial - } + mapProps(props => ({ + headings: props.columns + .filter(column => (props.utils.isColumnVisible(column, + { columnProperties: props.renderProperties.columnProperties, + ignoredColumns: props.renderProperties.ignoredColumns + }))) + .map(column => { + let columnProperty = props.utils.getColumnPropertyObject(props.renderProperties.columnProperties, column); + const sortAscending = props.pageProperties && props.pageProperties.sortAscending; + const sorted = props.pageProperties && props.pageProperties.sortColumns.indexOf(column) > -1; - render() { - let { headingClick, headingHover } = this.props.events; - const { renderProperties } = this.props; - const { style, className } = getStyleProperties(this.props, 'tableHeading'); + const title = getColumnTitle(column, props); + return { - let columnProperty = ColumnHelper.getColumnPropertyObject(renderProperties.columnProperties, column); - const showColumn = ColumnHelper.isColumnVisible(column, { columnProperties: renderProperties.columnProperties, ignoredColumns: renderProperties.ignoredColumns }); - const sortAscending = this.props.pageProperties && this.props.pageProperties.sortAscending; - const sorted = this.props.pageProperties && this.props.pageProperties.sortColumns.indexOf(column) > -1 + columnProperty={columnProperty} + {...columnProperty} - const title = this.getColumnTitle(column); - let component = null; - if(showColumn) { - component = (); - } + {...props} + /> + }), - return component; - }); + style: props.styleProperties.style, - return this.props.columns.length > 0 ? ( - - - {headings} - - - ) : null; - } -} + className: props.styleProperties.className + })) +)(({ style, className, headings, ...props }) => ( + + + {headings} + + +)); - export default TableHeading; +export default TableHeading; From 6872c71b1cfb3dcb78aa4dd086e5a55782ca9d0b Mon Sep 17 00:00:00 2001 From: Ryan Lanciaux Date: Sun, 14 Feb 2016 01:32:20 -0500 Subject: [PATCH 06/16] Switch heading-cell to function component --- src/table-heading-cell.js | 110 +++++++++++++++++--------------------- 1 file changed, 48 insertions(+), 62 deletions(-) diff --git a/src/table-heading-cell.js b/src/table-heading-cell.js index 578ec1f..da2692f 100644 --- a/src/table-heading-cell.js +++ b/src/table-heading-cell.js @@ -1,79 +1,65 @@ -import React from 'react'; +import React, { PropTypes } from 'react'; import classnames from 'classnames'; -import { getStyleProperties } from './utils/styleHelper'; -class TableHeadingCell extends React.Component { - constructor(props, context) { - super(props, context); - this._handleClick = this._handleClick.bind(this); - this._handleHover = this._handleHover.bind(this); - } +import { compose, getContext, mapProps } from 'recompose'; + +function isSortable(props) { + const { column, renderProperties } = props; + const columnProperties = renderProperties.columnProperties[column]; - getSortIcon() { - const { sorted, sortAscending, icons } = this.props; + if(columnProperties && + columnProperties.hasOwnProperty('sortable') && + columnProperties.sortable === false) { - if (sorted) { - return sortAscending ? icons.sortAscending : icons.sortDescending; - } + return false; } - isSortable() { - const { column, renderProperties } = this.props; - const columnProperties = renderProperties.columnProperties[column]; + return true; +} - if(columnProperties && columnProperties.hasOwnProperty('sortable') && columnProperties.sortable === false) { - return false; - } +const TableHeadingCell = compose( + getContext({ utils: PropTypes.object }), - return true; - } + mapProps(props => ({ + sortable: isSortable(props), + ...props + })), + mapProps(props => ({ + handleClick: (props.sortable ? (() => props.headingClick(props.column)) : null), - render() { - const style = this.props.styles.getStyle({ - styles: this.props.styles.inlineStyles, + handleHover: props.events.headingHover(props.column), + + sortIcon: props.sorted ? + (props.sortAscending ? props.icons.sortAscending : props.icons.sortDescending) : + null, + + style: props.styles.getStyle({ + styles: props.styles.inlineStyles, styleName: 'columnTitle', mergeStyles: { - width: this.props.columnProperty.width, - ...(this.props.alignment || this.props.headerAlignment ? {textAlign: this.props.headerAlignment || this.props.alignment} : {}), - ...this.props.style + width: props.columnProperty.width, + ...(props.alignment || props.headerAlignment ? {textAlign: props.headerAlignment || props.alignment} : {}), + ...props.style } - }); - - const { className } = getStyleProperties(this.props, 'tableHeadingCell'); - const classNames = classnames(className, this.props.columnProperty ? this.props.columnProperty.headerCssClassName : null) - const { sorted } = this.props; - const clickEvent = this.isSortable() ? this._handleClick : null; + }), - return ( - - {this.props.title} { this.getSortIcon() } - ); - } - - _handleHover() { - this.props.headingHover(this.props.column); - } - - _handleClick() { - this.props.headingClick(this.props.column); - } -} + className: classnames( + props.utils.getStyleProperties(props, 'tableHeadingCell'), + props.columnProperty ? props.columnProperty.headerCssClassName : null), -TableHeadingCell.propTypes = { - headingHover: React.PropTypes.func, - headingClick: React.PropTypes.func, - column: React.PropTypes.string, - headerAlignment: React.PropTypes.oneOf(['left', 'right', 'center']), - alignment: React.PropTypes.oneOf(['left', 'right', 'center']), - sortAscending: React.PropTypes.bool, - sorted: React.PropTypes.bool -}; + ...props + })) +)(({ column, style, handleHover, handleClick, title, sortIcon, className}) => ( + + { title } { sortIcon } + +)) export default TableHeadingCell; From cc94794bc130415b93bee6053ef471bbd7e0e3cc Mon Sep 17 00:00:00 2001 From: Ryan Lanciaux Date: Sun, 14 Feb 2016 16:23:42 -0500 Subject: [PATCH 07/16] Switch loading component to recompose --- src/loading.js | 38 +++++++++++++++++++++++--------------- 1 file changed, 23 insertions(+), 15 deletions(-) diff --git a/src/loading.js b/src/loading.js index 98a2710..9f6b0ea 100644 --- a/src/loading.js +++ b/src/loading.js @@ -1,20 +1,28 @@ -import React from 'react'; +import React, { PropTypes } from 'react'; import {getStyleProperties} from './utils/styleHelper'; -class Loading extends React.Component { - render() { - const { style, className } = getStyleProperties(this.props, 'loading'); +import { compose, getContext, mapProps } from 'recompose'; - return ( - - -
-

Loading...

-
- - - ); - } -} +const Loading = compose( + getContext({ utils: PropTypes.object }), + + mapProps(props => ({ + styleProperties: props.utils.getStyleProperties(props, 'loading'), + ...props + })), + + mapProps(props => ({ + style: props.styleProperties.style, + className: props.styleProperties.className + })) +)(({ style, className }) => ( + + +
+

Loading...

+
+ + +)) export default Loading; From 25fe9c0c362af70889d486462b8952315cd1c7c0 Mon Sep 17 00:00:00 2001 From: Ryan Lanciaux Date: Sun, 14 Feb 2016 16:27:25 -0500 Subject: [PATCH 08/16] Switch no-results to function component --- src/no-results.js | 30 +++++++++++++++++++----------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/src/no-results.js b/src/no-results.js index 7d7eb8d..ac294a1 100644 --- a/src/no-results.js +++ b/src/no-results.js @@ -1,16 +1,24 @@ -import React from 'react'; +import React, { PropTypes } from 'react'; import {getStyleProperties} from './utils/styleHelper'; -class NoResults extends React.Component { - render() { - const { style, className } = getStyleProperties(this.props, 'noResults'); +import { compose, getContext, mapProps } from 'recompose'; - return ( -
-

No results found.

-
- ) - } -} +const NoResults = compose( + getContext({ utils: PropTypes.object }), + + mapProps(props => ({ + styleProperties: props.utils.getStyleProperties(props, 'noResults'), + ...props + })), + + mapProps(props => ({ + style: props.styleProperties.style, + className: props.styleProperties.className + })) +)(({ style, className }) => ( +
+

No results found.

+
+)) export default NoResults; From 00a7c8f81151c973db3559b767f46595e1ca8eae Mon Sep 17 00:00:00 2001 From: Ryan Lanciaux Date: Sun, 14 Feb 2016 16:45:27 -0500 Subject: [PATCH 09/16] Switch to function component --- src/pagination.js | 65 +++++++++++++++++++++++++++-------------------- 1 file changed, 37 insertions(+), 28 deletions(-) diff --git a/src/pagination.js b/src/pagination.js index d8cb2cb..f9149bd 100644 --- a/src/pagination.js +++ b/src/pagination.js @@ -1,38 +1,47 @@ -import React from 'react' +import React, { PropTypes } from 'react' import { getStyleProperties } from './utils/styleHelper'; -class Pagination extends React.Component { - constructor(props, context) { - super(props, context); +import { compose, getContext, mapProps } from 'recompose'; - this._handleChange = this._handleChange.bind(this); - } - - render() { - const {style, className } = getStyleProperties(this.props, 'pagination'); +function getPageNumberDropdown(props) { + if(!props.pageProperties || !props.pageProperties.maxPage ) { return; } - return (
- {this.props.hasPrevious ? : null } - {this._getSelect()} - {this.props.hasNext ? : null } -
); - } + //Make this nicer + var options = []; - _handleChange(e) { - this.props.events.getPage(parseInt(e.target.value)); + for(var i = 1; i <= props.pageProperties.maxPage; i++) { + options.push() } - _getSelect() { - if( !this.props.pageProperties || !this.props.pageProperties.maxPage ) { return; } - //Make this nicer - var options = []; - - for(var i = 1; i <= this.props.pageProperties.maxPage; i++) { - options.push() - } - - return - } + return ( + + ); } +const Pagination = compose( + getContext({ utils: PropTypes.object }), + + mapProps(props => ({ + styleProperties: props.utils.getStyleProperties(props, 'pagination'), + handleChange: (e) => props.events.getPage(parseInt(e.target.value)), + ...props + })), + + mapProps(props => ({ + pageNumberDropdown: getPageNumberDropdown(props), + style: props.styleProperties.style, + className: props.styleProperties.className, + ...props + })) +)(({ style, className, pageNumberDropdown, handleChange, hasNext, hasPrevious, events }) => ( +
+ { hasPrevious ? : null } + {pageNumberDropdown} + { hasNext ? : null } +
+)) + export default Pagination; From 1197889b682c990f01144e934ee19584f7ab3199 Mon Sep 17 00:00:00 2001 From: Ryan Lanciaux Date: Sun, 14 Feb 2016 17:19:05 -0500 Subject: [PATCH 10/16] Switch settings toggle component to function --- src/settings-toggle.js | 51 +++++++++++++++++++++--------------------- 1 file changed, 25 insertions(+), 26 deletions(-) diff --git a/src/settings-toggle.js b/src/settings-toggle.js index ecb55bb..7f2bfa5 100644 --- a/src/settings-toggle.js +++ b/src/settings-toggle.js @@ -1,34 +1,33 @@ -import React from 'react'; -import { getStyleProperties } from './utils/styleHelper'; +import React, { PropTypes } from 'react'; import classnames from 'classnames'; +import { compose, shouldUpdate, getContext, mapProps, withState } from 'recompose'; -class SettingsToggle extends React.Component { - constructor() { - super(); - this.state = {}; - this.state.toggled = false; +const SettingsToggle = compose( + getContext({ utils: PropTypes.object }), - this._handleButton = this._handleButton.bind(this); - } + shouldUpdate(() => (false)), - shouldComponentUpdate() { - return false; - } + withState('toggled', 'setToggled', false), - render() { - const { style, className } = getStyleProperties(this.props, 'settingsToggle'); - const toggleClass = classnames(this.state.toggled ? 'toggled' : 'not-toggled', className); + mapProps(props => ({ + styleProperties: props.utils.getStyleProperties(props, 'settingsToggle'), + ...props + })), - return ; - } - - //this should keep track locally if it's toggled - //and just send whether or not settings should be shown - _handleButton() { - const toggled = !this.state.toggled; - this.props.showSettings(toggled); - this.setState({toggled: toggled}); - } -} + mapProps(props => ({ + toggleSettings: () => { + const showSettings = !props.toggled; + props.setToggled(showSettings); + props.showSettings(showSettings); + }, + style: props.styleProperties.style, + className: classnames(props.toggled ? 'toggled' : 'not-toggled', props.styleProperties.className), + ...props + })) +)(( { style, toggleSettings, className }) => ( + +)) export default SettingsToggle; From 8d80179e5deb27980e5be73eae748d498ae00597 Mon Sep 17 00:00:00 2001 From: Ryan Lanciaux Date: Sun, 14 Feb 2016 21:21:19 -0500 Subject: [PATCH 11/16] Switch some of the local settings components to functions --- src/settings.js | 65 ++++++++++++++++++++----------------------------- 1 file changed, 27 insertions(+), 38 deletions(-) diff --git a/src/settings.js b/src/settings.js index 1ab2f47..93b9fc9 100644 --- a/src/settings.js +++ b/src/settings.js @@ -1,50 +1,39 @@ import React from 'react'; import { getStyleProperties } from './utils/styleHelper'; -class CheckItem extends React.Component { - render() { - return ( - ); - } +import { compose, getContext, mapProps, shouldUpdate } from 'recompose'; - _handleClick = () => { - this.props.toggleColumn(this.props.name); - } -} +const CheckItem = mapProps(props => ({ + shouldUpdate: (() => false), -CheckItem.propTypes = { - toggleColumn: React.PropTypes.func.isRequired, - name: React.PropTypes.string.isRequired, - checked: React.PropTypes.bool, - value: React.PropTypes.oneOfType([ - React.PropTypes.string, - React.PropTypes.Number - ]), - text: React.PropTypes.string -}; + handleClick: () => props.toggleColumn(props.name), + ...props +}))(({ name, checked, value, text, handleClick}) => ( + +)) -class PageSize extends React.Component { - render() { - return ( - - ); - } +const PageSize = compose( + mapProps(props => ({ + defaultSizes: [5, 10, 20, 30, 50, 100], + handleChange: (e) => { props.setPageSize(parseInt(e.target.value)) }, + ...props + })), + mapProps(props => ({ + options: props.defaultSizes.map(size => ), + ...props + })) +)(({ handleChange, options }) => ( + +)); - _handleChange = (e) => { - this.props.setPageSize(parseInt(e.target.value)); - } -} - -PageSize.defaultProps = { - sizes: [5, 10, 20, 30, 50, 100] -} class Settings extends React.Component { render() { + const keys = Object.keys(this.props.renderProperties.columnProperties); const { style, className } = getStyleProperties(this.props, 'settings'); From ef3e983506069865b5e542462cd4936430e4269c Mon Sep 17 00:00:00 2001 From: Ryan Lanciaux Date: Mon, 15 Feb 2016 16:08:12 -0500 Subject: [PATCH 12/16] Use function component for settings --- src/settings.js | 78 +++++++++++++++++++++++++------------------------ 1 file changed, 40 insertions(+), 38 deletions(-) diff --git a/src/settings.js b/src/settings.js index 93b9fc9..ec3e4ec 100644 --- a/src/settings.js +++ b/src/settings.js @@ -1,8 +1,24 @@ -import React from 'react'; +import React, { PropTypes } from 'react'; import { getStyleProperties } from './utils/styleHelper'; import { compose, getContext, mapProps, shouldUpdate } from 'recompose'; +function getColumnDisplayName(props, column) { + const { renderProperties } = props; + + if(renderProperties.columnProperties.hasOwnProperty(column)) { + return renderProperties.columnProperties[column].hasOwnProperty('displayName') ? + renderProperties.columnProperties[column].displayName : + column + } else if (renderProperties.hiddenColumnProperties.hasOwnProperty(column)) { + return renderProperties.hiddenColumnProperties[column].hasOwnProperty('displayName') ? + renderProperties.hiddenColumnProperties[column].displayName : + column + } + + return column; +} + const CheckItem = mapProps(props => ({ shouldUpdate: (() => false), @@ -31,46 +47,32 @@ const PageSize = compose( )); -class Settings extends React.Component { - render() { +const Settings = compose( + getContext({ utils: PropTypes.object }), - const keys = Object.keys(this.props.renderProperties.columnProperties); - const { style, className } = getStyleProperties(this.props, 'settings'); + mapProps(props => ({ + styleProperties: props.utils.getStyleProperties(props, 'settings'), + keys: Object.keys(props.renderProperties.columnProperties), + ...props + })), - var columns = this.props.allColumns.map(column => + mapProps(props => ({ + style: props.styleProperties.style, + className: props.styleProperties.className, + columns: props.allColumns.map(column => -1} />); - - return ( -
- {columns} - -
- ); - } - - _getDisplayName = (column) => { - const { renderProperties } = this.props; - if(renderProperties.columnProperties.hasOwnProperty(column)) { - return renderProperties.columnProperties[column].hasOwnProperty('displayName') ? - renderProperties.columnProperties[column].displayName : - column - } else if (renderProperties.hiddenColumnProperties.hasOwnProperty(column)) { - return renderProperties.hiddenColumnProperties[column].hasOwnProperty('displayName') ? - renderProperties.hiddenColumnProperties[column].displayName : - column - } - - return column; - } -} - -Settings.propTypes = { - allColumns: React.PropTypes.arrayOf(React.PropTypes.string), - visibleColumns: React.PropTypes.arrayOf(React.PropTypes.node) -}; + text={getColumnDisplayName(props, column)} + checked={props.keys.indexOf(column) > -1} /> + ), + setPageSize: props.events.setPageSize + })) +)(({ style, className, columns, setPageSize}) => ( +
+ {columns} + +
+)); export default Settings; From cc04909bc61ff2d4983740b0d214328d75214f18 Mon Sep 17 00:00:00 2001 From: Ryan Lanciaux Date: Mon, 15 Feb 2016 16:39:29 -0500 Subject: [PATCH 13/16] Use function component for Table --- src/table.js | 67 +++++++++++++++++++++++----------------------------- 1 file changed, 30 insertions(+), 37 deletions(-) diff --git a/src/table.js b/src/table.js index 5380b2c..7b4ab82 100644 --- a/src/table.js +++ b/src/table.js @@ -1,47 +1,40 @@ -import React from 'react'; +import React, { PropTypes } from 'react'; import RowDefinition from './row-definition'; -import { getStyleProperties } from './utils/styleHelper'; +import { compose, getContext, mapProps } from 'recompose'; -class Table extends React.Component { - constructor(props, context) { - super(props, context); +const Table = compose( + getContext({ utils: PropTypes.object }), - this.state = {}; - } - - render() { - const { settings, styles } = this.props; - const style = styles.getStyle({ - styles: styles.inlineStyles, + mapProps(props => ({ + style: props.styles.getStyle({ + styles: props.styles.inlineStyles, styleName: 'table', - mergeStyles: settings.useFixedTable && styles.getStyle({ - styles: styles.inlineStyles, + mergeStyles: props.settings.useFixedTable && props.styles.getStyle({ + styles: props.styles.inlineStyles, styleName: 'fixedTable', }) - }); + }), - const { className } = getStyleProperties(this.props, 'table'); - //translate the definition object to props for Heading / Body - return this.props.data.length > 0 ? - ( - - - -
- ) : null; - } -} + className: props.utils.getStyleProperties(props, 'table').className, -//TODO: enabled the propTypes again -/* -Table.propTypes = { - children: React.PropTypes.oneOfType([ - React.PropTypes.instanceOf(RowDefinition) - // React.PropTypes.arrayOf(React.PropTypes.instanceOf(ColumnDefinition)) - ]) -}; */ + useFixedTable: props.settings.useFixedTable, + columns: props.data.length > 0 ? Object.keys(props.data[0]) : [], + TableHeading: props.components.TableHeading, + TableBody: props.components.TableBody, + data: props.data, + props: props, + })) +)(({data, className, style, columns, props, TableHeading, TableBody }) => ( + data.length > 0 ? + ( + + + +
+ ) : +)) export default Table; From 5f315e953ab03d8558faa91cf978d65bde12f9fe Mon Sep 17 00:00:00 2001 From: Ryan Lanciaux Date: Mon, 15 Feb 2016 17:08:22 -0500 Subject: [PATCH 14/16] Switch table-body to function component --- src/griddle.js | 9 ++++- src/table-body.js | 100 ++++++++++++++++++++++++++-------------------- 2 files changed, 65 insertions(+), 44 deletions(-) diff --git a/src/griddle.js b/src/griddle.js index 6edfda1..94bfc2f 100644 --- a/src/griddle.js +++ b/src/griddle.js @@ -26,7 +26,14 @@ export default class Griddle extends React.Component { getStyleProperties, isColumnVisible: ColumnHelper.isColumnVisible, getColumnPropertyObject: ColumnHelper.getColumnPropertyObject, - arraysEqual: arraysEqual + arraysEqual: arraysEqual, + getOriginalRowData: (rowIndex) => { + return this.props.state.data[rowIndex] + }, + getMetadata: (rowIndex) => { + const row = this.props.data[rowIndex]; + return row ? row.__metadata : {} + } } }; } diff --git a/src/table-body.js b/src/table-body.js index 6d64992..16e5436 100644 --- a/src/table-body.js +++ b/src/table-body.js @@ -1,46 +1,60 @@ -import React from 'react'; -import { getStyleProperties } from './utils/styleHelper'; - -class TableBody extends React.Component { - constructor(props, context) { - super(props, context); - } - - shouldComponentUpdate(nextProps) { - return this.props.data !== nextProps.data; - } - - render() { - const { data, loading, components, styles, settings, events, renderProperties, tableProperties } = this.props; - - const rows = loading ? - : data - .filter(data => data.visible === undefined || data.visible === true) - .map((data, index) => - - ); - - const { style, className } = getStyleProperties(this.props, 'tableBody'); - - return ( - - {rows} - - ); - } +import React, { PropTypes } from 'react'; +import { compose, shouldUpdate, mapProps, getContext } from 'recompose'; + +export function getRowsData(props, utils) { + const { data, loading, components, styles, settings, events, renderProperties, tableProperties } = props; + + + return loading ? + : + data + .filter(data => data.visible === undefined || data.visible === true) + .map((data, index) => { + const metadata = utils.getMetadata(index); + + return + }); } +const TableBody = compose( + getContext({ + utils: PropTypes.object, + }), + + shouldUpdate((props, nextProps) => ( props.data !== nextProps.data )), + + mapProps(props => ({ + styleProperties: props.utils.getStyleProperties(props, 'tableBody'), + ...props + })), + + mapProps(props => ({ + rows: getRowsData(props, props.utils), + style: props.styleProperties.style, + className: props.styleProperties.className + })) +)(({ style, className, rows }) => ( + + {rows} + +)); + export default TableBody; From cd2af1c4e90759f5c88f63848262363e8a70ae15 Mon Sep 17 00:00:00 2001 From: Ryan Lanciaux Date: Sat, 19 Mar 2016 16:33:40 -0400 Subject: [PATCH 15/16] Use selector props for table data --- src/table-body.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/table-body.js b/src/table-body.js index 16e5436..1e8fb4b 100644 --- a/src/table-body.js +++ b/src/table-body.js @@ -14,17 +14,18 @@ export function getRowsData(props, utils) { data .filter(data => data.visible === undefined || data.visible === true) .map((data, index) => { - const metadata = utils.getMetadata(index); + const metadata = props.metadata[index]; + const currentPageData = props.currentPageData[index]; return Date: Fri, 25 Mar 2016 00:37:42 -0400 Subject: [PATCH 16/16] use renderable columns --- src/griddle.js | 1 + src/settings.js | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/griddle.js b/src/griddle.js index 94bfc2f..51517a1 100644 --- a/src/griddle.js +++ b/src/griddle.js @@ -74,6 +74,7 @@ export default class Griddle extends React.Component { const events = this.getEvents(); const components = this.getComponents(); const { styles, settings } = this; + return (
{/*TODO: Lets not duplicate these prop defs all over (events/components) */} diff --git a/src/settings.js b/src/settings.js index ec3e4ec..a5985f6 100644 --- a/src/settings.js +++ b/src/settings.js @@ -59,7 +59,7 @@ const Settings = compose( mapProps(props => ({ style: props.styleProperties.style, className: props.styleProperties.className, - columns: props.allColumns.map(column => + columns: props.renderableColumns.map(column =>