@mwater/visualization 5.1.0 → 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 (101) hide show
  1. package/lib/GlobalFilter.d.ts +13 -0
  2. package/lib/GlobalFilter.js +2 -0
  3. package/lib/MWaterCompleteTableSelectComponent.d.ts +2 -9
  4. package/lib/MWaterContextComponent.d.ts +15 -3
  5. package/lib/MWaterContextComponent.js +38 -13
  6. package/lib/MWaterCustomTablesetListComponent.js +9 -3
  7. package/lib/MWaterGlobalFiltersComponent.d.ts +6 -5
  8. package/lib/MWaterGlobalFiltersComponent.js +4 -4
  9. package/lib/MWaterLoaderComponent.d.ts +12 -2
  10. package/lib/MWaterLoaderComponent.js +9 -1
  11. package/lib/axes/Axis.d.ts +20 -25
  12. package/lib/axes/AxisBuilder.js +9 -7
  13. package/lib/axes/AxisComponent.d.ts +4 -4
  14. package/lib/dashboards/DashboardComponent.d.ts +0 -5
  15. package/lib/dashboards/DashboardComponent.js +2 -29
  16. package/lib/dashboards/DashboardDesign.d.ts +2 -17
  17. package/lib/dashboards/DashboardViewComponent.js +3 -4
  18. package/lib/dashboards/LayoutOptionsComponent.js +4 -3
  19. package/lib/dashboards/SettingsModalComponent.d.ts +4 -15
  20. package/lib/dashboards/SettingsModalComponent.js +24 -38
  21. package/lib/datagrids/DatagridComponent.d.ts +2 -9
  22. package/lib/datagrids/DatagridDataSource.d.ts +3 -3
  23. package/lib/datagrids/DatagridDataSource.js +0 -14
  24. package/lib/datagrids/DatagridDesignerComponent.d.ts +2 -93
  25. package/lib/datagrids/DatagridDesignerComponent.js +8 -6
  26. package/lib/datagrids/DatagridViewComponent.js +1 -1
  27. package/lib/datagrids/FindReplaceModalComponent.d.ts +4 -20
  28. package/lib/datagrids/FindReplaceModalComponent.js +27 -13
  29. package/lib/datagrids/ServerDatagridDataSource.d.ts +1 -1
  30. package/lib/datagrids/ServerDatagridDataSource.js +1 -3
  31. package/lib/demo.js +1 -1
  32. package/lib/index.d.ts +1 -0
  33. package/lib/layouts/blocks/BlocksDisplayComponent.d.ts +2 -1
  34. package/lib/layouts/grid/GridLayoutManager.d.ts +2 -1
  35. package/lib/maps/BufferLayer.js +3 -1
  36. package/lib/maps/ClusterLayer.js +3 -1
  37. package/lib/maps/GridLayer.js +5 -3
  38. package/lib/maps/GridLayerDesigner.js +0 -1
  39. package/lib/maps/LayerSwitcherComponent.js +1 -1
  40. package/lib/maps/MapComponent.d.ts +2 -7
  41. package/lib/maps/MapDesign.d.ts +2 -13
  42. package/lib/maps/MapFiltersDesignerComponent.d.ts +0 -4
  43. package/lib/maps/MapFiltersDesignerComponent.js +4 -5
  44. package/lib/maps/RasterMapViewComponent.d.ts +2 -9
  45. package/lib/maps/RegionSelectComponent.d.ts +2 -1
  46. package/lib/maps/ServerMapDataSource.d.ts +1 -1
  47. package/lib/maps/vectorMaps.d.ts +1 -0
  48. package/lib/maps/vectorMaps.js +10 -2
  49. package/lib/quickfilter/QuickfilterCompiler.d.ts +1 -1
  50. package/lib/widgets/IFrameWidgetComponent.d.ts +2 -9
  51. package/lib/widgets/ImageWidgetComponent.d.ts +6 -24
  52. package/lib/widgets/MapWidget.d.ts +2 -7
  53. package/lib/widgets/MarkdownWidget.d.ts +2 -7
  54. package/lib/widgets/TOCWidget.d.ts +2 -9
  55. package/lib/widgets/charts/ChartWidget.d.ts +3 -15
  56. package/lib/widgets/charts/imagemosaic/ImagePopupComponent.d.ts +2 -7
  57. package/lib/widgets/charts/layered/LayeredChartDesignerComponent.d.ts +2 -31
  58. package/lib/widgets/charts/layered/LayeredChartLayerDesignerComponent.d.ts +2 -7
  59. package/lib/widgets/charts/pivot/IntersectionDesignerComponent.d.ts +73 -66
  60. package/lib/widgets/charts/pivot/PivotChartDesignerComponent.d.ts +10 -6
  61. package/lib/widgets/charts/pivot/PivotChartViewComponent.d.ts +3 -22
  62. package/lib/widgets/charts/pivot/SegmentDesignerComponent.d.ts +55 -58
  63. package/lib/widgets/charts/table/TableChartViewComponent.js +21 -7
  64. package/lib/widgets/text/ExprInsertModalComponent.d.ts +2 -13
  65. package/lib/widgets/text/ExprUpdateModalComponent.d.ts +2 -13
  66. package/lib/widgets/text/TextWidgetDesign.d.ts +3 -1
  67. package/package.json +1 -1
  68. package/src/GlobalFilter.ts +17 -0
  69. package/src/MWaterContextComponent.tsx +37 -19
  70. package/src/MWaterCustomTablesetListComponent.tsx +21 -3
  71. package/src/MWaterGlobalFiltersComponent.ts +8 -8
  72. package/src/MWaterLoaderComponent.ts +8 -1
  73. package/src/axes/Axis.ts +24 -25
  74. package/src/axes/AxisBuilder.ts +9 -8
  75. package/src/dashboards/DashboardComponent.tsx +2 -40
  76. package/src/dashboards/DashboardDesign.ts +2 -22
  77. package/src/dashboards/DashboardViewComponent.ts +4 -5
  78. package/src/dashboards/LayoutOptionsComponent.tsx +6 -4
  79. package/src/dashboards/SettingsModalComponent.tsx +170 -0
  80. package/src/datagrids/DatagridDataSource.ts +6 -12
  81. package/src/datagrids/DatagridDesignerComponent.tsx +22 -18
  82. package/src/datagrids/DatagridViewComponent.ts +3 -3
  83. package/src/datagrids/ExprCellComponent.ts +0 -1
  84. package/src/datagrids/FindReplaceModalComponent.ts +39 -22
  85. package/src/datagrids/ServerDatagridDataSource.ts +1 -2
  86. package/src/demo.ts +1 -1
  87. package/src/index.ts +1 -0
  88. package/src/maps/BufferLayer.ts +3 -1
  89. package/src/maps/ClusterLayer.ts +3 -1
  90. package/src/maps/GridLayer.ts +5 -3
  91. package/src/maps/GridLayerDesigner.tsx +0 -1
  92. package/src/maps/LayerSwitcherComponent.tsx +1 -1
  93. package/src/maps/MapDesign.ts +2 -17
  94. package/src/maps/{MapFiltersDesignerComponent.ts → MapFiltersDesignerComponent.tsx} +25 -25
  95. package/src/maps/ServerMapDataSource.ts +1 -1
  96. package/src/maps/VectorMapViewComponent.tsx +0 -1
  97. package/src/maps/vectorMaps.tsx +10 -1
  98. package/src/quickfilter/QuickfilterCompiler.ts +1 -1
  99. package/src/widgets/charts/table/TableChartViewComponent.ts +21 -7
  100. package/src/widgets/text/TextWidgetDesign.ts +4 -1
  101. package/src/dashboards/SettingsModalComponent.ts +0 -169
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" },
@@ -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
@@ -237,45 +237,6 @@ export default class DashboardComponent extends React.Component<DashboardCompone
237
237
  )
