@mwater/visualization 5.4.5 → 5.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (132) hide show
  1. package/.storybook/head.html +0 -1
  2. package/lib/MWaterContextComponent.js +1 -1
  3. package/lib/MWaterLoaderComponent.d.ts +2 -2
  4. package/lib/dashboards/DashboardComponent.js +2 -1
  5. package/lib/dashboards/LayoutOptionsComponent.js +18 -11
  6. package/lib/dashboards/ServerDashboardDataSource.d.ts +10 -1
  7. package/lib/dashboards/ServerDashboardDataSource.js +29 -0
  8. package/lib/dashboards/layoutOptions.d.ts +5 -1
  9. package/lib/datagrids/DatagridComponent.js +1 -1
  10. package/lib/datagrids/ExprCellComponent.d.ts +1 -0
  11. package/lib/datagrids/ExprCellComponent.js +22 -20
  12. package/lib/maps/BufferLayer.d.ts +18 -0
  13. package/lib/maps/BufferLayer.js +24 -14
  14. package/lib/maps/ChoroplethLayer.d.ts +18 -0
  15. package/lib/maps/ChoroplethLayer.js +34 -25
  16. package/lib/maps/ChoroplethLayerDesign.d.ts +3 -2
  17. package/lib/maps/ChoroplethLayerDesigner.d.ts +11 -1
  18. package/lib/maps/DirectMapDataSource.js +17 -0
  19. package/lib/maps/EditHoverOver.d.ts +1 -1
  20. package/lib/maps/EditHoverOver.js +62 -33
  21. package/lib/maps/HoverContent.d.ts +10 -5
  22. package/lib/maps/HoverContent.js +6 -35
  23. package/lib/maps/Layer.d.ts +37 -0
  24. package/lib/maps/Layer.js +30 -4
  25. package/lib/maps/MWaterServerLayer.d.ts +2 -2
  26. package/lib/maps/MWaterServerLayer.js +6 -6
  27. package/lib/maps/MapLayerDataSource.d.ts +9 -0
  28. package/lib/maps/MapUtils.d.ts +19 -1
  29. package/lib/maps/MapUtils.js +71 -1
  30. package/lib/maps/MarkersLayer.d.ts +18 -0
  31. package/lib/maps/MarkersLayer.js +24 -24
  32. package/lib/maps/MarkersLayerDesignerComponent.d.ts +14 -1
  33. package/lib/maps/RasterMapViewComponent.js +1 -1
  34. package/lib/maps/ServerMapDataSource.d.ts +9 -0
  35. package/lib/maps/ServerMapDataSource.js +29 -0
  36. package/lib/maps/VectorMapViewComponent.js +6 -6
  37. package/lib/maps/maps.d.ts +4 -2
  38. package/lib/mwater_table_selection/FormsListComponent.d.ts +33 -0
  39. package/lib/mwater_table_selection/FormsListComponent.js +141 -0
  40. package/lib/mwater_table_selection/IndicatorsListComponent.d.ts +47 -0
  41. package/lib/mwater_table_selection/IndicatorsListComponent.js +182 -0
  42. package/lib/mwater_table_selection/IssuesListComponent.d.ts +29 -0
  43. package/lib/mwater_table_selection/IssuesListComponent.js +123 -0
  44. package/lib/mwater_table_selection/MWaterAccountingSystemListComponent.d.ts +20 -0
  45. package/lib/mwater_table_selection/MWaterAccountingSystemListComponent.js +157 -0
  46. package/lib/mwater_table_selection/MWaterAssetSystemsListComponent.d.ts +17 -0
  47. package/lib/mwater_table_selection/MWaterAssetSystemsListComponent.js +79 -0
  48. package/lib/mwater_table_selection/MWaterCompleteTableSelectComponent.d.ts +37 -0
  49. package/lib/mwater_table_selection/MWaterCompleteTableSelectComponent.js +275 -0
  50. package/lib/mwater_table_selection/MWaterCustomTablesetListComponent.d.ts +17 -0
  51. package/lib/mwater_table_selection/MWaterCustomTablesetListComponent.js +94 -0
  52. package/lib/mwater_table_selection/MWaterMetricsTableListComponent.d.ts +17 -0
  53. package/lib/mwater_table_selection/MWaterMetricsTableListComponent.js +80 -0
  54. package/lib/mwater_table_selection/MWaterTableSelectComponent.d.ts +32 -0
  55. package/lib/mwater_table_selection/MWaterTableSelectComponent.js +158 -0
  56. package/lib/widgets/charts/Chart.d.ts +11 -0
  57. package/lib/widgets/charts/Chart.js +15 -0
  58. package/lib/widgets/charts/ChartWidgetComponent.d.ts +1 -0
  59. package/lib/widgets/charts/ChartWidgetComponent.js +27 -1
  60. package/lib/widgets/charts/layered/LayeredChartDesign.d.ts +1 -1
  61. package/lib/widgets/charts/layered/LayeredChartDesignerComponent.d.ts +1 -1
  62. package/lib/widgets/charts/layered/LayeredChartDesignerComponent.js +5 -12
  63. package/lib/widgets/charts/layered/LayeredChartLayerDesignerComponent.d.ts +43 -57
  64. package/lib/widgets/charts/layered/LayeredChartLayerDesignerComponent.js +113 -110
  65. package/lib/widgets/charts/layered/LayeredChartUtils.d.ts +2 -1
  66. package/lib/widgets/charts/layered/LayeredChartUtils.js +0 -2
  67. package/lib/widgets/charts/pivot/PivotChart.d.ts +2 -0
  68. package/lib/widgets/charts/pivot/PivotChart.js +156 -0
  69. package/lib/widgets/charts/pivot/PivotChartDesignerComponent.d.ts +5 -20
  70. package/lib/widgets/charts/pivot/PivotChartDesignerComponent.js +31 -61
  71. package/lib/widgets/charts/pivot/PivotChartLayoutBuilder.d.ts +4 -0
  72. package/lib/widgets/charts/pivot/PivotChartLayoutBuilder.js +4 -2
  73. package/lib/widgets/charts/pivot/PivotChartLayoutComponent.d.ts +5 -44
  74. package/lib/widgets/charts/pivot/PivotChartLayoutComponent.js +38 -63
  75. package/lib/widgets/charts/pivot/SegmentDesignerComponent.d.ts +7 -68
  76. package/lib/widgets/charts/pivot/SegmentDesignerComponent.js +58 -106
  77. package/lib/widgets/charts/table/TableChart.d.ts +2 -0
  78. package/lib/widgets/charts/table/TableChart.js +172 -1
  79. package/lib/widgets/charts/table/TableChartDesignerComponent.d.ts +7 -17
  80. package/lib/widgets/charts/table/TableChartDesignerComponent.js +79 -95
  81. package/lib/widgets/charts/table/TableChartViewComponent.d.ts +1 -7
  82. package/lib/widgets/charts/table/TableChartViewComponent.js +19 -27
  83. package/package.json +3 -8
  84. package/src/MWaterContextComponent.tsx +1 -1
  85. package/src/MWaterLoaderComponent.ts +1 -1
  86. package/src/dashboards/DashboardComponent.tsx +2 -1
  87. package/src/dashboards/LayoutOptionsComponent.tsx +22 -10
  88. package/src/dashboards/ServerDashboardDataSource.ts +36 -1
  89. package/src/dashboards/layoutOptions.tsx +5 -1
  90. package/src/datagrids/DatagridComponent.tsx +1 -1
  91. package/src/datagrids/ExprCellComponent.tsx +23 -20
  92. package/src/maps/BufferLayer.ts +35 -20
  93. package/src/maps/ChoroplethLayer.ts +51 -33
  94. package/src/maps/ChoroplethLayerDesign.ts +3 -2
  95. package/src/maps/ChoroplethLayerDesigner.tsx +2 -2
  96. package/src/maps/DirectMapDataSource.ts +21 -1
  97. package/src/maps/EditHoverOver.tsx +91 -51
  98. package/src/maps/HoverContent.tsx +16 -47
  99. package/src/maps/Layer.ts +42 -4
  100. package/src/maps/MWaterServerLayer.ts +6 -6
  101. package/src/maps/MapLayerDataSource.ts +8 -0
  102. package/src/maps/MapUtils.ts +70 -3
  103. package/src/maps/MarkersLayer.ts +34 -24
  104. package/src/maps/RasterMapViewComponent.ts +1 -1
  105. package/src/maps/ServerMapDataSource.ts +35 -0
  106. package/src/maps/VectorMapViewComponent.tsx +6 -6
  107. package/src/maps/maps.ts +4 -2
  108. package/src/mwater_table_selection/FormsListComponent.tsx +188 -0
  109. package/src/mwater_table_selection/IndicatorsListComponent.tsx +283 -0
  110. package/src/mwater_table_selection/IssuesListComponent.tsx +167 -0
  111. package/src/mwater_table_selection/MWaterAccountingSystemListComponent.tsx +225 -0
  112. package/src/{MWaterAssetSystemsListComponent.tsx → mwater_table_selection/MWaterAssetSystemsListComponent.tsx} +2 -2
  113. package/src/mwater_table_selection/MWaterCompleteTableSelectComponent.tsx +377 -0
  114. package/src/{MWaterCustomTablesetListComponent.tsx → mwater_table_selection/MWaterCustomTablesetListComponent.tsx} +1 -1
  115. package/src/{MWaterMetricsTableListComponent.tsx → mwater_table_selection/MWaterMetricsTableListComponent.tsx} +1 -1
  116. package/src/{MWaterTableSelectComponent.tsx → mwater_table_selection/MWaterTableSelectComponent.tsx} +83 -86
  117. package/src/widgets/charts/Chart.ts +17 -0
  118. package/src/widgets/charts/ChartWidgetComponent.tsx +36 -1
  119. package/src/widgets/charts/layered/LayeredChartDesign.ts +1 -1
  120. package/src/widgets/charts/layered/LayeredChartDesignerComponent.tsx +23 -24
  121. package/src/widgets/charts/layered/LayeredChartLayerDesignerComponent.tsx +260 -211
  122. package/src/widgets/charts/layered/LayeredChartUtils.ts +7 -7
  123. package/src/widgets/charts/pivot/PivotChart.ts +191 -0
  124. package/src/widgets/charts/pivot/PivotChartDesignerComponent.tsx +124 -129
  125. package/src/widgets/charts/pivot/PivotChartLayoutBuilder.ts +4 -2
  126. package/src/widgets/charts/pivot/PivotChartLayoutComponent.tsx +120 -149
  127. package/src/widgets/charts/pivot/SegmentDesignerComponent.tsx +178 -198
  128. package/src/widgets/charts/table/TableChart.ts +177 -1
  129. package/src/widgets/charts/table/TableChartDesignerComponent.tsx +422 -0
  130. package/src/widgets/charts/table/{TableChartViewComponent.ts → TableChartViewComponent.tsx} +65 -60
  131. package/src/MWaterCompleteTableSelectComponent.tsx +0 -975
  132. package/src/widgets/charts/table/TableChartDesignerComponent.ts +0 -441
