-
Notifications
You must be signed in to change notification settings - Fork 95
/
Copy pathGridField.js
185 lines (163 loc) · 5.18 KB
/
GridField.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
/* global confirm */
import React, { Component } from 'react';
import i18n from 'i18n';
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import FormConstants from 'components/Form/FormConstants';
import * as actions from 'state/records/RecordsActions';
import castStringToElement from 'lib/castStringToElement';
import PropTypes from 'prop-types';
import GridFieldTable from './GridFieldTable';
import GridFieldHeader from './GridFieldHeader';
import GridFieldHeaderCell from './GridFieldHeaderCell';
import GridFieldRow from './GridFieldRow';
import GridFieldCell from './GridFieldCell';
import GridFieldAction from './GridFieldAction';
const NotYetLoaded = [];
/**
* The component acts as a container for a grid field,
* with smarts around data retrieval from external sources.
*
* @todo Convert to higher order component which hooks up form
* schema data to an API backend as a grid data source
* @todo Replace "dumb" inner components with third party library (e.g. https://griddlegriddle.github.io)
*/
class GridField extends Component {
constructor(props) {
super(props);
this.deleteRecord = this.deleteRecord.bind(this);
this.editRecord = this.editRecord.bind(this);
}
componentDidMount() {
const data = this.props.data;
this.props.actions.fetchRecords(
data.recordType,
data.collectionReadEndpoint.method,
data.collectionReadEndpoint.url
);
}
createRowActions(record) {
return (
<GridFieldCell className="grid-field__cell--actions" key="Actions">
<GridFieldAction
icon="cog"
onClick={this.editRecord}
record={record}
/>
<GridFieldAction
icon="cancel"
onClick={this.deleteRecord}
record={record}
/>
</GridFieldCell>
);
}
createCell(record, column) {
const handleDrillDown = this.props.data.onDrillDown;
const cellProps = {
className: handleDrillDown ? 'grid-field__cell--drillable' : '',
onDrillDown: handleDrillDown ? (event) => handleDrillDown(event, record) : null,
key: `${column.name}`,
width: column.width,
};
const val = column.field.split('.').reduce((a, b) => a[b], record);
return castStringToElement(GridFieldCell, val, cellProps);
}
/**
* Create a row components for the passed record.
*/
createRow(record) {
const rowProps = {
className: this.props.data.onDrillDown ? 'grid-field__row--drillable' : '',
key: `${record.ID}`,
};
const cells = this.props.data.columns.map((column) =>
this.createCell(record, column)
);
const rowActions = this.createRowActions(record);
return (
<GridFieldRow {...rowProps}>
{cells}
{rowActions}
</GridFieldRow>
);
}
/**
* @param {Event} event
* @param {number} id
*/
deleteRecord(event, id) {
event.preventDefault();
const headers = {};
headers[FormConstants.CSRF_HEADER] = this.props.config.SecurityID;
// eslint-disable-next-line no-alert
if (!confirm(
i18n._t('CampaignAdmin.DELETECAMPAIGN', 'Are you sure you want to delete this record?')
)) {
return;
}
this.props.actions.deleteRecord(
this.props.data.recordType,
id,
this.props.data.itemDeleteEndpoint.method,
this.props.data.itemDeleteEndpoint.url,
headers
);
}
/**
* @param {Event} event
* @param {number} id
*/
editRecord(event, id) {
event.preventDefault();
if (!this.props.data) {
return;
}
if (typeof this.props.data.onEditRecord === 'function') {
this.props.data.onEditRecord(event, id);
}
}
render() {
if (this.props.records === NotYetLoaded) {
// TODO Replace with better loading indicator
return <div>{ i18n._t('CampaignAdmin.LOADING', 'Loading...') }</div>;
}
if (!this.props.records.length) {
return <div>{ i18n._t('CampaignAdmin.NO_RECORDS', 'No campaigns created yet.') }</div>;
}
// Placeholder to align the headers correctly with the content
const actionPlaceholder = <th key="holder" className="grid-field__action-placeholder" />;
const headerCells = this.props.data.columns.map((column) =>
<GridFieldHeaderCell key={column.name}>{column.name}</GridFieldHeaderCell>
);
const header = <GridFieldHeader>{headerCells.concat(actionPlaceholder)}</GridFieldHeader>;
const rows = this.props.records.map((record) =>
this.createRow(record)
);
return (
<GridFieldTable header={header} rows={rows} />
);
}
}
GridField.propTypes = {
data: PropTypes.shape({
recordType: PropTypes.string.isRequired,
headerColumns: PropTypes.array,
collectionReadEndpoint: PropTypes.object,
onDrillDown: PropTypes.func,
onEditRecord: PropTypes.func,
}),
};
function mapStateToProps(state, ownProps) {
const recordType = ownProps.data && ownProps.data.recordType;
return {
config: state.config,
records: (recordType && state.records[recordType]) ? state.records[recordType] : NotYetLoaded,
};
}
function mapDispatchToProps(dispatch) {
return {
actions: bindActionCreators(actions, dispatch),
};
}
export default connect(mapStateToProps, mapDispatchToProps)(GridField);