238
238
  }
239
239
 
240
- renderStyleItem(style: any) {
241
- const isActive = (this.props.design.style || "default") === style
242
-
243
- const content = (() => {
244
- switch (style) {
245
- case "default":
246
- return [
247
- R("h4", { key: "name", className: "list-group-item-heading" }, "Classic Dashboard"),
248
- R("p", { key: "description", className: "" }, "Ideal for data display with minimal text")
249
- ]
250
- case "greybg":
251
- return [
252
- R("h4", { key: "name", className: "list-group-item-heading" }, "Framed Dashboard"),
253
- R("p", { key: "description", className: "" }, "Each widget is white on a grey background")
254
- ]
255
- case "story":
256
- return [
257
- R("h4", { key: "name", className: "list-group-item-heading" }, "Story"),
258
- R(
259
- "p",
260
- { key: "description", className: "" },
261
- "Ideal for data-driven storytelling with lots of text. Responsive and mobile-friendly"
262
- )
263
- ]
264
- }
265
- return null
266
- })()
267
-
268
- return R(
269
- "a",
270
- {
271
- key: style,
272
- className: `list-group-item ${isActive ? "active" : ""}`,
273
- onClick: this.handleStyleChange.bind(null, style)
274
- },
275
- content
276
- )
277
- }
278
-
279
240
  renderStyle() {
280
241
  return R(
281
242
  "button",
@@ -379,7 +340,8 @@ export default class DashboardComponent extends React.Component<DashboardCompone
379
340
  locks: this.props.quickfilterLocks,
380
341
  filters: this.getCompiledFilters(),
381
342
  hideTopBorder: this.props.hideTitleBar,
382
- 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
383
345
  })