@@ -1,9 +1,8 @@
1
1
  import _ from "lodash"
2
2
  import React from "react"
3
- const R = React.createElement
4
- import { ToggleEditComponent, OptionListComponent } from "./UIComponents"
3
+ import { ToggleEditComponent, OptionListComponent } from "../UIComponents"
5
4
  import { ExprUtils, Schema } from "@mwater/expressions"
6
- import MWaterResponsesFilterComponent from "./MWaterResponsesFilterComponent"
5
+ import MWaterResponsesFilterComponent from "../MWaterResponsesFilterComponent"
7
6
  import ModalPopupComponent from "@mwater/react-library/lib/ModalPopupComponent"
8
7
  import MWaterCompleteTableSelectComponent from "./MWaterCompleteTableSelectComponent"
9
8
  import { ActiveTablesContext } from "@mwater/expressions-ui"
@@ -18,16 +17,16 @@ export interface MWaterTableSelectComponentProps {
18
17
  user?: string
19
18
  table?: string
20
19
  /** Called with table selected */
21
- onChange: any
22
- extraTables: any
23
- onExtraTablesChange: any
20
+ onChange: (table: string | null) => void
21
+ extraTables: string[]
22
+ onExtraTablesChange: (tables: string[]) => void
24
23
  /** Can also perform filtering for some types. Include these props to enable this */
25
24
  filter?: any
26
- onFilterChange?: any
25
+ onFilterChange?: (filter: any) => void
27
26
  }
