@mwater/visualization 5.0.1 → 5.2.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 (172) hide show
  1. package/lib/GlobalFilter.d.ts +13 -0
  2. package/lib/GlobalFilter.js +2 -0
  3. package/lib/MWaterAddRelatedFormComponent.d.ts +1 -1
  4. package/lib/MWaterAddRelatedFormComponent.js +10 -17
  5. package/lib/MWaterCompleteTableSelectComponent.d.ts +2 -9
  6. package/lib/MWaterContextComponent.d.ts +31 -9
  7. package/lib/MWaterContextComponent.js +85 -76
  8. package/lib/MWaterCustomTablesetListComponent.js +9 -3
  9. package/lib/MWaterGlobalFiltersComponent.d.ts +6 -5
  10. package/lib/MWaterGlobalFiltersComponent.js +4 -4
  11. package/lib/MWaterLoaderComponent.d.ts +14 -4
  12. package/lib/MWaterLoaderComponent.js +10 -2
  13. package/lib/MWaterTableSelectComponent.d.ts +0 -1
  14. package/lib/MWaterTableSelectComponent.js +20 -41
  15. package/lib/axes/Axis.d.ts +20 -25
  16. package/lib/axes/AxisBuilder.js +9 -7
  17. package/lib/axes/AxisComponent.d.ts +4 -4
  18. package/lib/axes/RangesComponent.d.ts +12 -6
  19. package/lib/axes/RangesComponent.js +21 -10
  20. package/lib/dashboards/DashboardComponent.d.ts +1 -14
  21. package/lib/dashboards/DashboardComponent.js +18 -56
  22. package/lib/dashboards/DashboardDesign.d.ts +2 -17
  23. package/lib/dashboards/DashboardViewComponent.js +3 -4
  24. package/lib/dashboards/LayoutOptionsComponent.js +4 -3
  25. package/lib/dashboards/ServerDashboardDataSource.d.ts +1 -0
  26. package/lib/dashboards/ServerDashboardDataSource.js +3 -0
  27. package/lib/dashboards/SettingsModalComponent.d.ts +4 -15
  28. package/lib/dashboards/SettingsModalComponent.js +24 -38
  29. package/lib/datagrids/DatagridComponent.d.ts +10 -13
  30. package/lib/datagrids/DatagridComponent.js +27 -5
  31. package/lib/datagrids/DatagridDataSource.d.ts +3 -2
  32. package/lib/datagrids/DatagridDataSource.js +0 -11
  33. package/lib/datagrids/DatagridDesign.d.ts +2 -0
  34. package/lib/datagrids/DatagridDesignerComponent.d.ts +2 -93
  35. package/lib/datagrids/DatagridDesignerComponent.js +11 -8
  36. package/lib/datagrids/DatagridViewComponent.js +2 -2
  37. package/lib/datagrids/DirectDatagridDataSource.d.ts +1 -0
  38. package/lib/datagrids/DirectDatagridDataSource.js +26 -0
  39. package/lib/datagrids/FindReplaceModalComponent.d.ts +4 -20
  40. package/lib/datagrids/FindReplaceModalComponent.js +27 -13
  41. package/lib/datagrids/ServerDatagridDataSource.d.ts +2 -1
  42. package/lib/datagrids/ServerDatagridDataSource.js +16 -3
  43. package/lib/demo.js +1 -1
  44. package/lib/index.d.ts +1 -1
  45. package/lib/index.js +2 -4
  46. package/lib/layouts/blocks/BlocksDisplayComponent.d.ts +2 -1
  47. package/lib/layouts/blocks/BlocksDisplayComponent.js +2 -2
  48. package/lib/layouts/grid/GridLayoutManager.d.ts +2 -1
  49. package/lib/layouts/grid/LegoLayoutEngine.d.ts +1 -1
  50. package/lib/maps/BufferLayer.js +3 -1
  51. package/lib/maps/BufferLayerDesignerComponent.js +2 -2
  52. package/lib/maps/ChoroplethLayerDesigner.js +2 -2
  53. package/lib/maps/ClusterLayer.js +3 -1
  54. package/lib/maps/ClusterLayerDesignerComponent.js +2 -2
  55. package/lib/maps/DirectMapDataSource.js +1 -2
  56. package/lib/maps/GridLayer.js +5 -3
  57. package/lib/maps/GridLayerDesigner.js +2 -3
  58. package/lib/maps/LayerSwitcherComponent.js +1 -1
  59. package/lib/maps/MapComponent.d.ts +2 -7
  60. package/lib/maps/MapDesign.d.ts +2 -13
  61. package/lib/maps/MapDesignerComponent.d.ts +1 -12
  62. package/lib/maps/MapDesignerComponent.js +5 -12
  63. package/lib/maps/MapFiltersDesignerComponent.d.ts +0 -4
  64. package/lib/maps/MapFiltersDesignerComponent.js +4 -5
  65. package/lib/maps/MarkersLayerDesignerComponent.js +2 -2
  66. package/lib/maps/PopupFilterJoinsUtils.d.ts +6 -1
  67. package/lib/maps/PopupFilterJoinsUtils.js +4 -3
  68. package/lib/maps/RasterMapViewComponent.d.ts +2 -9
  69. package/lib/maps/RegionSelectComponent.d.ts +2 -1
  70. package/lib/maps/ServerMapDataSource.d.ts +1 -1
  71. package/lib/maps/UtfGridLayer.js +1 -1
  72. package/lib/maps/vectorMaps.d.ts +1 -0
  73. package/lib/maps/vectorMaps.js +10 -2
  74. package/lib/quickfilter/QuickfilterCompiler.d.ts +1 -1
  75. package/lib/widgets/IFrameWidgetComponent.d.ts +2 -9
  76. package/lib/widgets/ImageWidgetComponent.d.ts +6 -24
  77. package/lib/widgets/ImageWidgetComponent.js +2 -2
  78. package/lib/widgets/MapWidget.d.ts +2 -7
  79. package/lib/widgets/MarkdownWidget.d.ts +2 -7
  80. package/lib/widgets/TOCWidget.d.ts +2 -9
  81. package/lib/widgets/charts/ChartWidget.d.ts +3 -15
  82. package/lib/widgets/charts/calendar/CalendarChartDesignerComponent.js +2 -2
  83. package/lib/widgets/charts/imagemosaic/ImageMosaicChart.d.ts +1 -1
  84. package/lib/widgets/charts/imagemosaic/ImageMosaicChart.js +1 -1
  85. package/lib/widgets/charts/imagemosaic/ImageMosaicChartDesignerComponent.js +2 -2
  86. package/lib/widgets/charts/imagemosaic/ImagePopupComponent.d.ts +2 -7
  87. package/lib/widgets/charts/layered/LayeredChartDesignerComponent.d.ts +2 -31
  88. package/lib/widgets/charts/layered/LayeredChartLayerDesignerComponent.d.ts +2 -7
  89. package/lib/widgets/charts/layered/LayeredChartLayerDesignerComponent.js +2 -2
  90. package/lib/widgets/charts/pivot/IntersectionDesignerComponent.d.ts +73 -66
  91. package/lib/widgets/charts/pivot/PivotChartDesignerComponent.d.ts +10 -6
  92. package/lib/widgets/charts/pivot/PivotChartDesignerComponent.js +2 -2
  93. package/lib/widgets/charts/pivot/PivotChartViewComponent.d.ts +3 -22
  94. package/lib/widgets/charts/pivot/SegmentDesignerComponent.d.ts +55 -58
  95. package/lib/widgets/charts/table/TableChartDesignerComponent.js +2 -2
  96. package/lib/widgets/charts/table/TableChartViewComponent.js +21 -7
  97. package/lib/widgets/text/ExprInsertModalComponent.d.ts +2 -13
  98. package/lib/widgets/text/ExprItemEditorComponent.js +2 -2
  99. package/lib/widgets/text/ExprUpdateModalComponent.d.ts +2 -13
  100. package/lib/widgets/text/TextWidgetDesign.d.ts +3 -1
  101. package/package.json +1 -1
  102. package/src/GlobalFilter.ts +17 -0
  103. package/src/MWaterAddRelatedFormComponent.ts +15 -20
  104. package/src/MWaterContextComponent.tsx +158 -0
  105. package/src/MWaterCustomTablesetListComponent.tsx +21 -3
  106. package/src/MWaterGlobalFiltersComponent.ts +8 -8
  107. package/src/MWaterLoaderComponent.ts +10 -3
  108. package/src/{MWaterTableSelectComponent.ts → MWaterTableSelectComponent.tsx} +61 -66
  109. package/src/axes/Axis.ts +24 -25
  110. package/src/axes/AxisBuilder.ts +10 -9
  111. package/src/axes/RangesComponent.ts +27 -16
  112. package/src/dashboards/{DashboardComponent.ts → DashboardComponent.tsx} +39 -80
  113. package/src/dashboards/DashboardDesign.ts +2 -22
  114. package/src/dashboards/DashboardViewComponent.ts +4 -5
  115. package/src/dashboards/LayoutOptionsComponent.tsx +6 -4
  116. package/src/dashboards/ServerDashboardDataSource.ts +16 -12
  117. package/src/dashboards/SettingsModalComponent.tsx +170 -0
  118. package/src/datagrids/DatagridComponent.ts +45 -14
  119. package/src/datagrids/DatagridDataSource.ts +10 -8
  120. package/src/datagrids/DatagridDesign.ts +3 -0
  121. package/src/datagrids/DatagridDesignerComponent.tsx +31 -19
  122. package/src/datagrids/DatagridViewComponent.ts +4 -4
  123. package/src/datagrids/DirectDatagridDataSource.ts +35 -0
  124. package/src/datagrids/ExprCellComponent.ts +0 -1
  125. package/src/datagrids/FindReplaceModalComponent.ts +39 -22
  126. package/src/datagrids/ServerDatagridDataSource.ts +23 -6
  127. package/src/demo.ts +1 -1
  128. package/src/index.ts +1 -2
  129. package/src/layouts/blocks/BlocksDisplayComponent.ts +2 -2
  130. package/src/layouts/grid/LegoLayoutEngine.ts +2 -2
  131. package/src/layouts/grid/WidgetContainerComponent.ts +2 -2
  132. package/src/maps/BingLayer.ts +2 -2
  133. package/src/maps/BufferLayer.ts +3 -1
  134. package/src/maps/BufferLayerDesignerComponent.ts +1 -1
  135. package/src/maps/ChoroplethLayerDesigner.tsx +1 -1
  136. package/src/maps/ClusterLayer.ts +3 -1
  137. package/src/maps/ClusterLayerDesignerComponent.ts +1 -1
  138. package/src/maps/DirectMapDataSource.ts +1 -2
  139. package/src/maps/GridLayer.ts +5 -3
  140. package/src/maps/GridLayerDesigner.tsx +1 -2
  141. package/src/maps/LayerSwitcherComponent.tsx +1 -1
  142. package/src/maps/LegendGroup.ts +1 -1
  143. package/src/maps/MWaterServerLayer.ts +2 -2
  144. package/src/maps/MapDesign.ts +2 -17
  145. package/src/maps/{MapDesignerComponent.ts → MapDesignerComponent.tsx} +8 -16
  146. package/src/maps/{MapFiltersDesignerComponent.ts → MapFiltersDesignerComponent.tsx} +25 -25
  147. package/src/maps/MarkersLayerDesignerComponent.ts +1 -1
  148. package/src/maps/PopupFilterJoinsUtils.ts +4 -4
  149. package/src/maps/ServerMapDataSource.ts +7 -7
  150. package/src/maps/SwitchableTileUrlLayerDesigner.tsx +1 -13
  151. package/src/maps/UtfGridLayer.ts +4 -4
  152. package/src/maps/VectorMapViewComponent.tsx +0 -1
  153. package/src/maps/mapboxUtils.ts +2 -2
  154. package/src/maps/vectorMaps.tsx +10 -1
  155. package/src/quickfilter/QuickfilterCompiler.ts +1 -1
  156. package/src/richtext/ExprItemsHtmlConverter.ts +1 -1
  157. package/src/richtext/FontColorPaletteItem.ts +1 -1
  158. package/src/richtext/FontSizePaletteItem.ts +1 -1
  159. package/src/richtext/ItemsHtmlConverter.ts +2 -2
  160. package/src/widgets/ImageWidgetComponent.ts +1 -1
  161. package/src/widgets/charts/calendar/CalendarChartDesignerComponent.ts +1 -1
  162. package/src/widgets/charts/imagemosaic/ImageMosaicChart.ts +1 -1
  163. package/src/widgets/charts/imagemosaic/ImageMosaicChartDesignerComponent.ts +1 -1
  164. package/src/widgets/charts/layered/LayeredChartLayerDesignerComponent.tsx +1 -1
  165. package/src/widgets/charts/pivot/PivotChartDesignerComponent.tsx +1 -1
  166. package/src/widgets/charts/table/TableChartDesignerComponent.ts +1 -1
  167. package/src/widgets/charts/table/TableChartViewComponent.ts +21 -7
  168. package/src/widgets/text/ExprItemEditorComponent.tsx +1 -1
  169. package/src/widgets/text/TextWidgetDesign.ts +4 -1
  170. package/src/MWaterContextComponent.ts +0 -141
  171. package/src/TableSelectComponent.ts +0 -60
  172. package/src/dashboards/SettingsModalComponent.ts +0 -169
