Skip to content

Commit e3cd82c

Browse files
authored
feat(field): add a new property autorefresh (#136)
1 parent d53e07f commit e3cd82c

9 files changed

+217
-15
lines changed

src/Field.ts

+25
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import Widget from "./Widget";
22
import { replaceEntities, isTrue } from "./helpers/attributeParser";
3+
import { parseBoolAttribute } from "./helpers/nodeParser";
34

45
class Field extends Widget {
56
/**
@@ -125,6 +126,27 @@ class Field extends Widget {
125126
this._selectionValues = value;
126127
}
127128

129+
_autoRefresh?: boolean = false;
130+
get autoRefresh(): boolean {
131+
return this._autoRefresh ?? false;
132+
}
133+
134+
set autoRefresh(value: boolean) {
135+
this._autoRefresh = value;
136+
}
137+
138+
get readOnly(): boolean | undefined {
139+
if (this.autoRefresh) {
140+
return true;
141+
} else {
142+
return super.readOnly;
143+
}
144+
}
145+
146+
set readOnly(value: boolean | undefined) {
147+
super.readOnly = value;
148+
}
149+
128150
constructor(props: any) {
129151
super(props);
130152

@@ -176,6 +198,9 @@ class Field extends Widget {
176198
if (props.help_inline) {
177199
this.tooltipInline = isTrue(props.help_inline);
178200
}
201+
if (props.autorefresh) {
202+
this.autoRefresh = parseBoolAttribute(props.autorefresh);
203+
}
179204
}
180205
}
181206

src/Form.ts

+13
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,14 @@ class Form {
124124
this._invisibleFields = value;
125125
}
126126

127+
/**
128+
* List of autorefreshable fields
129+
*/
130+
_autorefreshableFields: string[] = [];
131+
get autorefreshableFields(): string[] {
132+
return this._autorefreshableFields;
133+
}
134+
127135
/**
128136
* Context for each field in the form
129137
*/
@@ -170,6 +178,11 @@ class Form {
170178
this._contextForFields[unknownWidget._id] = widget._context;
171179
}
172180
});
181+
182+
// Also we store all the autorefreshables fields in a list
183+
this._autorefreshableFields = allWidgets
184+
.filter((widget) => widget instanceof Field && widget.autoRefresh)
185+
.map((field) => (field as Field)._id);
173186
}
174187

175188
parseNode({

src/Tree.ts

+13-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import WidgetFactory from "./WidgetFactory";
22
import Widget from "./Widget";
33
import { replaceEntities } from "./helpers/attributeParser";
4-
import { ParsedNode } from "./helpers/nodeParser";
4+
import { parseBoolAttribute, ParsedNode } from "./helpers/nodeParser";
55
import * as txml from "txml";
66
import { parseContext } from "./helpers/contextParser";
77

@@ -67,6 +67,14 @@ class Tree {
6767
this._contextForFields = value;
6868
}
6969

70+
/**
71+
* List of autorefreshable fields
72+
*/
73+
_autorefreshableFields: string[] = [];
74+
get autorefreshableFields(): string[] {
75+
return this._autorefreshableFields;
76+
}
77+
7078
/**
7179
* Is infinite
7280
*/
@@ -145,6 +153,10 @@ class Tree {
145153
const widget = widgetFactory.createWidget(widgetType, mergedAttrs);
146154
this._columns.push(widget);
147155
}
156+
157+
if (parseBoolAttribute(mergedAttrs.autorefresh)) {
158+
this._autorefreshableFields.push(name);
159+
}
148160
}
149161
});
150162
}

src/Widget.ts

+2-13
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { replaceEntities, parseWidgetProps } from "./helpers/attributeParser";
2+
import { parseBoolAttribute } from "./helpers/nodeParser";
23