28
27
 
29
28
  interface MWaterTableSelectComponentState {
30
- pendingExtraTable: any
29
+ pendingExtraTable: string | null
31
30
  }
32
31
 
33
32
  // Allows selection of a mwater-visualization table. Loads forms as well and calls event if modified
@@ -35,7 +34,7 @@ export default class MWaterTableSelectComponent extends React.Component<
35
34
  MWaterTableSelectComponentProps,
36
35
  MWaterTableSelectComponentState
37
36
  > {
38
- toggleEdit: any
37
+ toggleEdit: ToggleEditComponent | null = null
39
38
 
40
39
  constructor(props: MWaterTableSelectComponentProps) {
41
40
  super(props)
@@ -45,46 +44,46 @@ export default class MWaterTableSelectComponent extends React.Component<
45
44
  }
46
45
  }
47
46
 
48
- componentWillReceiveProps(nextProps: any) {
47
+ componentDidUpdate(prevProps: MWaterTableSelectComponentProps) {
49
48
  // If received new schema with pending extra table, select it
50
- let table
49
+ let table: string | null = null
51
50
  if (this.state.pendingExtraTable) {
52
51
  table = this.state.pendingExtraTable
53
- if (nextProps.schema.getTable(table)) {
52
+ if (this.props.schema.getTable(table)) {
54
53
  // No longer waiting
55
54
  this.setState({ pendingExtraTable: null })
56
55
 
57
56
  // Close toggle edit
58
- this.toggleEdit.close()
57
+ this.toggleEdit?.close()
59
58
 
60
59
  // Fire change
61
- nextProps.onChange(table)
60
+ this.props.onChange(table)
62
61
  }
63
62
  }
64
63
 
65
64
  // If table is newly selected and is a responses table and no filters, set filters to final only
66
65
  if (
67
- nextProps.table &&
68
- nextProps.table.match(/responses:/) &&
69
- nextProps.table !== this.props.table &&
70
- !nextProps.filter &&
71
- nextProps.onFilterChange
66
+ this.props.table &&
67
+ this.props.table.match(/responses:/) &&
68
+ this.props.table !== prevProps.table &&
69
+ !this.props.filter &&
70
+ this.props.onFilterChange
72
71
  ) {
73
- return nextProps.onFilterChange({
72
+ this.props.onFilterChange({
74
73
  type: "op",
75
74
  op: "= any",
76
- table: nextProps.table,
75
+ table: this.props.table,
77
76
  exprs: [
78
- { type: "field", table: nextProps.table, column: "status" },
77
+ { type: "field", table: this.props.table, column: "status" },
79
78
  { type: "literal", valueType: "enumset", value: ["final"] }
80
79
  ]
81
80
  })
82
81
  }
83
82
  }
84
83
 
85
- handleChange = (tableId: any) => {
84
+ handleChange = (tableId: string | null) => {
86
85
  // Close toggle edit
87
- this.toggleEdit.close()
86
+ this.toggleEdit?.close()
88
87
 
89
88
  // Call onChange if different
90
89
  if (tableId !== this.props.table) {
@@ -92,7 +91,7 @@ export default class MWaterTableSelectComponent extends React.Component<
92
91
  }
93
92
  }
94
93
 
95
- handleTableChange = (tableId: any) => {
94
+ handleTableChange = (tableId: string | null) => {
96
95
  // If not part of extra tables, add it and wait for new schema
97
96
  if (tableId && !this.props.schema.getTable(tableId)) {
98
97
  return this.setState({ pendingExtraTable: tableId }, () => {
@@ -104,56 +103,54 @@ export default class MWaterTableSelectComponent extends React.Component<
104
103
  }
105
104
 
106
105
  render() {
107
- const editor = R(EditModeTableSelectComponent, {
108
- apiUrl: this.props.apiUrl,
109
- client: this.props.client,
110
- schema: this.props.schema,
111
- user: this.props.user,
112
- table: this.props.table,
113
- onChange: this.handleTableChange,
114
- extraTables: this.props.extraTables,
115
- onExtraTablesChange: this.props.onExtraTablesChange
116
- })
106
+ const editor = <EditModeTableSelectComponent
107
+ apiUrl={this.props.apiUrl}
108
+ client={this.props.client}
109
+ schema={this.props.schema}
110
+ user={this.props.user}
111
+ table={this.props.table}
112
+ onChange={this.handleTableChange}
113
+ extraTables={this.props.extraTables}
114
+ onExtraTablesChange={this.props.onExtraTablesChange}
115
+ />
117
116
 
118
- return R(
119
- "div",
120
- null,
121
- // Show message if loading
122
- this.state.pendingExtraTable
123
- ? R(
124
- "div",
125
- { className: "alert alert-info", key: "pendingExtraTable" },
126
- R("i", { className: "fa fa-spinner fa-spin" }),
127
- `\u00a0${T`Please wait...`}`
128
- )
129
- : undefined,
117
+ return (
118
+ <div>
119
+ {/* Show message if loading */}
120
+ {this.state.pendingExtraTable ? (
121
+ <div className="alert alert-info" key="pendingExtraTable">
122
+ <i className="fa fa-spinner fa-spin"/>
123
+ &nbsp;{T`Please wait...`}
124
+ </div>
125
+ ) : undefined}
130
126
 
131
- R(ToggleEditComponent, {
132
- ref: (c: any) => {
133
- this.toggleEdit = c
134
- },
135
- forceOpen: !this.props.table, // Must have table
136
- label: this.props.table
137
- ? ExprUtils.localizeString(this.props.schema.getTable(this.props.table)?.name, T.locale)
138
- : "",
139
- editor,
140
- onRemove: () => {
141
- this.props.onChange(null)
142
- }
143
- }),
127
+ <ToggleEditComponent
128
+ ref={(c: ToggleEditComponent | null) => {
129
+ this.toggleEdit = c
130
+ }}
131
+ forceOpen={!this.props.table} // Must have table
132
+ label={this.props.table
133
+ ? ExprUtils.localizeString(this.props.schema.getTable(this.props.table)?.name, T.locale)
134
+ : ""}
135
+ editor={editor}
136
+ onRemove={() => {
137
+ this.props.onChange(null)
138
+ }}
139
+ />
144
140
 
145
- // Make sure table still exists
146
- this.props.table &&
147
- this.props.onFilterChange &&
148
- this.props.table.match(/^responses:/) &&
149
- this.props.schema.getTable(this.props.table)
150
- ? R(MWaterResponsesFilterComponent, {
151
- schema: this.props.schema,
152
- table: this.props.table,
153
- filter: this.props.filter,
154
- onFilterChange: this.props.onFilterChange
155
- })
156
- : undefined
141
+ {/* Make sure table still exists */}
142
+ {this.props.table &&
143
+ this.props.onFilterChange &&
144
+ this.props.table.match(/^responses:/) &&
145
+ this.props.schema.getTable(this.props.table) ? (
146
+ <MWaterResponsesFilterComponent
147
+ schema={this.props.schema}
148
+ table={this.props.table}
149
+ filter={this.props.filter}
150
+ onFilterChange={this.props.onFilterChange}
151
+ />
152
+ ) : undefined}
153
+ </div>
157
154
  )
158
155
  }
159
156
  }
@@ -168,13 +165,13 @@ interface EditModeTableSelectComponentProps {
168
165
  user?: string
169
166
  table?: string
170
167
  /** Called with table selected */
171
- onChange: any
172
- extraTables: any
173
- onExtraTablesChange: any
168
+ onChange: (table: string | null) => void
169
+ extraTables: string[]
170
+ onExtraTablesChange: (tables: string[]) => void
174
171
  }
175
172
 
176
173
  interface EditModeTableSelectComponentState {
177
- completeMode: any
174
+ completeMode: boolean
178
175
  }
179
176
 
180
177
  // Is the table select component when in edit mode. Toggles between complete list and simplified list
@@ -182,7 +179,7 @@ class EditModeTableSelectComponent extends React.Component<
182
179
  EditModeTableSelectComponentProps,
183
180
  EditModeTableSelectComponentState
184
181
  > {
185
- constructor(props: any) {
182
+ constructor(props: EditModeTableSelectComponentProps) {
186
183
  super(props)
187
184
 
188
185
  this.state = {
@@ -203,7 +200,7 @@ class EditModeTableSelectComponent extends React.Component<
203
200
 
204
201
  // Remove dead tables
205
202
  tables = tables.filter(
206
- (t: any) => this.props.schema.getTable(t) != null && !this.props.schema.getTable(t)!.deprecated
203
+ (t: string) => this.props.schema.getTable(t) != null && !this.props.schema.getTable(t)?.deprecated
207
204
  )
208
205
  tables = _.union(
209
206
  tables,
@@ -223,7 +220,7 @@ class EditModeTableSelectComponent extends React.Component<
223
220
  }
224
221
  } else {
225
222
  // Add if exists
226
- if (this.props.schema.getTable(extraTable) && !this.props.schema.getTable(extraTable)!.deprecated) {
223
+ if (this.props.schema.getTable(extraTable) && !this.props.schema.getTable(extraTable)?.deprecated) {
227
224
  tables = _.union(tables, [extraTable])
228
225
  }
229
226
  }
@@ -231,13 +228,13 @@ class EditModeTableSelectComponent extends React.Component<
231
228
 
232
229
  // Sort by name
233
230
  tables = _.sortBy(tables, (tableId) =>
234
- ExprUtils.localizeString(this.props.schema.getTable(tableId)!.name, T.locale)
231
+ ExprUtils.localizeString(this.props.schema.getTable(tableId)?.name, T.locale)
235
232
  )
236
233
 
237
234
  return tables
238
235
  }
239
236
 
240
- handleCompleteChange = (tableId: any) => {
237
+ handleCompleteChange = (tableId: string | null) => {
241
238
  this.setState({ completeMode: false })
242
239
  return this.props.onChange(tableId)
243
240
  }
@@ -245,7 +242,7 @@ class EditModeTableSelectComponent extends React.Component<
245
242
  render() {
246
243
  return (
247
244
  <ActiveTablesContext.Consumer>
248
- {activeTables => (
245
+ {(activeTables: string[]) => (
249
246
  <div>
250
247
  {this.state.completeMode ? (
251
248
  <ModalPopupComponent
@@ -271,11 +268,11 @@ class EditModeTableSelectComponent extends React.Component<
271
268
  <>
272
269
  <div className="text-muted">{T`Select Data Source:`}</div>
273
270
  <OptionListComponent items={this.getTableShortlist(activeTables).map((tableId) => {
274
- const table = this.props.schema.getTable(tableId)!
271
+ const table = this.props.schema.getTable(tableId)
275
272
  return {
276
- name: ExprUtils.localizeString(table.name, T.locale),
277
- desc: ExprUtils.localizeString(table.desc, T.locale),
278
- onClick: () => this.props.onChange(table.id)
273
+ name: ExprUtils.localizeString(table?.name, T.locale),
274
+ desc: ExprUtils.localizeString(table?.desc, T.locale),
275
+ onClick: () => table && this.props.onChange(table.id)
279
276
  }
280
277
  })} />
281
278
  <div>
@@ -156,4 +156,21 @@ export default class Chart {
156
156
  translateDesign(design: any, translate: (input: string) => string): any {
157
157
  return design
158
158
  }
159
+
160
+ /** Determines if the chart supports exporting to XLSX format */
161
+ supportsXlsxExport(): boolean {
162
+ return false
163
+ }
164
+
165
+ /** Creates a SheetJS workbook for the chart data
166
+ * @param design Chart design
167
+ * @param schema Schema to use
168
+ * @param dataSource Data source
169
+ * @param data Data from chart
170
+ * @param locale Locale to use
171
+ * @returns base64 encoded XLSX file
172
+ */
173
+ createXlsxWorkbook(design: any, schema: Schema, dataSource: DataSource, data: any, locale: string): string {
174
+ throw new Error("Not implemented")
175
+ }
159
176
  }
@@ -104,6 +104,36 @@ export class ChartWidgetComponent extends React.PureComponent<ChartWidgetCompone
104
104
  })
105
105
  }
106
106
 
107
+ // Saves an xlsx file to disk
108
+ handleSaveXlsxFile = () => {
109
+ // Get the data
110
+ this.props.widgetDataSource.getData(this.props.design, this.props.filters, (err: any, data: any) => {
111
+ if (err) {
112
+ console.error(err)
113
+ alert(T`Failed to get data: ${err.message}`)
114
+ return
115
+ }
116
+
117
+ try {
118
+ // Create workbook
119
+ const blob = this.props.chart.createXlsxWorkbook(
120
+ this.props.design,
121
+ this.props.schema,
122
+ this.props.dataSource,
123
+ data,
124
+ this.context
125
+ )
126
+
127
+ // Require at use as causes server problems
128
+ const FileSaver = require("file-saver")
129
+ FileSaver.saveAs(blob, "Exported Data.xlsx")
130
+ } catch (ex) {
131
+ console.error(ex)
132
+ alert(T`Failed to export data: ${ex.message}`)
133
+ }
134
+ })
135
+ }
136
+
107
137
  handleStartEditing = () => {
108
138
  // Can't edit if already editing
109
139
  if (this.state.editDesign) {
@@ -259,7 +289,12 @@ export class ChartWidgetComponent extends React.PureComponent<ChartWidgetCompone
259
289
  this.props.filters
260
290
  )
261
291
  if (!designError) {
262
- dropdownItems.push({ label: T`Export Data`, onClick: this.handleSaveCsvFile })
292
+ dropdownItems.push({ label: T`Export Data (CSV)`, onClick: this.handleSaveCsvFile })
293
+
294
+ // Add XLSX export option if supported
295
+ if (this.props.chart.supportsXlsxExport()) {
296
+ dropdownItems.push({ label: T`Export Data (XLSX)`, onClick: this.handleSaveXlsxFile })
297
+ }
263
298
  }
264
299
  if (this.props.onDesignChange != null) {
265
300
  dropdownItems.unshift({
@@ -66,7 +66,7 @@ export interface LayeredChartDesign {
66
66
 
67
67
  export interface LayeredChartDesignLayer {
68
68
  /** bar/line/spline/scatter/area/pie/donut (overrides main one) */
69
- type: "bar" | "line" | "spline" | "scatter" | "area" | "pie" | "donut"
69
+ type?: "bar" | "line" | "spline" | "scatter" | "area" | "pie" | "donut"
70
70
 
71
71
  /** table of layer */
72
72
  table: string
@@ -197,11 +197,10 @@ export default class LayeredChartDesignerComponent extends React.Component<Layer
197
197
  </div>
198
198
  )
199
199
  }
200
-
201
200
  renderType() {
202
201
  const chartTypes = [
203
202
  { id: "bar", name: T`Bar`, desc: T`Best for most charts` },
204
- { id: "pie", name: T`Pie`, desc: T`Compare ratios of one variable` },
203
+ { id: "pie", name: T`Pie`, desc: T`Compare ratios of one variable` },
205
204
  { id: "donut", name: T`Donut`, desc: T`Pie chart with center removed` },
206
205
  { id: "line", name: T`Line`, desc: T`Show how data changes smoothly over time` },
207
206
  { id: "spline", name: T`Smoothed Line`, desc: T`For noisy data over time` },
@@ -209,29 +208,29 @@ export default class LayeredChartDesignerComponent extends React.Component<Layer
209
208
  { id: "area", name: T`Area`, desc: T`For cumulative data over time` }
210
209
  ]
211
210
 
212
- const current = _.findWhere(chartTypes, { id: this.props.design.type })
211
+ const current = chartTypes.find(ct => ct.id === this.props.design.type)
213
212
 
214
- return R(
215
- uiComponents.SectionComponent,
216
- { icon: "glyphicon-th", label: T`Chart Type` },
217
- R(uiComponents.ToggleEditComponent, {
218
- forceOpen: !this.props.design.type,
219
- label: current ? current.name : "",
220
- editor: (onClose: any) => {
221
- return R(uiComponents.OptionListComponent, {
222
- hint: T`Select a Chart Type`,
223
- items: _.map(chartTypes, ct => ({
224
- name: ct.name,
225
- desc: ct.desc,
226
- onClick: () => {
227
- onClose() // Close editor first
228
- return this.handleTypeChange(ct.id)
229
- }
230
- }))
231
- })
232
- }
233
- }),
234
- this.renderOptions()
213
+ return (
214
+ <uiComponents.SectionComponent label={T`Chart Type`}>
215
+ <uiComponents.ToggleEditComponent
216
+ forceOpen={!this.props.design.type}
217
+ label={current ? current.name : ""}
218
+ editor={(onClose: any) => (
219
+ <uiComponents.OptionListComponent
220
+ hint={T`Select a Chart Type`}
221
+ items={chartTypes.map(ct => ({
222
+ name: ct.name,
223
+ desc: ct.desc,
224
+ onClick: () => {
225
+ onClose() // Close editor first
226
+ return this.handleTypeChange(ct.id)
227
+ }
228
+ }))}
229
+ />
230
+ )}
231
+ />
232
+ {this.renderOptions()}
233
+ </uiComponents.SectionComponent>
235
234
  )
236
235
  }
237
236