384
346
  }
385
347
 
@@ -1,7 +1,7 @@
1
- import { JsonQLFilter } from "../JsonQLFilter"
2
1
  import { Quickfilter } from "../quickfilter/Quickfilter"
3
- import { LiteralType, Expr } from "@mwater/expressions"
2
+ import { Expr } from "@mwater/expressions"
4
3
  import { BlocksLayoutOptions, DashboardTheme } from "./layoutOptions"
4
+ import { GlobalFilter } from "../GlobalFilter"
5
5
 
6
6
  /** Dashboard design
7
7
  * Each understands enough of the dashboard design to create widgets.
@@ -35,23 +35,3 @@ export interface DashboardDesign {
35
35
  /** array of global filters. See below. */
36
36
  globalFilters?: GlobalFilter[]
37
37
  }
38
-
39
- /** Global Filters:
40
-
41
- Global filters apply to multiple tables at once if a certain column is present. User-interface to set them is application-specific
42
- and the default (non-mWater) dashboard applies them but does not allow editing.
43
-
44
- */
45
- export interface GlobalFilter {
46
- /** id of column to filter */
47
- columnId: string
48
-
49
- /** type of column to filter (to ensure that consistent) */
50
- columnType: LiteralType
51
-
52
- /** op of expression for filtering */
53
- op: string
54
-
55
- /** array of expressions to use for filtering. field expression for column will be injected as expression 0 in the resulting filter expression */
56
- exprs: Expr[]
57
- }
@@ -14,7 +14,7 @@ import WidgetScopesViewComponent from "../widgets/WidgetScopesViewComponent"
14
14
  import { getLayoutOptions } from "./layoutOptions"
15
15
  import { WidgetComponent } from "./WidgetComponent"
16
16
  import { DashboardDataSource, DashboardDesign, JsonQLFilter } from ".."
17
- import { getMapTilerApiKey, setMapTilerApiKey } from "../maps/vectorMaps"
17
+ import { setPrintingModeEnabled } from "../maps/vectorMaps"
18
18
 
