@saltcorn/builder 1.6.0-alpha.1 → 1.6.0-alpha.11

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 (40) hide show
  1. package/dist/builder_bundle.js +85 -1
  2. package/dist/builder_bundle.js.LICENSE.txt +18 -51
  3. package/package.json +31 -27
  4. package/src/components/Builder.js +445 -155
  5. package/src/components/Library.js +25 -13
  6. package/src/components/RenderNode.js +26 -8
  7. package/src/components/Toolbox.js +333 -269
  8. package/src/components/elements/Action.js +144 -29
  9. package/src/components/elements/Aggregation.js +20 -23
  10. package/src/components/elements/ArrayManager.js +17 -10
  11. package/src/components/elements/BoxModelEditor.js +19 -17
  12. package/src/components/elements/Card.js +47 -34
  13. package/src/components/elements/Clone.js +74 -2
  14. package/src/components/elements/Column.js +1 -1
  15. package/src/components/elements/Columns.js +130 -121
  16. package/src/components/elements/Container.js +185 -92
  17. package/src/components/elements/DropDownFilter.js +10 -8
  18. package/src/components/elements/DropMenu.js +18 -9
  19. package/src/components/elements/Field.js +9 -7
  20. package/src/components/elements/HTMLCode.js +3 -1
  21. package/src/components/elements/Image.js +20 -15
  22. package/src/components/elements/JoinField.js +15 -11
  23. package/src/components/elements/Link.js +18 -16
  24. package/src/components/elements/ListColumn.js +7 -3
  25. package/src/components/elements/ListColumns.js +4 -1
  26. package/src/components/elements/MonacoEditor.js +4 -2
  27. package/src/components/elements/Page.js +7 -4
  28. package/src/components/elements/RelationBadges.js +16 -11
  29. package/src/components/elements/RelationOnDemandPicker.js +18 -12
  30. package/src/components/elements/SearchBar.js +37 -10
  31. package/src/components/elements/Table.js +72 -65
  32. package/src/components/elements/Tabs.js +18 -15
  33. package/src/components/elements/Text.js +19 -14
  34. package/src/components/elements/ToggleFilter.js +28 -25
  35. package/src/components/elements/View.js +36 -18
  36. package/src/components/elements/ViewLink.js +15 -11
  37. package/src/components/elements/utils.js +224 -55
  38. package/src/components/storage.js +33 -134
  39. package/src/hooks/useTranslation.js +11 -0
  40. package/src/index.js +6 -3
@@ -5,6 +5,7 @@
5
5
  */
6
6
 
7
7
  import React, { Fragment, useState, useContext, useEffect } from "react";
8
+ import useTranslation from "../../hooks/useTranslation";
8
9
  import { ntimes } from "./Columns";
9
10
  import { Column } from "./Column";
10
11
  import optionsCtx from "../context";