@@ -2,19 +2,19 @@ import _ from "lodash"
2
2
  import React from "react"
3
3
  const R = React.createElement
4
4
 
5
- import querystring from "querystring"
6
5
  import * as ui from "@mwater/react-library/lib/bootstrap"
7
6
  import { IdLiteralComponent } from "@mwater/expressions-ui"
8
7
  import { DataSource, Schema } from "@mwater/expressions"
8
+ import { GlobalFilter } from "./GlobalFilter"
9
9
 
10
10
  export interface MWaterGlobalFiltersComponentProps {
11
11
  /** Schema of the database */
12
12
  schema: Schema
13
13
  /** Data source to use to get values */
14
14
  dataSource: DataSource
15
- filterableTables: any
16
- globalFilters?: any
17
- onChange: any
15
+ filterableTables: string[]
16
+ globalFilters?: GlobalFilter[]
17
+ onChange: (globalFilters: GlobalFilter[]) => void
18
18
  }
19
19
 
20
20
  // Control to edit the global filters (_managed_by and admin_region)
@@ -36,7 +36,7 @@ export default class MWaterGlobalFiltersComponent extends React.Component<MWater
36
36
  })
37
37
  }
38
38
 
39
- return this.props.onChange(globalFilters)
39
+ this.props.onChange(globalFilters)
40
40
  }
