-
Notifications
You must be signed in to change notification settings - Fork 2.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Better relationship display. #3063
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,7 @@ | ||
import React from 'react'; | ||
import { Columns } from 'FieldTypes'; | ||
import { Alert, Spinner } from 'elemental'; | ||
import { titlecase } from '../../../../utils/string'; | ||
|
||
const RelatedItemsList = React.createClass({ | ||
propTypes: { | ||
|
@@ -21,8 +22,10 @@ const RelatedItemsList = React.createClass({ | |
}, | ||
getColumns () { | ||
const { relationship, refList } = this.props; | ||
const columns = refList.expandColumns(refList.defaultColumns); | ||
return columns.filter(i => i.path !== relationship.refPath); | ||
return refList.expandColumns([refList.namePath, relationship.refPath]); | ||
}, | ||
getColumnType (type) { | ||
return Columns[type] || Columns.text; | ||
}, | ||
loadItems () { | ||
const { refList, relatedItemId, relationship } = this.props; | ||
|
@@ -45,51 +48,78 @@ const RelatedItemsList = React.createClass({ | |
this.setState({ items }); | ||
}); | ||
}, | ||
renderItems () { | ||
return this.state.items.results.length ? ( | ||
<div className="ItemList-wrapper"> | ||
<table cellPadding="0" cellSpacing="0" className="Table ItemList"> | ||
{this.renderTableCols()} | ||
{this.renderTableHeaders()} | ||
<tbody> | ||
{this.state.items.results.map(this.renderTableRow)} | ||
</tbody> | ||
</table> | ||
</div> | ||
) : ( | ||
<h4 className="Relationship__noresults">No related {this.props.refList.plural}</h4> | ||
); | ||
renderRelationshipColumn (item) { | ||
return <td key={'Relationship' + item.id || ''}>{this.props.relationship.label || titlecase(this.props.relationship.path)}</td>; | ||
}, | ||
renderTableCols () { | ||
const cols = this.state.columns.map((col) => <col width={col.width} key={col.path} />); | ||
return <colgroup>{cols}</colgroup>; | ||
renderReferenceListColumn (item) { | ||
const listHref = `${Keystone.adminPath}/${this.props.refList.path}`; | ||
return <td key={'Parent' + item.id} className="Relationship__link"><a href={listHref}>{this.props.refList.label}</a></td>; | ||
}, | ||
renderTableHeaders () { | ||
const cells = this.state.columns.map((col) => { | ||
return <th key={col.path}>{col.label}</th>; | ||
}); | ||
return <thead><tr>{cells}</tr></thead>; | ||
renderReferenceItemColumn (item) { | ||
const column = this.state.columns[0]; | ||
let ColumnType = this.getColumnType(column.type); | ||
const linkTo = `${Keystone.adminPath}/${this.props.refList.path}/${item.id}`; | ||
return <ColumnType key={column.path} list={this.props.refList} col={column} data={item} linkTo={linkTo} />; | ||
}, | ||
renderReferenceFieldColumn (item) { | ||
const linkTo = `${Keystone.adminPath}/${this.props.refList.path}/${item.id}`; | ||
const linkValue = this.state.columns[1] ? <a href={linkTo}>{this.state.columns[1].label}</a> : null; | ||
return <td key={'Field' + item.id} className="Relationship__link">{linkValue}</td>; | ||
}, | ||
renderReferenceFieldValueColumn (item) { | ||
const column = this.state.columns[1]; | ||
let ColumnType = this.getColumnType(column.type); | ||
const linkTo = `${Keystone.adminPath}/${this.props.refList.path}/${item.id}`; | ||
return <ColumnType key={column.path} list={this.props.refList} col={column} data={item} linkTo={linkTo} />; | ||
}, | ||
renderTableRow (item) { | ||
const cells = this.state.columns.map((col, i) => { | ||
const ColumnType = Columns[col.type] || Columns.__unrecognised__; | ||
const linkTo = !i ? `${Keystone.adminPath}/${this.props.refList.path}/${item.id}` : undefined; | ||
return <ColumnType key={col.path} list={this.props.refList} col={col} data={item} linkTo={linkTo} />; | ||
}); | ||
return <tr key={'i' + item.id}>{cells}</tr>; | ||
return ( | ||
<tr key={'table-row-item-' + item.id}>{[ | ||
this.renderRelationshipColumn(item), | ||
this.renderReferenceListColumn(item), | ||
this.renderReferenceItemColumn(item), | ||
this.renderReferenceFieldColumn(item), | ||
this.renderReferenceFieldValueColumn(item), | ||
]}</tr> | ||
); | ||
}, | ||
render () { | ||
if (this.state.err) { | ||
return <div className="Relationship">{this.state.err}</div>; | ||
} | ||
const listHref = `${Keystone.adminPath}/${this.props.refList.path}`; | ||
renderItems () { | ||
return this.state.items.results.map(this.renderTableRow); | ||
}, | ||
renderSpinner () { | ||
return <tr><td><Spinner size="sm" /></td></tr>; | ||
}, | ||
renderNoRelationships () { | ||
return ( | ||
<div className="Relationship"> | ||
<h3 className="Relationship__link"><a href={listHref}>{this.props.refList.label}</a></h3> | ||
{this.state.items ? this.renderItems() : <Spinner size="sm" />} | ||
</div> | ||
<tr> | ||
{this.renderRelationshipColumn({})} | ||
{this.renderReferenceListColumn({})} | ||
<td>None</td> | ||
<td></td> | ||
<td></td> | ||
</tr> | ||
); | ||
}, | ||
renderError () { | ||
return <tr><td>{this.state.err}</td></tr>; | ||
}, | ||
renderRelationshipTableBody () { | ||
const results = this.state.items && this.state.items.results; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. const results = loadedItems && loadedItems.results; |
||
let tbody = null; | ||
if (this.state.err) { | ||
tbody = this.renderError(); | ||
} else if (results && results.length) { | ||
tbody = this.renderItems(); | ||
} else if (results && !results.length) { | ||
tbody = this.renderNoRelationships(); | ||
} else { | ||
tbody = this.renderSpinner(); | ||
} | ||
return tbody; | ||
}, | ||
render () { | ||
return <tbody className="Relationship">{this.renderRelationshipTableBody()}</tbody>; | ||
}, | ||
}); | ||
|
||
module.exports = RelatedItemsList; |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -38,7 +38,7 @@ const ItemsRow = React.createClass({ | |
}); | ||
// item fields | ||
var cells = this.props.columns.map((col, i) => { | ||
var ColumnType = Columns[col.type] || Columns.__unrecognised__; | ||
var ColumnType = Columns[col.type] || Columns.text; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @JedWatson I had a KS frontend crash due to There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, there used to be an There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @JedWatson how would you handle cases where a list does not have a name mapping? In that case, the item ID, which doesn't not have a representative field type shows. That's what the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. just leaving a note here to clarify that this has been fixed in master |
||
var linkTo = !i ? `${Keystone.adminPath}/${this.props.list.path}/${itemId}` : undefined; | ||
return <ColumnType key={col.path} list={this.props.list} col={col} data={item} linkTo={linkTo} />; | ||
}); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -17,7 +17,7 @@ var TextColumn = React.createClass({ | |
render () { | ||
const value = this.getValue(); | ||
const empty = !value && this.props.linkTo ? true : false; | ||
const className = this.props.col.field.monospace ? 'ItemList__value--monospace' : undefined; | ||
const className = this.props.col.field && this.props.col.field.monospace ? 'ItemList__value--monospace' : undefined; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Had a crash here, too :) good to see this 👍 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If we're having crashes here, it means something lower level has gone horribly wrong - this check shouldn't be necessary, we should be validating it higher up There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @JedWatson this is the crash you get when it applies the |
||
return ( | ||
<ItemsTableCell> | ||
<ItemsTableValue className={className} href={this.props.linkTo} empty={empty} padded interior field={this.props.col.type}> | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
var React = require('react'); | ||
var renderToString = require('react-dom/server').renderToString; | ||
var ReactRouter = require('react-router'); | ||
|
||
var match = ReactRouter.match; | ||
var RoutingContext = ReactRouter.RoutingContext; | ||
|
||
module.exports = function createComponentRouter (routes) { | ||
return function componentRouter (req, res, next) { | ||
match({ routes: routes, location: req.url }, | ||
function (error, redirectLocation, renderProps) { | ||
if (error) return res.status(500).send(error.message); | ||
if (redirectLocation) { | ||
return res.redirect(302, redirectLocation.pathname + redirectLocation.search); | ||
} | ||
if (renderProps) { | ||
return res.render('default', { | ||
content: renderToString(React.createElement(RoutingContext, renderProps)), | ||
}); | ||
} | ||
next(null); | ||
} | ||
); | ||
}; | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
var keystone = require('../../../../index'); | ||
var Types = keystone.Field.Types; | ||
|
||
var NamelessRelationship = new keystone.List('NamelessRelationship'); | ||
|
||
NamelessRelationship.add({ | ||
fieldA: { | ||
type: Types.Relationship, | ||
ref: 'TargetRelationship', | ||
hidden: true, | ||
}, | ||
fieldB: { | ||
type: Types.Relationship, | ||
ref: 'TargetRelationship', | ||
many: true, | ||
}, | ||
}); | ||
|
||
NamelessRelationship.register(); | ||
NamelessRelationship.defaultColumns = 'fieldA, fieldB'; | ||
|
||
module.exports = NamelessRelationship; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A key of
i
could be non-unique, thus lead to confusion and unnecessary rerenders from React. Maybe use'table-row-item-' + item.id
or something like that?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually, keys need only be unique in the current parent, so just using
item.id
is fine (as long as that is defined).