19
19
  export interface DashboardViewComponentProps {
20
20
  /** schema to use */
@@ -147,10 +147,9 @@ export default class DashboardViewComponent extends React.Component<
147
147
 
148
148
  // Call to print the dashboard
149
149
  print = async () => {
150
- // Temporarily disable vector maps as WebGL is not supported in print mode
151
- const mapTilerAPIKey = getMapTilerApiKey()
150
+ // Temporarily enable print mode for vector maps
152
151
  try {
153
- setMapTilerApiKey("")
152
+ setPrintingModeEnabled(true)
154
153
 
155
154
  // Create element at 1080 wide (use as standard printing width)
156
155
  const elem = R(
@@ -162,7 +161,7 @@ export default class DashboardViewComponent extends React.Component<
162
161
  const printer = new ReactElementPrinter()
163
162
  await printer.print(elem, { delay: 5000 })
164
163
  } finally {
165
- setMapTilerApiKey(mapTilerAPIKey)
164
+ setPrintingModeEnabled(false)
166
165
  }
167
166
  }
168
167
 
@@ -137,7 +137,7 @@ export function LayoutOptionsComponent(props: {
137
137
  <div style={{ height: "100%", display: "grid", gridTemplateRows: "auto 1fr" }}>
138
138
  <div key="quickfilters">
139
139
  {layoutOptions.hideQuickfiltersWidth == null ||
140
- sizeOptions[previewSize].value.width > layoutOptions.hideQuickfiltersWidth
140
+ sizeOptions[previewSize].value.width > layoutOptions.hideQuickfiltersWidth
141
141
  ? props.quickfiltersView
142
142
  : null}
143
143
  </div>
@@ -196,9 +196,11 @@ function ThemeToggle(props: { theme?: DashboardTheme; onChange: (theme: Dashboar
196
196
 
197
197
  return (
198
198
  <FormGroup label="Theme">
199
- {renderStyleItem("default")}
200
- {renderStyleItem("greybg")}
201
- {renderStyleItem("story")}
199
+ <div className="list-group">
200
+ {renderStyleItem("default")}
201
+ {renderStyleItem("greybg")}
202
+ {renderStyleItem("story")}
203
+ </div>
202
204
  </FormGroup>
203
205
  )
204
206
  }
@@ -0,0 +1,170 @@
1
+ import _ from "lodash"
2
+ import React from "react"
3
+ const R = React.createElement
4
+ import update from "update-object"
5
+ import { languages } from "../languages"
6
+ import * as ui from "@mwater/react-library/lib/bootstrap"
7
+ import { default as ReactSelect } from "react-select"
8
+ import * as DashboardUtils from "./DashboardUtils"
9
+ import ActionCancelModalComponent from "@mwater/react-library/lib/ActionCancelModalComponent"
10
+ import QuickfiltersDesignComponent from "../quickfilter/QuickfiltersDesignComponent"
11
+ import FiltersDesignerComponent from "../FiltersDesignerComponent"
12
+ import { DataSource, Schema } from "@mwater/expressions"
13
+ import { DashboardDesign } from "./DashboardDesign"
14
+ import { GlobalFiltersElementFactoryContext } from "../MWaterContextComponent"
15
+
16
+ export interface SettingsModalComponentProps {
17
+ onDesignChange: any
18
+ schema: Schema
19
+ dataSource: DataSource
20
+ }
21
+
22
+ interface SettingsModalComponentState {
23
+ design: DashboardDesign | null
24
+ }
25
+
26
+ // Popup with settings for dashboard
27
+ export default class SettingsModalComponent extends React.Component<
28
+ SettingsModalComponentProps,
29
+ SettingsModalComponentState
30
+ > {
31
+ constructor(props: SettingsModalComponentProps) {
32
+ super(props)
33
+ this.state = {
34
+ design: null // Set when being edited
35
+ }
36
+ }
37
+
38
+ show(design: any) {
39
+ return this.setState({ design })
40
+ }
41
+
42
+ handleSave = () => {
43
+ this.props.onDesignChange(this.state.design)
44
+ return this.setState({ design: null })
45
+ }
46
+
47
+ handleCancel = () => {
48
+ return this.setState({ design: null })
49
+ }
50
+ handleDesignChange = (design: any) => {
51
+ return this.setState({ design })
52
+ }
53
+
54
+ handleFiltersChange = (filters: any) => {
55
+ const design = _.extend({}, this.state.design, { filters })
56
+ return this.handleDesignChange(design)
57
+ }
58
+
59
+ handleGlobalFiltersChange = (globalFilters: any) => {
60
+ const design = _.extend({}, this.state.design, { globalFilters })
61
+ return this.handleDesignChange(design)
62
+ }
63
+
64
+ render() {
65
+ // Don't show if not editing
66
+ if (!this.state.design) {
67
+ return null
68
+ }
69
+
70
+ // Get filterable tables
71
+ const filterableTables = DashboardUtils.getFilterableTables(this.state.design, this.props.schema)
72
+
73
+ const localeOptions = _.map(languages, (language) => {
74
+ return {
75
+ value: language.code,
76
+ label: language.en + " (" + language.name + ")"
77
+ }
78
+ })
79
+
80
+ return (
81
+ <ActionCancelModalComponent
82
+ size="large"
83
+ onCancel={this.handleCancel}
84
+ onAction={this.handleSave}
85
+ >
86
+ <div style={{ paddingBottom: 200 }}>
87
+ <h4>Quick Filters</h4>
88
+ <div className="text-muted">
89
+ Quick filters are shown to the user as a dropdown at the top of the dashboard and can be used to filter data of widgets.
90
+ </div>
91
+
92
+ {filterableTables.length > 0 ? (
93
+ <QuickfiltersDesignComponent
94
+ design={this.state.design.quickfilters || []}
95
+ onDesignChange={(design: any) =>
96
+ this.handleDesignChange(update(this.state.design, { quickfilters: { $set: design } }))
97
+ }
98
+ schema={this.props.schema}
99
+ dataSource={this.props.dataSource}
100
+ tables={filterableTables}
101
+ />
102
+ ) : (
103
+ "Nothing to quickfilter. Add widgets to the dashboard"
104
+ )}
105
+
106
+ <h4 style={{ paddingTop: 10 }}>Filters</h4>
107
+ <div className="text-muted">
108
+ Filters are built in to the dashboard and cannot be changed by viewers of the dashboard.
109
+ </div>
110
+
111
+ {filterableTables.length > 0 ? (
112
+ <FiltersDesignerComponent
113
+ schema={this.props.schema}
114
+ dataSource={this.props.dataSource}
115
+ filters={this.state.design.filters}
116
+ onFiltersChange={this.handleFiltersChange}
117
+ filterableTables={filterableTables}
118
+ />
119
+ ) : (
120
+ "Nothing to filter. Add widgets to the dashboard"
121
+ )}
122
+
123
+ <GlobalFiltersElementFactoryContext.Consumer>
124
+ {globalFiltersElementFactory =>
125
+ globalFiltersElementFactory ? (
126
+ <div>
127
+ <h4 style={{ paddingTop: 10 }}>Global Filters</h4>
128
+ {globalFiltersElementFactory({
129
+ schema: this.props.schema,
130
+ dataSource: this.props.dataSource,
131
+ filterableTables,
132
+ globalFilters: this.state.design!.globalFilters || [],
133
+ onChange: this.handleGlobalFiltersChange
134
+ })}
135
+ </div>
136
+ ) : undefined
137
+ }
138
+ </GlobalFiltersElementFactoryContext.Consumer>
139
+
140
+ <h4 style={{ paddingTop: 10 }}>Language</h4>
141
+ <div className="text-muted">
142
+ Controls the preferred language of widgets and uses specified language when available
143
+ </div>
144
+
145
+ <ReactSelect
146
+ value={_.findWhere(localeOptions, { value: this.state.design.locale || "en" }) || null}
147
+ options={localeOptions}
148
+ onChange={(locale: any) =>
149
+ this.handleDesignChange(update(this.state.design, { locale: { $set: locale.value } }))
150
+ }
151
+ />
152
+
153
+ {this.state.design.implicitFiltersEnabled && (
154
+ <>
155
+ <h4 style={{ paddingTop: 10 }}>Advanced</h4>
156
+ <ui.Checkbox
157
+ value={this.state.design.implicitFiltersEnabled != null ? this.state.design.implicitFiltersEnabled : true}
158
+ onChange={(value: boolean) =>
159
+ this.handleDesignChange(update(this.state.design, { implicitFiltersEnabled: { $set: value } }))
160
+ }
161
+ >
162
+ Enable Implicit Filtering (leave unchecked for new dashboards)
163
+ </ui.Checkbox>
164
+ </>
165
+ )}
166
+ </div>
167
+ </ActionCancelModalComponent>
168
+ )
169
+ }
170
+ }
@@ -2,28 +2,22 @@ import { Row } from "@mwater/expressions"
2
2
  import { DatagridDesign, JsonQLFilter } from ".."
3
3
  import { QuickfiltersDataSource } from "../quickfilter/QuickfiltersDataSource"
4
4
 
5
- export default class DatagridDataSource {
5
+ export default interface DatagridDataSource {
6
6
  /** Gets the rows specified */
7
7
  getRows(
8
8
  design: DatagridDesign,
9
9
  offset: number,
10
10
  limit: number,
11
11
  filters: JsonQLFilter[] | undefined,
12
- callback: (error: any, rows: Row[]) => void
13
- ): void {
14
- throw new Error("Not implemented")
15
- }
12
+ callback: (error: any, rows?: Row[]) => void
13
+ ): void
16
14
 
17
15
  countRows(
18
16
  design: DatagridDesign,
19
17
  filters: JsonQLFilter[] | undefined,
20
- callback: (error: any, numRows: number) => void
21
- ): void {
22
- throw new Error("Not implemented")
23
- }
18
+ callback: (error: any, numRows?: number) => void
19
+ ): void
24
20
 
25
21
  /** Gets the quickfilters data source */
26
- getQuickfiltersDataSource(): QuickfiltersDataSource {
27
- throw new Error("Not implemented")
28
- }
22
+ getQuickfiltersDataSource(): QuickfiltersDataSource
29
23
  }
@@ -1,4 +1,3 @@
1
- import PropTypes from "prop-types"
2
1
  import _ from "lodash"
3
2
  import React, { useMemo } from "react"
4
3
  const R = React.createElement
@@ -22,6 +21,7 @@ import { getFormatOptions } from "../valueFormatter"
22
21
  import { getDefaultFormat } from "../valueFormatter"
23
22
  import { DatagridDesignColumn } from "./DatagridDesign"
24
23
  import { DatagridDesign } from ".."
24
+ import { GlobalFiltersElementFactoryContext } from "../MWaterContextComponent"
25
25
 
26
26
  export interface DatagridDesignerComponentProps {
27
27
  /** schema to use */
@@ -36,8 +36,6 @@ export interface DatagridDesignerComponentProps {
36
36
 
37
37
  // Designer for the datagrid. Currenly allows only single-table designs (no subtable rows)
38
38
  export default class DatagridDesignerComponent extends React.Component<DatagridDesignerComponentProps> {
39
- static contextTypes = { globalFiltersElementFactory: PropTypes.func }
40
-
41
39
  handleTableChange = (table: any) => {
42
40
  const design = {
43
41
  table,
@@ -93,20 +91,24 @@ export default class DatagridDesignerComponent extends React.Component<DatagridD
93
91
  value: this.props.design.filter,
94
92
  onChange: this.handleFilterChange
95
93
  }),
96
- this.context.globalFiltersElementFactory
97
- ? R(
98
- "div",
99
- { style: { marginTop: 20 } },
100
- this.context.globalFiltersElementFactory({
101
- schema: this.props.schema,
102
- dataSource: this.props.dataSource,
103
- filterableTables: [this.props.design.table],
104
- globalFilters: this.props.design.globalFilters,
105
- onChange: this.handleGlobalFiltersChange,
106
- nullIfIrrelevant: true
107
- })
108
- )
109
- : undefined
94
+ <GlobalFiltersElementFactoryContext.Consumer>
95
+ {(globalFiltersElementFactory) =>
96
+ globalFiltersElementFactory
97
+ ? R(
98
+ "div",
99
+ { style: { marginTop: 20 } },
100
+ globalFiltersElementFactory({
101
+ schema: this.props.schema,
102
+ dataSource: this.props.dataSource,
103
+ filterableTables: [this.props.design.table!],
104
+ globalFilters: this.props.design.globalFilters,
105
+ onChange: this.handleGlobalFiltersChange,
106
+ nullIfIrrelevant: true
107
+ })
108
+ )
109
+ : undefined
110
+ }
111
+ </GlobalFiltersElementFactoryContext.Consumer>
110
112
  )
111
113
  },
112
114
  {
@@ -562,7 +564,9 @@ class ColumnDesignerComponent extends React.Component<ColumnDesignerComponentPro
562
564
  }),
563
565
  this.renderSplit(),
564
566
  this.renderFormat(),
565
- error ? R("i", { className: "fa fa-exclamation-circle text-danger" }) : undefined
567
+ error ? <span className="text-danger">
568
+ <i className="fa fa-exclamation-circle" /> {error}
569
+ </span> : undefined
566
570
  ),
567
571
 
568
572
  R(
@@ -191,8 +191,8 @@ export default class DatagridViewComponent extends React.Component<
191
191
  }
192
192
 
193
193
  const newRows = produce(this.state.rows, draft => {
194
- if (rows[0]) {
195
- draft[rowIndex] = rows[0]
194
+ if (rows![0]) {
195
+ draft[rowIndex] = rows![0]
196
196
  }
197
197
  else {
198
198
  // If row missing, remove it from list
@@ -308,7 +308,7 @@ export default class DatagridViewComponent extends React.Component<
308
308
  }
309
309
 
310
310
  handleRowDoubleClick = (ev: any, rowIndex: any) => {
311
- if (this.props.onRowDoubleClick != null && this.state.rows[rowIndex].id) {
311
+ if (this.props.onRowDoubleClick != null && this.state.rows[rowIndex]?.id) {
312
312
  this.props.onRowDoubleClick(this.props.design.table!, this.state.rows[rowIndex].id, rowIndex)
313
313
  }
314
314
  }
@@ -1,4 +1,3 @@
1
- import PropTypes from "prop-types"
2
1
  import _ from "lodash"
3
2
  import React from "react"
4
3
  const R = React.createElement