@adcops/autocore-react 3.5.5 → 3.5.9

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/components/ams/AssetRegistryTable.d.ts.map +1 -1
  2. package/dist/components/ams/AssetRegistryTable.js +1 -1
  3. package/dist/components/forms/useEditDraft.d.ts +49 -0
  4. package/dist/components/forms/useEditDraft.d.ts.map +1 -0
  5. package/dist/components/forms/useEditDraft.js +1 -0
  6. package/dist/components/index.d.ts +2 -0
  7. package/dist/components/index.d.ts.map +1 -1
  8. package/dist/components/index.js +1 -1
  9. package/dist/components/tis/TestDataView.d.ts +8 -0
  10. package/dist/components/tis/TestDataView.d.ts.map +1 -1
  11. package/dist/components/tis/TestDataView.js +1 -1
  12. package/dist/components/tis/TestRawDataView.d.ts.map +1 -1
  13. package/dist/components/tis/TestRawDataView.js +1 -1
  14. package/dist/components/tis/chartViews.d.ts +57 -0
  15. package/dist/components/tis/chartViews.d.ts.map +1 -0
  16. package/dist/components/tis/chartViews.js +1 -0
  17. package/dist/components/tis-editor/editor/ChartViewDialog.d.ts.map +1 -1
  18. package/dist/components/tis-editor/editor/ChartViewDialog.js +1 -1
  19. package/dist/components/tis-editor/editor/TestFieldDialog.d.ts.map +1 -1
  20. package/dist/components/tis-editor/editor/TestFieldDialog.js +1 -1
  21. package/dist/components/tis-editor/editor/ViewsEditor.d.ts.map +1 -1
  22. package/dist/components/tis-editor/editor/ViewsEditor.js +1 -1
  23. package/dist/components/tis-editor/types.d.ts +11 -0
  24. package/dist/components/tis-editor/types.d.ts.map +1 -1
  25. package/package.json +3 -1
  26. package/src/components/ams/AssetRegistryTable.tsx +40 -9
  27. package/src/components/forms/useEditDraft.ts +0 -0
  28. package/src/components/index.ts +3 -0
  29. package/src/components/tis/TestDataView.tsx +16 -10
  30. package/src/components/tis/TestRawDataView.tsx +6 -7
  31. package/src/components/tis/chartViews.ts +104 -0
  32. package/src/components/tis-editor/editor/ChartViewDialog.tsx +24 -10
  33. package/src/components/tis-editor/editor/TestFieldDialog.tsx +54 -13
  34. package/src/components/tis-editor/editor/ViewsEditor.tsx +31 -5
  35. package/src/components/tis-editor/types.ts +11 -0
  36. package/tools/tests/dist/tis-editor.entry.css +90 -0
  37. package/tools/tests/dist/tis-editor.entry.js +41431 -0
  38. package/tools/tests/tis-editor.entry.tsx +46 -0
  39. package/tools/tests/tis-editor.test.mjs +133 -0
  40. package/tools/tests/vite.config.mjs +30 -0
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Fixture for `tools/tests/tis-editor.test.mjs`.
3
+ *
4
+ * Mounts the two TIS-editor sections whose dialogs seed a draft from a record
5
+ * — the chart-view list and a field array — under a parent that can be made
6
+ * to re-render on demand. That re-render is the whole point: on a machine the
7
+ * editor sits under providers that re-render with live tag traffic, and the
8
+ * dialogs used to throw away the operator's typing every time one arrived.
9
+ */
10
+
11
+ import * as React from 'react';
12
+ import { useState } from 'react';
13
+ import { createRoot } from 'react-dom/client';
14
+ import { ViewsEditor } from '../../src/components/tis-editor/editor/ViewsEditor';
15
+ import { orderedViews } from '../../src/components/tis/chartViews';
16
+ import { FieldArrayEditor } from '../../src/components/tis-editor/editor/FieldArrayEditor';
17
+
18
+ const METHOD: any = {
19
+ label: 'golf', cycle_fields: [{ name: 'peak_load' }, { name: 'deflection' }],
20
+ views: {
21
+ load_vs_deflection: { title: 'Peak Load vs Deflection (per cycle)', type: 'cycle_scatter', x: { field: 'deflection', label: 'Deflection' }, y: [{ field: 'peak_load', label: 'Peak Load', y_axis: 'left' }] },
22
+ peak_load_by_cycle: { title: 'Peak Load by Cycle', type: 'cycle_scatter', x: { field: 'cycle_index', label: 'Cycle' }, y: [{ field: 'peak_load', label: 'Peak Load', y_axis: 'left' }] },
23
+ },
24
+ };
25
+
26
+ let forceParentRender: () => void = () => {};
27
+ let current: any = METHOD;
28
+
29
+ const Harness: React.FC = () => {
30
+ const [, setTick] = useState(0);
31
+ const [method, setMethod] = useState<any>(METHOD);
32
+ forceParentRender = () => setTick(t => t + 1);
33
+ current = method;
34
+ const onChange = (m: any) => { current = m; setMethod(m); };
35
+ return (
36
+ <>
37
+ <ViewsEditor method={method} onChange={onChange} />
38
+ <FieldArrayEditor arrayKey="cycle_fields" method={method} onChange={onChange} />
39
+ </>
40
+ );
41
+ };
42
+
43
+ (window as any).__mount = () => createRoot(document.getElementById('root')!).render(<Harness />);
44
+ (window as any).__forceParentRender = () => forceParentRender();
45
+ (window as any).__method = () => current;
46
+ (window as any).__order = () => orderedViews(current.views).map((v: any) => v.name);
@@ -0,0 +1,133 @@
1
+ /**
2
+ * TIS-editor regression test — run with `npm run test:tis-editor`.
3
+ *
4
+ * Covers two things that have no other guard:
5
+ *
6
+ * 1. An open dialog keeps the operator's edits across re-renders of the tree
7
+ * above it. The editors live under `AutoCoreTagProvider`/`TisProvider`,
8
+ * whose consumers re-render on live tag and broadcast traffic; a draft
9
+ * reseeded on prop identity is wiped a frame after every keystroke, which
10
+ * is what made the chart-view dialog read-only on a running machine
11
+ * (gen4_ac, 2026-08-29).
12
+ * 2. Chart views display in the author's `order`, and Move up / Move down
13
+ * renumbers it — the server hands `views` back id-sorted, so the order is
14
+ * carried per view rather than implied by the map.
15
+ *
16
+ * No test framework: jsdom + `react`'s `act`, driving the real components.
17
+ */
18
+
19
+ import { JSDOM } from 'jsdom';
20
+
21
+ const dom = new JSDOM('<!doctype html><html><body><div id="root"></div></body></html>', {
22
+ url: 'http://localhost/', pretendToBeVisual: true,
23
+ });
24
+ global.window = dom.window;
25
+ global.document = dom.window.document;
26
+ Object.defineProperty(global, "navigator", { value: dom.window.navigator, configurable: true });
27
+ global.HTMLElement = dom.window.HTMLElement;
28
+ global.Element = dom.window.Element;
29
+ global.Node = dom.window.Node;
30
+ global.Event = dom.window.Event;
31
+ global.KeyboardEvent = dom.window.KeyboardEvent;
32
+ global.MouseEvent = dom.window.MouseEvent;
33
+ global.getComputedStyle = dom.window.getComputedStyle;
34
+ global.requestAnimationFrame = (cb) => setTimeout(() => cb(Date.now()), 0);
35
+ global.cancelAnimationFrame = (id) => clearTimeout(id);
36
+ global.IS_REACT_ACT_ENVIRONMENT = true;
37
+ dom.window.IS_REACT_ACT_ENVIRONMENT = true;
38
+ global.matchMedia = dom.window.matchMedia ?? (() => ({ matches: false, addListener(){}, removeListener(){}, addEventListener(){}, removeEventListener(){} }));
39
+ dom.window.confirm = () => true;
40
+
41
+ await import('./dist/tis-editor.entry.js');
42
+ const { act } = await import('react');
43
+
44
+ const tick = async () => { await act(async () => { await new Promise(r => setTimeout(r, 20)); }); };
45
+
46
+ await act(async () => { window.__mount(); });
47
+ await tick();
48
+
49
+ const q = (sel) => [...document.querySelectorAll(sel)];
50
+ const rowIds = () => [...q('tbody')[0].querySelectorAll('tr')].map(tr => tr.querySelector('td')?.textContent);
51
+ const rowButtons = (row) => [...q('tbody')[0].querySelectorAll('tr')[row].querySelectorAll('button')];
52
+ const byLabel = (row, label) => rowButtons(row).find(b => b.getAttribute('aria-label') === label);
53
+ const dialog = () => q('.p-dialog').at(-1);
54
+ const inputs = () => [...dialog().querySelectorAll('input')];
55
+ const setValue = (el, v) => {
56
+ const desc = Object.getOwnPropertyDescriptor(Object.getPrototypeOf(el), 'value');
57
+ desc.set.call(el, v);
58
+ el.dispatchEvent(new dom.window.Event('input', { bubbles: true }));
59
+ };
60
+ const check = (name, got, want) => {
61
+ const ok = JSON.stringify(got) === JSON.stringify(want);
62
+ console.log(`${ok ? 'PASS' : 'FAIL'} ${name}: ${JSON.stringify(got)}${ok ? '' : ' want ' + JSON.stringify(want)}`);
63
+ if (!ok) process.exitCode = 1;
64
+ };
65
+
66
+ check('initial row order (id order, no `order` keys)', rowIds(), ['load_vs_deflection', 'peak_load_by_cycle']);
67
+
68
+ // ---- 1. edit an existing view, then force a parent re-render mid-edit
69
+ await act(async () => { byLabel(1, 'Edit').click(); });
70
+ await tick();
71
+ const titleInput = inputs()[1];
72
+ check('dialog seeded from the edited view', titleInput.value, 'Peak Load by Cycle');
73
+
74
+ await act(async () => { setValue(titleInput, 'Deflection by Cycle'); });
75
+ await tick();
76
+ check('typing lands', inputs()[1].value, 'Deflection by Cycle');
77
+
78
+ for (let i = 0; i < 5; i++) { await act(async () => { window.__forceParentRender(); }); await tick(); }
79
+ check('survives 5 parent re-renders', inputs()[1].value, 'Deflection by Cycle');
80
+
81
+ // also edit the Y series field, then re-render again
82
+ const yField = inputs().find(el => el.value === 'peak_load' && el.placeholder === 'field');
83
+ await act(async () => { setValue(yField, 'deflection'); });
84
+ await tick();
85
+ await act(async () => { window.__forceParentRender(); });
86
+ await tick();
87
+ check('series edit survives too', inputs().find(el => el.placeholder === 'field').value, 'deflection');
88
+
89
+ // Save
90
+ await act(async () => { [...dialog().querySelectorAll('button')].find(b => b.textContent.includes('Save')).click(); });
91
+ await tick();
92
+ const saved = window.__method().views.peak_load_by_cycle;
93
+ check('saved title', saved.title, 'Deflection by Cycle');
94
+ check('saved series field', saved.y[0].field, 'deflection');
95
+
96
+ // ---- 2. reorder
97
+ await act(async () => { byLabel(1, 'Move up').click(); });
98
+ await tick();
99
+ check('row order after Move up', rowIds(), ['peak_load_by_cycle', 'load_vs_deflection']);
100
+ check('order keys are dense', Object.fromEntries(Object.entries(window.__method().views).map(([k, v]) => [k, v.order])), { peak_load_by_cycle: 0, load_vs_deflection: 1 });
101
+ check('runtime picker agrees', window.__order(), ['peak_load_by_cycle', 'load_vs_deflection']);
102
+
103
+ // Move-up on the first row is disabled; move the first row down again
104
+ check('first row Move up disabled', byLabel(0, 'Move up').disabled, true);
105
+ check('last row Move down disabled', byLabel(1, 'Move down').disabled, true);
106
+ await act(async () => { byLabel(0, 'Move down').click(); });
107
+ await tick();
108
+ check('row order after Move down', rowIds(), ['load_vs_deflection', 'peak_load_by_cycle']);
109
+
110
+ // ---- 3. a new view lands last, not first
111
+ await act(async () => { [...document.querySelectorAll('button')].find(b => b.textContent.includes('Add view')).click(); });
112
+ await tick();
113
+ await act(async () => { setValue(inputs()[0], 'aaa_new_view'); });
114
+ await tick();
115
+ await act(async () => { window.__forceParentRender(); });
116
+ await tick();
117
+ check('new-view id survives a re-render', inputs()[0].value, 'aaa_new_view');
118
+ await act(async () => { [...dialog().querySelectorAll('button')].find(b => b.textContent.includes('Save')).click(); });
119
+ await tick();
120
+ check('new view sorts last despite its id', rowIds(), ['load_vs_deflection', 'peak_load_by_cycle', 'aaa_new_view']);
121
+
122
+ // ---- 4. the field dialog (same reseed class) — second DataTable on the page
123
+ const fieldRows = () => [...q('tbody')[1].querySelectorAll('tr')];
124
+ const fieldBtn = (row, label) => [...fieldRows()[row].querySelectorAll('button')].find(b => b.getAttribute('aria-label') === label);
125
+ check('field rows', fieldRows().map(tr => tr.querySelector('td')?.textContent), ['peak_load', 'deflection']);
126
+ await act(async () => { fieldBtn(0, 'Edit').click(); });
127
+ await tick();
128
+ const nameInput = inputs()[0];
129
+ check('field dialog seeded', nameInput.value, 'peak_load');
130
+ await act(async () => { setValue(nameInput, 'peak_load_n'); });
131
+ await tick();
132
+ for (let i = 0; i < 5; i++) { await act(async () => { window.__forceParentRender(); }); await tick(); }
133
+ check('field edit survives parent re-renders', inputs()[0].value, 'peak_load_n');
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Bundles the TIS-editor regression entry for the jsdom run.
3
+ *
4
+ * Points at `src`, not `dist`, so a failure names a real source line, and
5
+ * bundles React/PrimeReact in so the jsdom run has exactly one copy of React.
6
+ */
7
+
8
+ import { defineConfig } from 'vite';
9
+ import { fileURLToPath } from 'node:url';
10
+ import { dirname, resolve } from 'node:path';
11
+
12
+ const here = dirname(fileURLToPath(import.meta.url));
13
+
14
+ export default defineConfig({
15
+ root: here,
16
+ build: {
17
+ outDir: resolve(here, 'dist'),
18
+ emptyOutDir: true,
19
+ lib: {
20
+ entry: resolve(here, 'tis-editor.entry.tsx'),
21
+ formats: ['es'],
22
+ fileName: 'tis-editor.entry',
23
+ },
24
+ minify: false,
25
+ target: 'node20',
26
+ },
27
+ // PrimeReact and React both branch on this; the development build gives
28
+ // the act() warnings that catch an un-awaited state update.
29
+ define: { 'process.env.NODE_ENV': '"development"' },
30
+ });