41
41
 
42
42
  handleManagedByChange = (managedBy: any) => {
@@ -56,13 +56,13 @@ export default class MWaterGlobalFiltersComponent extends React.Component<MWater
56
56
  })
57
57
  }
58
58
 
59
- return this.props.onChange(globalFilters)
59
+ this.props.onChange(globalFilters)
60
60
  }
61
61
 
62
62
  render() {
63
63
  // Find managed by
64
64
  let adminRegions, managedBy
65
- const managedByEntry = _.find(this.props.globalFilters, (gf) => gf.op === "within" && gf.columnId === "_managed_by")
65
+ const managedByEntry = _.find(this.props.globalFilters || [], (gf) => gf.op === "within" && gf.columnId === "_managed_by")
66
66
  if (managedByEntry) {
67
67
  managedBy = managedByEntry.exprs[0].value.split(":")[1]
68
68
  } else {
@@ -71,7 +71,7 @@ export default class MWaterGlobalFiltersComponent extends React.Component<MWater
71
71
 
72
72
  // Find admin region
73
73
  const adminRegionEntry = _.find(
74
- this.props.globalFilters,
74
+ this.props.globalFilters || [],
75
75
  (gf) => gf.op === "within any" && gf.columnId === "admin_region"
76
76
  )
77
77
  if (adminRegionEntry) {
@@ -10,7 +10,7 @@ import MWaterContextComponent from "./MWaterContextComponent"
10
10
 
11
11
  /**
12
12
  * Loads an mWater schema from the server and creates child with schema and dataSource
13
- * Also creates a tableSelectElementFactory context to allow selecting of a table in an mWater-friendly way
13
+ * Also creates context to allow selecting of a table in an mWater-friendly way
14
14
  * and several other context items
15
15
  */
16
16
  export default class MWaterLoaderComponent extends AsyncLoadComponent<
@@ -32,7 +32,7 @@ export default class MWaterLoaderComponent extends AsyncLoadComponent<
32
32
  addLayerElementFactory?: any
33
33
  children: (error: any, config?: { schema: Schema; dataSource: DataSource }) => ReactElement<any>
34
34
  /** Custom error formatter that returns React node or string, gets passed the error response from server */
35
- errorFormatter: (data: any, defaultError: string) => string
35
+ errorFormatter?: (data: any, defaultError: string) => string
36
36
  },
37
37
  {
38
38
  error: any
@@ -79,7 +79,14 @@ export default class MWaterLoaderComponent extends AsyncLoadComponent<
79
79
  if (error) {
80
80
  const defaultError = `Cannot load one of the forms that this depends on. Perhaps the administrator has not shared the form with you? Details: ${error.message}`
81
81
  if (this.props.errorFormatter) {
82
- return callback({ error: this.props.errorFormatter(JSON.parse(error.message), defaultError) })
82
+ try {
83
+ const parsedError = JSON.parse(error.message)
84
+ if (parsedError) {
85
+ return callback({ error: this.props.errorFormatter(parsedError, defaultError) })
86
+ }
87
+ } catch (e) {
88
+ // Ignore
89
+ }
83
90
  }
84
91
  return callback({ error: defaultError })
85
92
  }
@@ -7,6 +7,7 @@ import { ExprUtils, Schema } from "@mwater/expressions"
7
7
  import MWaterResponsesFilterComponent from "./MWaterResponsesFilterComponent"
8
8
  import ModalPopupComponent from "@mwater/react-library/lib/ModalPopupComponent"
9
9
  import MWaterCompleteTableSelectComponent from "./MWaterCompleteTableSelectComponent"
10
+ import { ActiveTablesContext } from "@mwater/expressions-ui"
10
11
 
11
12
  export interface MWaterTableSelectComponentProps {
12
13
  /** Url to hit api */
@@ -37,10 +38,8 @@ export default class MWaterTableSelectComponent extends React.Component<
37
38
  > {
38
39
  static contextTypes = {
39
40
  locale: PropTypes.string, // e.g. "en"
40
-
41
- // Optional list of tables (ids) being used. Use this to present an initially short list to select from
42
- activeTables: PropTypes.arrayOf(PropTypes.string.isRequired)
43
41
  }
42
+
44
43
  toggleEdit: any
45
44
 
46
45
  constructor(props: any) {
@@ -187,9 +186,6 @@ class EditModeTableSelectComponent extends React.Component<
187
186
  > {
188
187
  static contextTypes = {
189
188
  locale: PropTypes.string, // e.g. "en"
190
-
191
- // Optional list of tables (ids) being used. Use this to present an initially short list to select from
192
- activeTables: PropTypes.arrayOf(PropTypes.string.isRequired)
193
189
  }
194
190
 
195
191
  constructor(props: any) {
@@ -208,8 +204,8 @@ class EditModeTableSelectComponent extends React.Component<
208
204
  // Get list of tables that should be included in shortlist
209
205
  // This is all active tables and all responses tables in schema (so as to include rosters) and all extra tables
210
206
  // Also includes current table
211
- getTableShortlist(): string[] {
212
- let tables: string[] = this.context.activeTables || []
207
+ getTableShortlist(activeTables: string[]): string[] {
208
+ let tables: string[] = activeTables
213
209
 
214
210
  // Remove dead tables
215
211
  tables = tables.filter(
@@ -253,64 +249,63 @@ class EditModeTableSelectComponent extends React.Component<
253
249
  }
254
250
 
255
251
  render() {
256
- const items = _.map(this.getTableShortlist(), (tableId) => {
257
- const table = this.props.schema.getTable(tableId)!
258
-
259
- return {
260
- name: ExprUtils.localizeString(table.name, this.context.locale),
261
- desc: ExprUtils.localizeString(table.desc, this.context.locale),
262
- onClick: this.props.onChange.bind(null, table.id)
263
- }
264
- })
265
-
266
- return R(
267
- "div",
268
- null,
269
- this.state.completeMode
270
- ? R(
271
- ModalPopupComponent,
272
- {
273
- header: "Select Data Source",
274
- onClose: () => this.setState({ completeMode: false }),
275
- showCloseX: true,
276
- size: "x-large"
277
- },
278
- R(MWaterCompleteTableSelectComponent, {
279
- apiUrl: this.props.apiUrl,
280
- client: this.props.client,
281
- schema: this.props.schema,
282
- user: this.props.user,
283
- table: this.props.table,
284
- onChange: this.handleCompleteChange,
285
- extraTables: this.props.extraTables,
286
- onExtraTablesChange: this.props.onExtraTablesChange
287
- })
288
- )
289
- : undefined,
290
-
291
- items.length > 0
292
- ? [
293
- R("div", { className: "text-muted" }, "Select Data Source:"),
294
-
295
- R(OptionListComponent, { items }),
296
-
297
- R(
298
- "div",
299
- null,
300
- items.length > 0
301
- ? R(
302
- "button",
303
- { type: "button", className: "btn btn-link btn-sm", onClick: this.handleShowMore },
304
- "Show All Available Data Sources..."
305
- )
306
- : undefined
307
- )
308
- ]
309
- : R(
310
- "button",
311
- { type: "button", className: "btn btn-link", onClick: this.handleShowMore },
312
- "Select Data Source..."
313
- )
252
+ return (
253
+ <ActiveTablesContext.Consumer>
254
+ {activeTables => (
255
+ <div>
256
+ {this.state.completeMode ? (
257
+ <ModalPopupComponent
258
+ header="Select Data Source"
259
+ onClose={() => this.setState({ completeMode: false })}
260
+ showCloseX={true}
261
+ size="x-large"
262
+ >
263
+ <MWaterCompleteTableSelectComponent
264
+ apiUrl={this.props.apiUrl}
265
+ client={this.props.client}
266
+ schema={this.props.schema}
267
+ user={this.props.user}
268
+ table={this.props.table}
269
+ onChange={this.handleCompleteChange}
270
+ extraTables={this.props.extraTables}
271
+ onExtraTablesChange={this.props.onExtraTablesChange}
272
+ />
273
+ </ModalPopupComponent>
274
+ ) : null}
275
+
276
+ {this.getTableShortlist(activeTables).length > 0 ? (
277
+ <>
278
+ <div className="text-muted">Select Data Source:</div>
279
+ <OptionListComponent items={this.getTableShortlist(activeTables).map((tableId) => {
280
+ const table = this.props.schema.getTable(tableId)!
281
+ return {
282
+ name: ExprUtils.localizeString(table.name, this.context.locale),
283
+ desc: ExprUtils.localizeString(table.desc, this.context.locale),
284
+ onClick: () => this.props.onChange(table.id)
285
+ }
286
+ })} />
287
+ <div>
288
+ <button
289
+ type="button"
290
+ className="btn btn-link btn-sm"
291
+ onClick={this.handleShowMore}
292
+ >
293
+ Show All Available Data Sources...
294
+ </button>
295
+ </div>
296
+ </>
297
+ ) : (
298
+ <button
299
+ type="button"
300
+ className="btn btn-link"
301
+ onClick={this.handleShowMore}
302
+ >
303
+ Select Data Source...
304
+ </button>
305
+ )}
306
+ </div>
307
+ )}
308
+ </ActiveTablesContext.Consumer>
314
309
  )
315
310
  }
316
311
  }
package/src/axes/Axis.ts CHANGED
@@ -5,37 +5,19 @@ import { Expr } from "@mwater/expressions"
5
5
  An axis is an expression with optional aggregation, transform and color mapping
6
6
  In ggplot2 parlance, an "aesthetic"
7
7
 
8
- It contains:
9
- expr: expression
10
- aggr: DEPRECATED: optional aggregation (e.g. sum)
11
- xform: optional transformation to be applied. object with `type` field. See below
12
- colorMap: optional array of color mappings. See below
13
- excludedValues: Array of post-xform values to exclude when displaying.
14
- format: optional d3-format format for numeric values. Default if null is ","
15
-
16
- ## Xforms
17
-
18
- types:
19
-
20
-
21
8
  */
22
9
  export interface Axis {
10
+ /** Expression to be displayed on axis */
23
11
  expr: Expr
24
- /**
25
- * `bin`: convert into bins. always has `numBins` integer and `min` and `max`. Can have `excludeUpper` and/or `excludeLower` to remove open bin on top or bottem. type enum
26
- * `date`: convert to complete date e.g. `2015-02-08`. type date
27
- * `year`: year only e.g. `2015-01-01`. type date
28
- * `yearmonth`: year and month only e.g. `2015-02-01`. type date
29
- * `yearquarter`: year and quarter only e.g. `2015-01`. type enum
30
- * `yearweek`: year and week (ISO) only e.g. `2015-31`. type enum
31
- * `month`: month only e.g. `02`. type enum
32
- * `week`: ISO week of year e.g. `01` for the first week that contains January 4th
33
- * `ranges`: convert to ranges. type enum. `ranges` is array of { id (unique id), label (optional label), minValue (null for none), maxValue (null for none), minOpen (true for >, false for >=), maxOpen (true for <, false for <=) }
34
- * `floor`: convert to floor. type enum
12
+
13
+ /** Optional transformation to be applied. If a date is used, for example, it's generally
14
+ * better to use a transform to convert to a year or month rather than creating an expression.
35
15
  */
36
16
  xform?: AxisXform
37
17
 
18
+ /** optional array of color mappings */
38
19
  colorMap?: ColorMap
20
+
39
21
  /** optional array of category values which define the order in which categories should be rendered */
40
22
  drawOrder?: any[]
41
23
 
@@ -45,12 +27,16 @@ export interface Axis {
45
27
  /** optional string for null category */
46
28
  nullLabel?: string
47
29
 
30
+ /** Array of post-xform values to exclude when displaying. */
48
31
  excludedValues?: any[]
32
+
33
+ /** optional d3-format format for numeric values. Default if null is "," */
49
34
  format?: string
50
35
 
51
- /** @deprecated */
36
+ /** @deprecated optional aggregation (e.g. sum) */
52
37
  aggr?: string
53
38
  }
39
+
54
40
  /**
55
41
  * Color map
56
42
  * Array of { value: post-transform value of expression, color: html color }
@@ -70,6 +56,19 @@ export interface AxisCategory {
70
56
  }
71
57
 
72
58
  export interface AxisXform {
59
+ /**
60
+ * Type of transformation
61
+ * `bin`: convert into bins. always has `numBins` integer and `min` and `max`. Can have `excludeUpper` and/or `excludeLower` to remove open bin on top or bottem. type enum
62
+ * `date`: convert to complete date e.g. `2015-02-08`. type date
63
+ * `year`: year only e.g. `2015-01-01`. type date
64
+ * `yearmonth`: year and month only e.g. `2015-02-01`. type date
65
+ * `yearquarter`: year and quarter only e.g. `2015-01`. type enum
66
+ * `yearweek`: year and week (ISO) only e.g. `2015-31`. type enum
67
+ * `month`: month only e.g. `02`. type enum
68
+ * `week`: ISO week of year e.g. `01` for the first week that contains January 4th
69
+ * `ranges`: convert to ranges. type enum. `ranges` is array of { id (unique id), label (optional label), minValue (null for none), maxValue (null for none), minOpen (true for >, false for >=), maxOpen (true for <, false for <=) }
70
+ * `floor`: convert to floor. type enum
71
+ */
73
72
  type: "bin" | "date" | "year" | "yearmonth" | "month" | "week" | "ranges" | "yearweek" | "yearquarter" | "floor"
74
73
  numBins?: number
75
74
  min?: number
@@ -12,6 +12,7 @@ import { default as produce } from "immer"
12
12
  import { formatValue } from "../valueFormatter"
13
13
  import { Axis, AxisCategory } from "./Axis"
14
14
  import { JsonQLExpr, JsonQLSelectQuery } from "@mwater/jsonql"
15
+ import dayjs from '../dayjs'
15
16
 
16
17
  const xforms: { type: string; input: LiteralType; output: LiteralType }[] = [
17
18
  { type: "bin", input: "number", output: "enum" },
@@ -253,7 +254,7 @@ export default class AxisBuilder {
253
254
  if (options.axis.xform.excludeUpper) {
254
255
  const thresholds = _.map(
255
256
  _.range(0, options.axis.xform.numBins),
256
- (bin) => min + ((max - min) * bin) / options.axis.xform!.numBins!
257
+ (bin) => min + ((max - min) * bin) / options.axis!.xform!.numBins!
257
258
  )
258
259
  thresholds.push(max + epsilon)
259
260
  compiledExpr = {
@@ -913,10 +914,9 @@ export default class AxisBuilder {
913
914
  if (values.length === 0) {
914
915
  return [noneCategory]
915
916
  }
916
-
917
917
  if (options.onlyValuesPresent) {
918
918
  // Sort and take only present
919
- categories = _.sortBy(_.uniq(values), item => item).map(value => ({ value, label: moment(value, "YYYY-MM-DD").format("ll") }))
919
+ categories = _.sortBy(_.uniq(values), item => item).map(value => ({ value, label: formatValue("date", value, axis.format)}))
920
920
 
921
921
  if (hasNone) {
922
922
  categories.push(noneCategory)
@@ -930,12 +930,12 @@ export default class AxisBuilder {
930
930
  max = values.sort().slice(-1)[0]
931
931
 
932
932
  // Use moment to get range
933
- current = moment(min, "YYYY-MM-DD")
934
- end = moment(max, "YYYY-MM-DD")
933
+ current = dayjs(min)
934
+ end = dayjs(max)
935
935
  categories = []
936
936
  while (!current.isAfter(end)) {
937
- categories.push({ value: current.format("YYYY-MM-DD"), label: current.format("ll") })
938
- current.add(1, "days")
937
+ categories.push({ value: current.format("YYYY-MM-DD"), label: current.format(axis.format ?? "ll") })
938
+ current = current.add(1, "days")
939
939
  if (categories.length >= 1000) {
940
940
  break
941
941
  }
@@ -1055,9 +1055,10 @@ export default class AxisBuilder {
1055
1055
  _.map(value as string[], (v, i) => R("div", { key: i }, v))
1056
1056
  )
1057
1057
  case "date":
1058
- return moment(value, moment.ISO_8601).format("ll")
1058
+ console.log(axis)
1059
+ return formatValue(type, value, axis.format)
1059
1060
  case "datetime":
1060
- return moment(value, moment.ISO_8601).format("lll")
1061
+ return formatValue(type, value, axis.format)
1061
1062
  }
1062
1063
 
1063
1064
  return "" + value
@@ -10,6 +10,7 @@ import NumberInputComponent from "@mwater/react-library/lib/NumberInputComponent
10
10
  import ReorderableListComponent from "@mwater/react-library/lib/reorderable/ReorderableListComponent"
11
11
  import { AxisXform, AxisXformRange } from "./Axis"
12
12
  import { Expr, Schema } from "@mwater/expressions"
13
+ import produce from "immer"
13
14
 
14
15
  export interface RangesComponentProps {
15
16
  schema: Schema
@@ -102,9 +103,9 @@ export default class RangesComponent extends React.Component<RangesComponentProp
102
103
 
103
104
  interface RangeComponentProps {
104
105
  /** Range to edit */
105
- range: any
106
- onChange: any
107
- onRemove: any
106
+ range: AxisXformRange
107
+ onChange: (range: AxisXformRange) => void
108
+ onRemove: () => void
108
109
  /** reorderable connector */
109
110
  connectDragSource: any
110
111
  /** reorderable connector */
@@ -114,14 +115,24 @@ interface RangeComponentProps {
114
115
 
115
116
  // Single range (row)
116
117
  class RangeComponent extends React.Component<RangeComponentProps> {
117
- handleMinOpenChange = (minOpen: any) => {
118
- return this.props.onChange(update(this.props.range, { minOpen: { $set: minOpen } }))
118
+ /**
119
+ * Handles change in minimum open value
120
+ */
121
+ handleMinOpenChange = (minOpen: boolean) => {
122
+ return this.props.onChange(produce(this.props.range, draft => {
123
+ draft.minOpen = minOpen
124
+ }))
119
125
  }
120
126
 
121
- handleMaxOpenChange = (maxOpen: any) => {
122
- return this.props.onChange(update(this.props.range, { maxOpen: { $set: maxOpen } }))
127
+ /**
128
+ * Handles change in maximum open value
129
+ */
130
+ handleMaxOpenChange = (maxOpen: boolean) => {
131
+ return this.props.onChange(produce(this.props.range, draft => {
132
+ draft.maxOpen = maxOpen
133
+ }))
123
134
  }
124
-
135
+
125
136
  render() {
126
137
  let placeholder = ""
127
138
  if (this.props.range.minValue != null) {
@@ -156,10 +167,10 @@ class RangeComponent extends React.Component<RangeComponentProps> {
156
167
  LinkComponent,
157
168
  {
158
169
  dropdownItems: [
159
- { id: true, name: "greater than" },
160
- { id: false, name: "greater than or equal to" }
170
+ { id: "true", name: "greater than" },
171
+ { id: "false", name: "greater than or equal to" }
161
172
  ],
162
- onDropdownItemClicked: this.handleMinOpenChange
173
+ onDropdownItemClicked: (id: string) => this.handleMinOpenChange(id === "true")
163
174
  },
164
175
  this.props.range.minOpen ? "greater than" : "greater than or equal to"
165
176
  )
@@ -169,7 +180,7 @@ class RangeComponent extends React.Component<RangeComponentProps> {
169
180
  "td",
170
181
  { key: "minValue" },
171
182
  R(NumberInputComponent, {
172
- value: this.props.range.minValue,
183
+ value: this.props.range.minValue ?? undefined,
173
184
  placeholder: "None",
174
185
  small: true,
175
186
  onChange: (v: any) => this.props.onChange(update(this.props.range, { minValue: { $set: v } }))
@@ -185,10 +196,10 @@ class RangeComponent extends React.Component<RangeComponentProps> {
185
196
  LinkComponent,
186
197
  {
187
198
  dropdownItems: [
188
- { id: true, name: "less than" },
189
- { id: false, name: "less than or equal to" }
199
+ { id: "true", name: "less than" },
200
+ { id: "false", name: "less than or equal to" }
190
201
  ],
191
- onDropdownItemClicked: this.handleMaxOpenChange
202
+ onDropdownItemClicked: (id: string) => this.handleMaxOpenChange(id === "true")
192
203
  },
193
204
  this.props.range.maxOpen ? "less than" : "less than or equal to"
194
205
  )
@@ -198,7 +209,7 @@ class RangeComponent extends React.Component<RangeComponentProps> {
198
209
  "td",
199
210
  { key: "maxValue" },
200
211
  R(NumberInputComponent, {
201
- value: this.props.range.maxValue,
212
+ value: this.props.range.maxValue ?? undefined,
202
213
  placeholder: "None",
203
214
  small: true,
204
215
  onChange: (v: any) => this.props.onChange(update(this.props.range, { maxValue: { $set: v } }))
@@ -19,6 +19,7 @@ import { getLayoutOptions } from "./layoutOptions"
19
19
  import { DashboardDesign } from "./DashboardDesign"
20
20
  import DashboardDataSource from "./DashboardDataSource"
21
21
  import { JsonQLFilter } from ".."
22
+ import { ActiveTablesContext } from "@mwater/expressions-ui"
22
23
 
23
24
  export interface DashboardComponentProps {
24
25
  design: DashboardDesign
@@ -80,7 +81,6 @@ export default class DashboardComponent extends React.Component<DashboardCompone
80
81
 
81
82
  static childContextTypes = {
82
83
  locale: PropTypes.string,
83
- activeTables: PropTypes.arrayOf(PropTypes.string.isRequired)
84
84
  }
85
85
 
86
86
  settings: SettingsModalComponent | null
@@ -89,9 +89,6 @@ export default class DashboardComponent extends React.Component<DashboardCompone
89
89
  return {
90
90
  // Pass locale down. Both here and DashboardViewComponent to ensure that quickfilters also get context
91
91
  locale: this.props.design.locale,
92
-
93
- // Pass active tables down to table select components so they can present a shorter list
94
- activeTables: DashboardUtils.getFilterableTables(this.props.design, this.props.schema)
95
92
  }
96
93
  }
97
94
 
@@ -240,45 +237,6 @@ export default class DashboardComponent extends React.Component<DashboardCompone
240
237
  )
241
238
  }
242
239
 
243
- renderStyleItem(style: any) {
244
- const isActive = (this.props.design.style || "default") === style
245
-
246
- const content = (() => {
247
- switch (style) {
248
- case "default":
249
- return [
250
- R("h4", { key: "name", className: "list-group-item-heading" }, "Classic Dashboard"),
251
- R("p", { key: "description", className: "" }, "Ideal for data display with minimal text")
252
- ]
253
- case "greybg":
254
- return [
255
- R("h4", { key: "name", className: "list-group-item-heading" }, "Framed Dashboard"),
256
- R("p", { key: "description", className: "" }, "Each widget is white on a grey background")
257
- ]
258
- case "story":
259
- return [
260
- R("h4", { key: "name", className: "list-group-item-heading" }, "Story"),
261
- R(
262
- "p",
263
- { key: "description", className: "" },
264
- "Ideal for data-driven storytelling with lots of text. Responsive and mobile-friendly"
265
- )
266
- ]
267
- }
268
- return null
269
- })()
270
-
271
- return R(
272
- "a",
273
- {
274
- key: style,
275
- className: `list-group-item ${isActive ? "active" : ""}`,
276
- onClick: this.handleStyleChange.bind(null, style)
277
- },
278
- content
279
- )
280
- }
281
-
282
240
  renderStyle() {
283
241
  return R(
284
242
  "button",
@@ -382,7 +340,8 @@ export default class DashboardComponent extends React.Component<DashboardCompone
382
340
  locks: this.props.quickfilterLocks,
383
341
  filters: this.getCompiledFilters(),
384
342
  hideTopBorder: this.props.hideTitleBar,
385
- onHide: () => this.setState({ hideQuickfilters: true })
343
+ // Don't hide if title bar is hidden as it can't be shown again
344
+ onHide: () => !this.props.hideTitleBar ? this.setState({ hideQuickfilters: true }) : undefined
386
345
  })
387
346
  }
388
347
 
@@ -428,41 +387,41 @@ export default class DashboardComponent extends React.Component<DashboardCompone
428
387
  hideScopes: this.state.hideQuickfilters
429
388
  })
430
389
 
431
- return R(
432
- "div",
433
- {
434
- style: {
435
- display: "grid",
436
- gridTemplateRows: this.props.hideTitleBar ? "auto 1fr" : "auto auto 1fr",
437
- height: "100%"
438
- }
439
- },
440
- !this.props.hideTitleBar ? this.renderTitleBar() : undefined,
441
- R("div", null, !this.state.hideQuickfilters ? this.renderQuickfilter() : undefined),
442
- dashboardView,
443
- this.props.onDesignChange != null
444
- ? R(SettingsModalComponent, {
445
- onDesignChange: this.handleDesignChange,
446
- schema: this.props.schema,
447
- dataSource: this.props.dataSource,
448
- ref: (c: SettingsModalComponent | null) => {
449
- this.settings = c
450
- }
451
- })
452
- : undefined,
453
- this.state.layoutOptionsOpen
454
- ? R(
455
- ModalWindowComponent,
456
- { isOpen: true, outerPadding: 10, innerPadding: 10 },
457
- R(LayoutOptionsComponent, {
458
- design: this.props.design,
459
- onDesignChange: this.props.onDesignChange!,
460
- onClose: () => this.setState({ layoutOptionsOpen: false }),
461
- dashboardView: readonlyDashboardView,
462
- quickfiltersView: this.renderQuickfilter()
463
- })
464
- )
465
- : undefined
466
- )
390
+
391
+ // Pass active tables down to table select components so they can present a shorter list
392
+ return <ActiveTablesContext.Provider
393
+ value={DashboardUtils.getFilterableTables(this.props.design, this.props.schema)}>
394
+
395
+ <div style={{
396
+ display: "grid",
397
+ gridTemplateRows: this.props.hideTitleBar ? "auto 1fr" : "auto auto 1fr",
398
+ height: "100%"
399
+ }}>
400
+ {!this.props.hideTitleBar ? this.renderTitleBar() : undefined}
401
+ <div>{!this.state.hideQuickfilters ? this.renderQuickfilter() : undefined}</div>
402
+ {dashboardView}
403
+ {this.props.onDesignChange != null && (
404
+ <SettingsModalComponent
405
+ onDesignChange={this.handleDesignChange}
406
+ schema={this.props.schema}
407
+ dataSource={this.props.dataSource}
408
+ ref={(c: SettingsModalComponent | null) => {
409
+ this.settings = c
410
+ }}
411
+ />
412
+ )}
413
+ {this.state.layoutOptionsOpen && (
414
+ <ModalWindowComponent isOpen={true} outerPadding={10} innerPadding={10}>
415
+ <LayoutOptionsComponent
416
+ design={this.props.design}
417
+ onDesignChange={this.props.onDesignChange!}
418
+ onClose={() => this.setState({ layoutOptionsOpen: false })}
419
+ dashboardView={readonlyDashboardView}
420
+ quickfiltersView={this.renderQuickfilter()}
421
+ />
422
+ </ModalWindowComponent>
423
+ )}
424
+ </div>
425
+ </ActiveTablesContext.Provider>
467
426
  }
468
427
  }