@@ -34,6 +35,7 @@ const Tabs = ({
34
35
  field,
35
36
  setting_tab_n,
36
37
  }) => {
38
+ const { t } = useTranslation();
37
39
  const {
38
40
  selected,
39
41
  connectors: { connect, drag },
@@ -121,7 +123,7 @@ const Tabs = ({
121
123
  (typeof titles[ix].label === "undefined"
122
124
  ? titles[ix]
123
125
  : titles[ix].label === ""
124
- ? "(empty)"
126
+ ? t("(empty)")
125
127
  : titles[ix].label)}
126
128
  </a>
127
129
  </li>
@@ -173,6 +175,7 @@ export /**
173
175
  * @subcategory components
174
176
  */
175
177
  const TabsSettings = () => {
178
+ const { t } = useTranslation();
176
179
  const node = useNode((node) => ({
177
180
  tabsStyle: node.data.props.tabsStyle,
178
181
  ntabs: node.data.props.ntabs,
@@ -237,11 +240,11 @@ const TabsSettings = () => {
237
240
  styleOptions.push("Value switch");
238
241
 
239
242
  return (
240
- <table className="w-100" accordiontitle="Placement">
243
+ <table className="w-100" accordiontitle={t("Placement")}>
241
244
  <tbody>
242
245
  <tr>
243
246
  <th>
244
- <label>Style</label>
247
+ <label>{t("Style")}</label>
245
248
  </th>
246
249
  <td>
247
250
  <select
@@ -257,7 +260,7 @@ const TabsSettings = () => {
257
260
  <Fragment>
258
261
  <tr>
259
262
  <td>
260
- <label>Field</label>
263
+ <label>{t("Field")}</label>
261
264
  </td>
262
265
  <td>
263
266
  <select
@@ -289,7 +292,7 @@ const TabsSettings = () => {
289
292
  onChange={setAProp("independent", { checked: true })}
290
293
  />
291
294
  <label className="form-check-label">
292
- Open independently
295
+ {t("Open independently")}
293
296
  </label>
294
297
  </div>
295
298
  </td>
@@ -305,7 +308,7 @@ const TabsSettings = () => {
305
308
  onChange={setAProp("lazyLoadViews", { checked: true })}
306
309
  />
307
310
  <label className="form-check-label">
308
- Lazy load views
311
+ {t("Lazy load views")}
309
312
  </label>
310
313
  </div>
311
314
  </td>
@@ -323,7 +326,7 @@ const TabsSettings = () => {
323
326
  checked={deeplink}
324
327
  onChange={setAProp("deeplink", { checked: true })}
325
328
  />
326
- <label className="form-check-label">Deep link</label>
329
+ <label className="form-check-label">{t("Deep link")}</label>
327
330
  </div>
328
331
  </td>
329
332
  </tr>
@@ -338,7 +341,7 @@ const TabsSettings = () => {
338
341
  onChange={setAProp("serverRendered", { checked: true })}
339
342
  />
340
343
  <label className="form-check-label">
341
- Server rendering
344
+ {t("Server rendering")}
342
345
  </label>
343
346
  </div>
344
347
  </td>
@@ -346,7 +349,7 @@ const TabsSettings = () => {
346
349
  {serverRendered ? (
347
350
  <tr>
348
351
  <th>
349
- <label>Identifier</label>
352
+ <label>{t("Identifier")}</label>
350
353
  </th>
351
354
  <td>
352
355
  <input
@@ -370,7 +373,7 @@ const TabsSettings = () => {
370
373
  onChange={setAProp("lazyLoadViews", { checked: true })}
371
374
  />
372
375
  <label className="form-check-label">
373
- Lazy load views
376
+ {t("Lazy load views")}
374
377
  </label>
375
378
  </div>
376
379
  </td>
@@ -389,7 +392,7 @@ const TabsSettings = () => {
389
392
  onChange={setAProp("startClosed", { checked: true })}
390
393
  />
391
394
  <label className="form-check-label">
392
- Inititally closed
395
+ {t("Initially closed")}
393
396
  </label>
394
397
  </div>
395
398
  </td>
@@ -407,7 +410,7 @@ const TabsSettings = () => {
407
410
  onChange={setAProp("disable_inactive", { checked: true })}
408
411
  />
409
412
  <label className="form-check-label">
410
- Disable inactive inputs
413
+ {t("Disable inactive inputs")}
411
414
  </label>
412
415
  </div>
413
416
  </td>
@@ -427,7 +430,7 @@ const TabsSettings = () => {
427
430
  </td>
428
431
  </tr>
429
432
  <tr>
430
- <th colSpan="2">Title</th>
433
+ <th colSpan="2">{t("Title")}</th>
431
434
  </tr>
432
435
  <tr>
433
436
  <td colSpan={2}>
@@ -449,7 +452,7 @@ const TabsSettings = () => {
449
452
  options.mode === "filter" ? (
450
453
  <Fragment>
451
454
  <tr>
452
- <th colSpan="2">Show if formula</th>
455
+ <th colSpan="2">{t("Show if formula")}</th>
453
456
  </tr>
454
457
  <tr>
455
458
  <td colSpan={2}>
@@ -489,7 +492,7 @@ const TabsSettings = () => {
489
492
  });
490
493
  }}
491
494
  />
492
- <label className="form-check-label">Initially open</label>
495
+ <label className="form-check-label">{t("Initially open")}</label>
493
496
  </div>
494
497
  </td>
495
498
  </tr>
@@ -21,12 +21,14 @@ import {
21
21
  } from "./utils";
22
22
  import ContentEditable from "react-contenteditable";
23
23
  import optionsCtx from "../context";
24
- import CKEditor from "ckeditor4-react";
24
+ import { CKEditor } from "ckeditor4-react";
25
25
  import FontIconPicker from "@fonticonpicker/react-fonticonpicker";
26
26
  import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
27
27
  import fas from "@fortawesome/free-solid-svg-icons";
28
28
  import far from "@fortawesome/free-regular-svg-icons";
29
29
  import { SingleLineEditor } from "./MonacoEditor";
30
+ import useTranslation from "../../hooks/useTranslation";
31
+
30
32
  const ckConfig = {
31
33
  toolbarGroups: [
32
34
  { name: "document", groups: ["mode", "document", "doctools"] },
@@ -109,7 +111,7 @@ const Text = ({
109
111
  isFormula.text ? "font-monospace" : ""
110
112
  } ${selected ? "selected-node" : ""}`}
111
113
  ref={(dom) => connect(drag(dom))}
112
- onClick={(e) => selected && setEditable(true)}
114
+ onDoubleClick={(e) => selected && setEditable(true)}
113
115
  style={{
114
116
  ...(font ? { fontFamily: font } : {}),
115
117
  ...reactifyStyles(style || {}),
@@ -131,7 +133,7 @@ const Text = ({
131
133
  ) : editable ? (
132
134
  <ErrorBoundary>
133
135
  <CKEditor
134
- data={text}
136
+ initData={text || ""}
135
137
  style={{ display: "inline" }}
136
138
  onChange={(e) =>
137
139
  setProp((props) => (props.text = e.editor.getData()))
@@ -155,7 +157,9 @@ export /**
155
157
  * @subcategory components
156
158
  */
157
159
  const TextSettings = () => {
160
+ const { t } = useTranslation();
158
161
  const node = useNode((node) => ({
162
+ id: node.id,
159
163
  text: node.data.props.text,
160
164
  block: node.data.props.block,
161
165
  inline: node.data.props.inline,
@@ -198,17 +202,18 @@ const TextSettings = () => {
198
202
  setProp((prop) => (prop.isFormula.text = checked));
199
203
  }}
200
204
  />
201
- <label className="form-check-label">Formula?</label>
205
+ <label className="form-check-label">{t("Formula?")}</label>
202
206
  </div>
203
207
  )}
204
- <label>Text to display</label>
208
+ <label>{t("Text to display")}</label>
205
209
  {allowFormula && isFormula.text ? (
206
210
  <SingleLineEditor setProp={setProp} value={text} propKey="text" />
207
211
  ) : (
208
212
  <ErrorBoundary>
209
213
  <div className="border">
210
214
  <CKEditor
211
- data={text}
215
+ key={node.id}
216
+ initData={text || ""}
212
217
  onChange={(e) => {
213
218
  if (e.editor) {
214
219
  const text = e.editor.getData();
@@ -223,7 +228,7 @@ const TextSettings = () => {
223
228
  )}
224
229
  {mode === "edit" && (
225
230
  <Fragment>
226
- <label>Label for Field</label>
231
+ <label>{t("Label for Field")}</label>
227
232
  <select
228
233
  value={labelFor}
229
234
  onChange={setAProp("labelFor")}
@@ -243,7 +248,7 @@ const TextSettings = () => {
243
248
  <TextStyleRow textStyle={textStyle} setProp={setProp} />
244
249
  <tr>
245
250
  <td>
246
- <label>Icon</label>
251
+ <label>{t("Icon")}</label>
247
252
  </td>
248
253
  <td>
249
254
  <FontIconPicker
@@ -258,7 +263,7 @@ const TextSettings = () => {
258
263
  <SettingsRow
259
264
  field={{
260
265
  name: "font",
261
- label: "Font family",
266
+ label: t("Font family"),
262
267
  type: "Font",
263
268
  }}
264
269
  node={node}
@@ -267,7 +272,7 @@ const TextSettings = () => {
267
272
  <SettingsRow
268
273
  field={{
269
274
  name: "font-size",
270
- label: "Font size",
275
+ label: t("Font size"),
271
276
  type: "DimUnits",
272
277
  }}
273
278
  node={node}
@@ -277,7 +282,7 @@ const TextSettings = () => {
277
282
  <SettingsRow
278
283
  field={{
279
284
  name: "font-weight",
280
- label: "Weight",
285
+ label: t("Weight"),
281
286
  type: "Integer",
282
287
  min: 100,
283
288
  max: 900,
@@ -290,7 +295,7 @@ const TextSettings = () => {
290
295
  <SettingsRow
291
296
  field={{
292
297
  name: "line-height",
293
- label: "Line height",
298
+ label: t("Line height"),
294
299
  type: "DimUnits",
295
300
  }}
296
301
  node={node}
@@ -298,7 +303,7 @@ const TextSettings = () => {
298
303
  isStyle={true}
299
304
  />
300
305
  <tr>
301
- <td>Class</td>
306
+ <td>{t("Class")}</td>
302
307
  <td>
303
308
  <input
304
309
  type="text"
@@ -312,7 +317,7 @@ const TextSettings = () => {
312
317
  <SettingsRow
313
318
  field={{
314
319
  name: "color",
315
- label: "Color",
320
+ label: t("Color"),
316
321
  type: "Color",
317
322
  }}
318
323
  node={node}
@@ -4,8 +4,9 @@
4
4
  * @subcategory components / elements
5
5
  */
6
6
 
7
- import React, { useContext, Fragment } from "react";
7
+ import React, { Fragment, useState, useContext, useEffect } from "react";
8
8
  import { useNode } from "@craftjs/core";
9
+ import useTranslation from "../../hooks/useTranslation";
9
10
  import optionsCtx from "../context";
10
11
  import { blockProps, BlockSetting, setAPropGen, buildOptions } from "./utils";
11
12
 
@@ -32,6 +33,7 @@ const ToggleFilter = ({
32
33
  size,
33
34
  style,
34
35
  }) => {
36
+ const { t } = useTranslation();
35
37
  const {
36
38
  selected,
37
39
  connectors: { connect, drag },
@@ -43,7 +45,7 @@ const ToggleFilter = ({
43
45
  ref={(dom) => connect(drag(dom))}
44
46
  >
45
47
  <button className={`btn btn-outline-${style || "primary"} ${size}`}>
46
- {label || value || preset_value || "Set label"}
48
+ {label || value || preset_value || t("Set label")}
47
49
  </button>
48
50
  </span>
49
51
  );
@@ -56,6 +58,7 @@ export /**
56
58
  * @subcategory components
57
59
  */
58
60
  const ToggleFilterSettings = () => {
61
+ const { t } = useTranslation();
59
62
  const {
60
63
  actions: { setProp },
61
64
  name,
@@ -85,7 +88,7 @@ const ToggleFilterSettings = () => {
85
88
  <tbody>
86
89
  <tr>
87
90
  <td>
88
- <label>Field</label>
91
+ <label>{t("Field")}</label>
89
92
  </td>
90
93
  <td>
91
94
  <select
@@ -115,7 +118,7 @@ const ToggleFilterSettings = () => {
115
118
  </tr>
116
119
  <tr>
117
120
  <td>
118
- <label>Value</label>
121
+ <label>{t("Value")}</label>
119
122
  </td>
120
123
  <td>
121
124
  {isBool ? (
@@ -124,9 +127,9 @@ const ToggleFilterSettings = () => {
124
127
  className="w-100 form-select"
125
128
  onChange={setAProp("value")}
126
129
  >
127
- <option value="on">True</option>
128
- <option value="off">False</option>
129
- <option value="?">Both</option>
130
+ <option value="on">{t("True")}</option>
131
+ <option value="off">{t("False")}</option>
132
+ <option value="?">{t("Both")}</option>
130
133
  </select>
131
134
  ) : (
132
135
  <input
@@ -140,7 +143,7 @@ const ToggleFilterSettings = () => {
140
143
  {preset_options && preset_options.length > 0 ? (
141
144
  <tr>
142
145
  <td>
143
- <label>Preset</label>
146
+ <label>{t("Preset")}</label>
144
147
  </td>
145
148
  <td>
146
149
  <select
@@ -160,7 +163,7 @@ const ToggleFilterSettings = () => {
160
163
  ) : null}
161
164
  <tr>
162
165
  <td>
163
- <label>Label</label>
166
+ <label>{t("Label")}</label>
164
167
  </td>
165
168
  <td>
166
169
  <input
@@ -172,7 +175,7 @@ const ToggleFilterSettings = () => {
172
175
  </tr>
173
176
  <tr>
174
177
  <td>
175
- <label>Button size</label>
178
+ <label>{t("Button size")}</label>
176
179
  </td>
177
180
  <td>
178
181
  <select
@@ -180,18 +183,18 @@ const ToggleFilterSettings = () => {
180
183
  value={size}
181
184
  onChange={setAProp("size")}
182
185
  >
183
- <option value="">Standard</option>
184
- <option value="btn-lg">Large</option>
185
- <option value="btn-sm">Small</option>
186
- <option value="btn-block">Block</option>
187
- <option value="btn-block btn-lg">Large block</option>
188
- <option value="btn-block btn-sm">Small block</option>
186
+ <option value="">{t("Standard")}</option>
187
+ <option value="btn-lg">{t("Large")}</option>
188
+ <option value="btn-sm">{t("Small")}</option>
189
+ <option value="btn-block">{t("Block")}</option>
190
+ <option value="btn-block btn-lg">{t("Large block")}</option>
191
+ <option value="btn-block btn-sm">{t("Small block")}</option>
189
192
  </select>
190
193
  </td>
191
194
  </tr>
192
195
  <tr>
193
196
  <td>
194
- <label>Button style</label>
197
+ <label>{t("Button style")}</label>
195
198
  </td>
196
199
  <td>
197
200
  <select
@@ -201,14 +204,14 @@ const ToggleFilterSettings = () => {
201
204
  >
202
205
  {buildOptions(
203
206
  [
204
- "primary",
205
- "secondary",
206
- "success",
207
- "danger",
208
- "warning",
209
- "info",
210
- "light",
211
- "dark",
207
+ t("primary"),
208
+ t("secondary"),
209
+ t("success"),
210
+ t("danger"),
211
+ t("warning"),
212
+ t("info"),
213
+ t("light"),
214
+ t("dark"),
212
215
  ],
213
216
  { valAttr: true, capitalize: true }
214
217
  )}
@@ -8,6 +8,7 @@ import React, { Fragment, useEffect, useMemo } from "react";
8
8
  import { useNode } from "@craftjs/core";
9
9
  import optionsCtx from "../context";
10
10
  import previewCtx from "../preview_context";
11
+ import useTranslation from "../../hooks/useTranslation";
11
12
  import relationsCtx from "../relations_context";
12
13
  import Select from "react-select";
13
14
 
@@ -19,6 +20,8 @@ import {
19
20
  HelpTopicLink,
20
21
  initialRelation,
21
22
  buildLayers,
23
+ reactSelectStyles,
24
+ builderSelectClassName,
22
25
  } from "./utils";
23
26
 
24
27
  import { RelationBadges } from "./RelationBadges";
@@ -47,6 +50,7 @@ const View = ({ name, view, configuration, state }) => {
47
50
  node_id,
48
51
  connectors: { connect, drag },
49
52
  } = useNode((node) => ({ selected: node.events.selected, node_id: node.id }));
53
+ const { t } = useTranslation();
50
54
  const options = React.useContext(optionsCtx);
51
55
 
52
56
  let viewname = view;
@@ -81,7 +85,7 @@ const View = ({ name, view, configuration, state }) => {
81
85
  dangerouslySetInnerHTML={{ __html: myPreview }}
82
86
  ></div>
83
87
  ) : (
84
- `View: ${label}`
88
+ `${t("View")}: ${label}`
85
89
  )}
86
90
  </div>
87
91
  );
@@ -94,6 +98,7 @@ export /**
94
98
  * @namespace
95
99
  */
96
100
  const ViewSettings = () => {
101
+ const { t } = useTranslation();
97
102
  const node = useNode((node) => ({
98
103
  name: node.data.props.name,
99
104
  view: node.data.props.view,
@@ -157,6 +162,7 @@ const ViewSettings = () => {
157
162
  }
158
163
  if (viewname && viewname.includes(".")) viewname = viewname.split(".")[0];
159
164
 
165
+ let cacheWasPopulated = false;
160
166
  if (
161
167
  finder &&
162
168
  !(relationsCache[tableName] && relationsCache[tableName][viewname])
@@ -173,8 +179,13 @@ const ViewSettings = () => {
173
179
  );
174
180
  relationsCache[tableName] = relationsCache[tableName] || {};
175
181
  relationsCache[tableName][viewname] = { relations, layers };
176
- setRelationsCache({ ...relationsCache });
182
+ cacheWasPopulated = true;
177
183
  }
184
+ useEffect(() => {
185
+ if (cacheWasPopulated) {
186
+ setRelationsCache({ ...relationsCache });
187
+ }
188
+ });
178
189
  const [relationsData, setRelationsData] = finder
179
190
  ? React.useState(relationsCache[tableName][viewname])
180
191
  : [undefined, undefined];
@@ -188,18 +199,23 @@ const ViewSettings = () => {
188
199
  subView.display_type
189
200
  );
190
201
  }
191
- if (
202
+ const needsInitialRelation =
192
203
  options.mode !== "filter" &&
193
204
  subView?.table_id &&
194
205
  !safeRelation &&
195
206
  !hasLegacyRelation &&
196
- relationsData?.relations.length > 0
197
- ) {
207
+ relationsData?.relations.length > 0;
208
+ if (needsInitialRelation) {
198
209
  safeRelation = initialRelation(relationsData.relations);
199
- setProp((prop) => {
200
- prop.relation = safeRelation.relationString;
201
- });
202
210
  }
211
+ useEffect(() => {
212
+ if (needsInitialRelation) {
213
+ const rel = initialRelation(relationsData.relations);
214
+ setProp((prop) => {
215
+ prop.relation = rel.relationString;
216
+ });
217
+ }
218
+ }, [needsInitialRelation]);
203
219
  const helpContext = { view_name: viewname };
204
220
  if (options.tableName) helpContext.srcTable = options.tableName;
205
221
  const set_view_name = (e) => {
@@ -235,7 +251,7 @@ const ViewSettings = () => {
235
251
  } else
236
252
  window.notifyAlert({
237
253
  type: "warning",
238
- text: `${target_value} has no relations`,
254
+ text: `${target_value} ${t("has no relations")}`,
239
255
  });
240
256
  }
241
257
  }
@@ -262,11 +278,12 @@ const ViewSettings = () => {
262
278
  <Select
263
279
  options={viewOptions}
264
280
  value={selectedView}
265
- className="react-select view-selector"
281
+ className={builderSelectClassName("react-select view-selector")}
282
+ classNamePrefix="builder-select"
266
283
  onChange={set_view_name}
267
284
  onBlur={set_view_name}
268
285
  menuPortalTarget={document.body}
269
- styles={{ menuPortal: (base) => ({ ...base, zIndex: 19999 }) }}
286
+ styles={reactSelectStyles()}
270
287
  ></Select>
271
288
  )}
272
289
  </div>
@@ -304,7 +321,8 @@ const ViewSettings = () => {
304
321
  <Select
305
322
  options={viewOptions}
306
323
  value={selectedView}
307
- className="react-select view-selector"
324
+ className={builderSelectClassName("react-select view-selector")}
325
+ classNamePrefix="builder-select"
308
326
  onChange={(e) => {
309
327
  const target_value = e?.target?.value || e?.value;
310
328
  setProp((prop) => {
@@ -312,7 +330,7 @@ const ViewSettings = () => {
312
330
  });
313
331
  }}
314
332
  menuPortalTarget={document.body}
315
- styles={{ menuPortal: (base) => ({ ...base, zIndex: 19999 }) }}
333
+ styles={reactSelectStyles()}
316
334
  ></Select>
317
335
  )}
318
336
  </div>
@@ -322,7 +340,7 @@ const ViewSettings = () => {
322
340
  theview?.viewtemplate === "Edit" &&
323
341
  targetTable ? (
324
342
  <div>
325
- <label>Order field</label>
343
+ <label>{t("Order field")}</label>
326
344
  <select
327
345
  value={order_field}
328
346
  className="form-control form-select"
@@ -341,7 +359,7 @@ const ViewSettings = () => {
341
359
  {options.mode !== "edit" && (
342
360
  <Fragment>
343
361
  <div>
344
- <label>State</label>
362
+ <label>{t("State")}</label>
345
363
  <select
346
364
  value={state}
347
365
  className="form-control form-select"
@@ -365,7 +383,7 @@ const ViewSettings = () => {
365
383
  fixed_state_fields &&
366
384
  fixed_state_fields.length > 0 && (
367
385
  <Fragment>
368
- <h6>View state fields</h6>
386
+ <h6>{t("View state fields")}</h6>
369
387
  <ConfigForm
370
388
  fields={fixed_state_fields}
371
389
  configuration={configuration || {}}
@@ -379,7 +397,7 @@ const ViewSettings = () => {
379
397
  {
380
398
  <Fragment>
381
399
  <label>
382
- Extra state Formula
400
+ {t("Extra state Formula")}
383
401
  <HelpTopicLink topic="Extra state formula" {...helpContext} />
384
402
  </label>
385
403
  <SingleLineEditor
@@ -401,7 +419,7 @@ const ViewSettings = () => {
401
419
  target="_blank"
402
420
  href={`/viewedit/config/${viewname}`}
403
421
  >
404
- Configure this view
422
+ {t("Configure this view")}
405
423
  </a>
406
424
  ) : null}
407
425
  </div>