34
abstract class Widget {
45
/**
@@ -132,19 +133,7 @@ abstract class Widget {
132133
this._colspan = +props.colspan;
133134
}
134135
if (props.readonly !== undefined) {
135-
if (
136-
props.readonly === "1" ||
137-
props.readonly === 1 ||
138-
props.readonly === true
139-
) {
140-
this._readOnly = true;
141-
} else if (
142-
props.readonly === "0" ||
143-
props.readonly === 0 ||
144-
props.readonly === false
145-
) {
146-
this._readOnly = false;
147-
}
136+
this._readOnly = parseBoolAttribute(props.readonly);
148137
}
149138
if (props.invisible) {
150139
if (

src/helpers/nodeParser.ts

+7-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,13 @@ type ParsedNode = {
55
};
66

77
const parseBoolAttribute = (attr: any): boolean => {
8-
if (attr === 1 || attr === "1" || attr === true || attr === "True") {
8+
if (
9+
attr === 1 ||
10+
attr === "1" ||
11+
attr === true ||
12+
attr === "True" ||
13+
attr === "true"
14+
) {
915
return true;
1016
} else {
1117
return false;

src/spec/Field.spec.ts

+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import { describe, it, expect } from "vitest";
2+
import Field from "../Field";
3+
4+
describe("Field", () => {
5+
describe("with the autoRefresh property", () => {
6+
it("should be false as default", () => {
7+
const props = {};
8+
const field = new Field(props);
9+
expect(field.autoRefresh).toBe(false);
10+
});
11+
it("should work with text", () => {
12+
const props = {
13+
autorefresh: "1",
14+
};
15+
const field = new Field(props);
16+
expect(field.autoRefresh).toBe(true);
17+
});
18+
describe("if autorefresh is not a valid boold", () => {
19+
it("should return false", () => {
20+
const props = {
21+
autorefresh: "abc",
22+
};
23+
const field = new Field(props);
24+
expect(field.autoRefresh).toBe(false);
25+
});
26+
});
27+
it("should return true for readOnly if autoRefresh is set", () => {
28+
const props = {
29+
autorefresh: true,
30+
};
31+
const field = new Field(props);
32+
expect(field.readOnly).toBe(true);
33+
});
34+
});
35+
});

src/spec/Form.spec.ts

+50
Original file line numberDiff line numberDiff line change
@@ -6015,6 +6015,56 @@ describe("A Form", () => {
60156015
expect(field_char?.type).toBe("arrow_steps");
60166016
expect(field_char?.id).toBe("field_char");
60176017
});
6018+
it("a field with autorefresh evaluated in attrs should be present in form autorefreshable fields property", () => {
6019+
const fields = {
6020+
field_char: {
6021+
string: "Etapa",
6022+
type: "char",
6023+
},
6024+
state: {
6025+
readonly: true,
6026+
required: true,
6027+
selection: [
6028+
["esborrany", "Borrador"],
6029+
["validar", "Validar"],
6030+
["pendent", "Pendiente"],
6031+
["activa", "Activa"],
6032+
["cancelada", "Cancelada"],
6033+
["contracte", "Activación Contrato"],
6034+
["novapolissa", "Creación nuevo contrato"],
6035+
["modcontractual", "Modificación Contractual"],
6036+
["impagament", "Impago"],
6037+
["tall", "Corte"],
6038+
["running", "En ejecución"],
6039+
["baixa", "Baja"],
6040+
["facturacio", "Facturación"],
6041+
],
6042+
string: "Estado",
6043+
type: "selection",
6044+
views: {},
6045+
},
6046+
};
6047+
6048+
const xmlViewForm = `<?xml version="1.0"?>
6049+
<form string="Form1">
6050+
<field name="field_char" widget="arrow_steps" colspan="4" nolabel="1" attrs="{'autorefresh':[('state', '=', 'running')]}" />
6051+
</form>`;
6052+
6053+
const form = new Form(fields);
6054+
form.parse(xmlViewForm, {
6055+
values: {
6056+
field_char: "test",
6057+
state: "running",
6058+
},
6059+
});
6060+
6061+
const field_char = form.findById("field_char") as Field;
6062+
expect(field_char).toBeDefined();
6063+
expect(field_char?.autoRefresh).toBeTruthy();
6064+
expect(field_char?.readOnly).toBeTruthy();
6065+
expect(form.autorefreshableFields.length).toBe(1);
6066+
expect(form.autorefreshableFields[0]).toBe("field_char");
6067+
});
60186068
describe("If the field has widget_props", () => {
60196069
it("should merge widget_props from fields definition and xml", () => {
60206070
const fields = {

src/spec/Tree.spec.ts

+19
Original file line numberDiff line numberDiff line change
@@ -377,4 +377,23 @@ describe("A Tree", () => {
377377
const nameWidget = tree.findById("name") as Char;
378378
expect(nameWidget.isFunction).toBeTruthy();
379379
});
380+
it("Should parse autorefreshable fields", () => {
381+
const tree = new Tree({
382+
name: {
383+
required: true,
384+
select: true,
385+
size: 128,
386+
string: "Pot&#232;ncia contractada (kW)",
387+
type: "char",
388+
views: {},
389+
},
390+
});
391+
tree.parse(
392+
`<tree string="Partners" colors="red:type=='updated'"><field name="name" sum="Pot&#232;ncia contractada (kW)" autorefresh="1"/></tree>`,
393+
);
394+
395+
const nameWidget = tree.findById("name") as Char;
396+
expect(nameWidget.autoRefresh).toBeTruthy();
397+
expect(tree._autorefreshableFields.length).toBe(1);
398+
});
380399
});

src/spec/nodeParser.spec.ts

+53
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import { describe, it, expect } from "vitest";
2+
import { parseBoolAttribute } from "../helpers/nodeParser";
3+
4+
describe("parseBoolAttribute", () => {
5+
it("returns true for numeric 1", () => {
6+
expect(parseBoolAttribute(1)).toBe(true);
7+
});
8+
9+
it('returns true for string "1"', () => {
10+
expect(parseBoolAttribute("1")).toBe(true);
11+
});
12+
13+
it("returns true for boolean true", () => {
14+
expect(parseBoolAttribute(true)).toBe(true);
15+
});
16+
17+
it('returns true for string "True"', () => {
18+
expect(parseBoolAttribute("True")).toBe(true);
19+
});
20+
21+
it('returns true for string "true"', () => {
22+
expect(parseBoolAttribute("true")).toBe(true);
23+
});
24+
25+
it("returns false for numeric 0", () => {
26+
expect(parseBoolAttribute(0)).toBe(false);
27+
});
28+
29+
it('returns false for string "0"', () => {
30+
expect(parseBoolAttribute("0")).toBe(false);
31+
});
32+
33+
it("returns false for boolean false", () => {
34+
expect(parseBoolAttribute(false)).toBe(false);
35+
});
36+
37+
it('returns false for string "False"', () => {
38+
expect(parseBoolAttribute("False")).toBe(false);
39+
});
40+
41+
it("returns false for null", () => {
42+
expect(parseBoolAttribute(null)).toBe(false);
43+
});
44+
45+
it("returns false for undefined", () => {
46+
expect(parseBoolAttribute(undefined)).toBe(false);
47+
});
48+
49+
it("returns false for non-boolean strings", () => {
50+
expect(parseBoolAttribute("yes")).toBe(false);
51+
expect(parseBoolAttribute("no")).toBe(false);
52+
});
53+
});

0 commit comments

Comments
 (0)