@asteby/metacore-runtime-react 18.28.3 → 19.0.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.
- package/CHANGELOG.md +9 -0
- package/dist/dynamic-kanban.d.ts +66 -0
- package/dist/dynamic-kanban.d.ts.map +1 -0
- package/dist/dynamic-kanban.js +341 -0
- package/dist/dynamic-view.d.ts +18 -0
- package/dist/dynamic-view.d.ts.map +1 -0
- package/dist/dynamic-view.js +75 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/types.d.ts +46 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +4 -1
- package/src/__tests__/dynamic-kanban.test.tsx +268 -0
- package/src/dynamic-kanban.tsx +767 -0
- package/src/dynamic-view.tsx +99 -0
- package/src/index.ts +15 -0
- package/src/types.ts +48 -0
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,aAAa;IAC1B,KAAK,EAAE,MAAM,CAAA;IACb,QAAQ,EAAE,MAAM,CAAA;IAChB,OAAO,EAAE,gBAAgB,EAAE,CAAA;IAC3B,OAAO,EAAE,gBAAgB,EAAE,CAAA;IAC3B,OAAO,CAAC,EAAE,gBAAgB,EAAE,CAAA;IAC5B,cAAc,EAAE,MAAM,EAAE,CAAA;IACxB,cAAc,EAAE,MAAM,CAAA;IACtB,iBAAiB,EAAE,MAAM,CAAA;IACzB,iBAAiB,EAAE,OAAO,CAAA;IAC1B,UAAU,EAAE,OAAO,CAAA;IACnB,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB;;;;;OAKG;IACH,SAAS,CAAC,EAAE,YAAY,EAAE,CAAA;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@asteby/metacore-runtime-react",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "19.0.0",
|
|
4
4
|
"description": "React runtime for metacore hosts — renders addon contributions dynamically",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -17,6 +17,9 @@
|
|
|
17
17
|
}
|
|
18
18
|
},
|
|
19
19
|
"dependencies": {
|
|
20
|
+
"@dnd-kit/core": "^6.3.0",
|
|
21
|
+
"@dnd-kit/sortable": "^10.0.0",
|
|
22
|
+
"@dnd-kit/utilities": "^3.2.2",
|
|
20
23
|
"@module-federation/runtime": "^2.5.0",
|
|
21
24
|
"recharts": "^2.15.0",
|
|
22
25
|
"zod": "^4.3.0"
|
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
// @vitest-environment happy-dom
|
|
2
|
+
//
|
|
3
|
+
// DynamicKanban coverage:
|
|
4
|
+
// 1. Pure board logic (no React): deriveStages (model-level + derived from the
|
|
5
|
+
// group_by column options), groupByStage (4 lanes / 10 cards + unassigned),
|
|
6
|
+
// isTransitionAllowed (wildcard + linear machine), applyOptimisticMove (the
|
|
7
|
+
// immediate local move that backs the optimistic drag), selectCardColumns.
|
|
8
|
+
// 2. Render smoke: the board paints all 4 lanes with the right card counts and
|
|
9
|
+
// a card's title + secondary field surface through the shared renderer.
|
|
10
|
+
// 3. Optimistic move + PUT contract: applying a valid move mutates local state
|
|
11
|
+
// and the destination payload is `{ <group_by>: <dest> }`; an invalid
|
|
12
|
+
// transition is rejected before any PUT.
|
|
13
|
+
import { afterEach, describe, expect, it, vi } from 'vitest'
|
|
14
|
+
import { cleanup, render, screen } from '@testing-library/react'
|
|
15
|
+
|
|
16
|
+
// react-i18next: identity translator returning the defaultValue so card field
|
|
17
|
+
// labels and lane labels surface verbatim.
|
|
18
|
+
vi.mock('react-i18next', () => ({
|
|
19
|
+
useTranslation: () => ({
|
|
20
|
+
t: (_k: string, opts?: { defaultValue?: string }) => opts?.defaultValue ?? _k,
|
|
21
|
+
i18n: { language: 'es' },
|
|
22
|
+
}),
|
|
23
|
+
}))
|
|
24
|
+
|
|
25
|
+
import {
|
|
26
|
+
deriveStages,
|
|
27
|
+
groupByStage,
|
|
28
|
+
isTransitionAllowed,
|
|
29
|
+
applyOptimisticMove,
|
|
30
|
+
selectCardColumns,
|
|
31
|
+
UNASSIGNED_LANE,
|
|
32
|
+
DynamicKanban,
|
|
33
|
+
} from '../dynamic-kanban'
|
|
34
|
+
import { ApiProvider, type ApiClient } from '../api-context'
|
|
35
|
+
import { useMetadataCache } from '../metadata-cache'
|
|
36
|
+
import type { TableMetadata, StageTransition } from '../types'
|
|
37
|
+
|
|
38
|
+
afterEach(cleanup)
|
|
39
|
+
|
|
40
|
+
// ---------------------------------------------------------------------------
|
|
41
|
+
// Fixtures: 4 stages, a linear transition machine, 10 issue cards
|
|
42
|
+
// ---------------------------------------------------------------------------
|
|
43
|
+
|
|
44
|
+
const STAGES = [
|
|
45
|
+
{ key: 'backlog', label: 'Backlog', color: 'slate', order: 0 },
|
|
46
|
+
{ key: 'in_progress', label: 'In Progress', color: 'blue', order: 1 },
|
|
47
|
+
{ key: 'review', label: 'Review', color: 'amber', order: 2 },
|
|
48
|
+
{ key: 'done', label: 'Done', color: 'green', order: 3, is_final: true },
|
|
49
|
+
]
|
|
50
|
+
|
|
51
|
+
const TRANSITIONS: StageTransition[] = [
|
|
52
|
+
{ from: 'backlog', to: 'in_progress' },
|
|
53
|
+
{ from: 'in_progress', to: 'review' },
|
|
54
|
+
{ from: 'review', to: 'done' },
|
|
55
|
+
{ from: 'review', to: 'in_progress' },
|
|
56
|
+
]
|
|
57
|
+
|
|
58
|
+
function meta(over: Partial<TableMetadata> = {}): TableMetadata {
|
|
59
|
+
return {
|
|
60
|
+
title: 'Issues',
|
|
61
|
+
endpoint: '/data/issue',
|
|
62
|
+
view_type: 'kanban',
|
|
63
|
+
group_by: 'stage',
|
|
64
|
+
stages: STAGES,
|
|
65
|
+
transitions: TRANSITIONS,
|
|
66
|
+
columns: [
|
|
67
|
+
{ key: 'title', label: 'Title', type: 'text', sortable: true, filterable: false, searchable: true },
|
|
68
|
+
{ key: 'assignee', label: 'Assignee', type: 'text', sortable: false, filterable: false },
|
|
69
|
+
{ key: 'priority', label: 'Priority', type: 'text', sortable: false, filterable: false },
|
|
70
|
+
{
|
|
71
|
+
key: 'stage',
|
|
72
|
+
label: 'Stage',
|
|
73
|
+
type: 'status',
|
|
74
|
+
sortable: false,
|
|
75
|
+
filterable: true,
|
|
76
|
+
options: STAGES.map((s) => ({ value: s.key, label: s.label, color: s.color })),
|
|
77
|
+
},
|
|
78
|
+
],
|
|
79
|
+
actions: [],
|
|
80
|
+
perPageOptions: [50],
|
|
81
|
+
defaultPerPage: 50,
|
|
82
|
+
searchPlaceholder: 'Buscar...',
|
|
83
|
+
enableCRUDActions: true,
|
|
84
|
+
hasActions: false,
|
|
85
|
+
...over,
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// 10 cards: 3 backlog, 3 in_progress, 2 review, 2 done
|
|
90
|
+
const CARDS = [
|
|
91
|
+
{ id: 1, title: 'Fix login bug', assignee: 'ana', priority: 'high', stage: 'backlog' },
|
|
92
|
+
{ id: 2, title: 'Dark mode', assignee: 'beto', priority: 'low', stage: 'backlog' },
|
|
93
|
+
{ id: 3, title: 'Onboarding flow', assignee: 'cris', priority: 'mid', stage: 'backlog' },
|
|
94
|
+
{ id: 4, title: 'Refactor api', assignee: 'ana', priority: 'mid', stage: 'in_progress' },
|
|
95
|
+
{ id: 5, title: 'Add webhooks', assignee: 'dani', priority: 'high', stage: 'in_progress' },
|
|
96
|
+
{ id: 6, title: 'Rate limiting', assignee: 'eva', priority: 'low', stage: 'in_progress' },
|
|
97
|
+
{ id: 7, title: 'E2E tests', assignee: 'beto', priority: 'mid', stage: 'review' },
|
|
98
|
+
{ id: 8, title: 'Docs pass', assignee: 'cris', priority: 'low', stage: 'review' },
|
|
99
|
+
{ id: 9, title: 'Ship v1', assignee: 'dani', priority: 'high', stage: 'done' },
|
|
100
|
+
{ id: 10, title: 'Retro notes', assignee: 'eva', priority: 'low', stage: 'done' },
|
|
101
|
+
]
|
|
102
|
+
|
|
103
|
+
// ---------------------------------------------------------------------------
|
|
104
|
+
// 1. Pure board logic
|
|
105
|
+
// ---------------------------------------------------------------------------
|
|
106
|
+
|
|
107
|
+
describe('deriveStages', () => {
|
|
108
|
+
it('prefers model-level stages sorted by order', () => {
|
|
109
|
+
const s = deriveStages(meta())
|
|
110
|
+
expect(s.map((x) => x.key)).toEqual(['backlog', 'in_progress', 'review', 'done'])
|
|
111
|
+
})
|
|
112
|
+
|
|
113
|
+
it('falls back to the group_by column options when no model stages', () => {
|
|
114
|
+
const m = meta({ stages: undefined })
|
|
115
|
+
const s = deriveStages(m)
|
|
116
|
+
expect(s.map((x) => x.key)).toEqual(['backlog', 'in_progress', 'review', 'done'])
|
|
117
|
+
expect(s[2]).toMatchObject({ key: 'review', label: 'Review', color: 'amber' })
|
|
118
|
+
})
|
|
119
|
+
|
|
120
|
+
it('returns [] when neither stages nor group_by options exist', () => {
|
|
121
|
+
expect(deriveStages(meta({ stages: undefined, group_by: undefined }))).toEqual([])
|
|
122
|
+
})
|
|
123
|
+
})
|
|
124
|
+
|
|
125
|
+
describe('groupByStage', () => {
|
|
126
|
+
it('buckets 10 cards into 4 lanes with the right counts', () => {
|
|
127
|
+
const g = groupByStage(CARDS, 'stage', STAGES)
|
|
128
|
+
expect(g.get('backlog')!.length).toBe(3)
|
|
129
|
+
expect(g.get('in_progress')!.length).toBe(3)
|
|
130
|
+
expect(g.get('review')!.length).toBe(2)
|
|
131
|
+
expect(g.get('done')!.length).toBe(2)
|
|
132
|
+
expect(g.has(UNASSIGNED_LANE)).toBe(false)
|
|
133
|
+
})
|
|
134
|
+
|
|
135
|
+
it('routes rows with an unknown stage into the unassigned lane', () => {
|
|
136
|
+
const g = groupByStage([...CARDS, { id: 11, title: 'orphan', stage: 'wat' }], 'stage', STAGES)
|
|
137
|
+
expect(g.get(UNASSIGNED_LANE)!.map((r) => r.id)).toEqual([11])
|
|
138
|
+
})
|
|
139
|
+
|
|
140
|
+
it('keeps empty lanes present', () => {
|
|
141
|
+
const g = groupByStage([], 'stage', STAGES)
|
|
142
|
+
expect([...g.keys()]).toEqual(['backlog', 'in_progress', 'review', 'done'])
|
|
143
|
+
expect(g.get('done')).toEqual([])
|
|
144
|
+
})
|
|
145
|
+
})
|
|
146
|
+
|
|
147
|
+
describe('isTransitionAllowed', () => {
|
|
148
|
+
it('allows declared transitions and rejects undeclared ones', () => {
|
|
149
|
+
expect(isTransitionAllowed(TRANSITIONS, 'backlog', 'in_progress')).toBe(true)
|
|
150
|
+
expect(isTransitionAllowed(TRANSITIONS, 'review', 'in_progress')).toBe(true)
|
|
151
|
+
expect(isTransitionAllowed(TRANSITIONS, 'backlog', 'done')).toBe(false)
|
|
152
|
+
expect(isTransitionAllowed(TRANSITIONS, 'done', 'backlog')).toBe(false)
|
|
153
|
+
})
|
|
154
|
+
|
|
155
|
+
it('treats same-stage as a no-op allow', () => {
|
|
156
|
+
expect(isTransitionAllowed(TRANSITIONS, 'done', 'done')).toBe(true)
|
|
157
|
+
})
|
|
158
|
+
|
|
159
|
+
it('is unrestricted when no transitions are declared', () => {
|
|
160
|
+
expect(isTransitionAllowed(undefined, 'done', 'backlog')).toBe(true)
|
|
161
|
+
expect(isTransitionAllowed([], 'done', 'backlog')).toBe(true)
|
|
162
|
+
})
|
|
163
|
+
|
|
164
|
+
it('honors wildcards on either side', () => {
|
|
165
|
+
expect(isTransitionAllowed([{ from: '*', to: 'done' }], 'backlog', 'done')).toBe(true)
|
|
166
|
+
expect(isTransitionAllowed([{ from: 'review', to: '*' }], 'review', 'anything')).toBe(true)
|
|
167
|
+
expect(isTransitionAllowed([{ from: '*', to: 'done' }], 'backlog', 'review')).toBe(false)
|
|
168
|
+
})
|
|
169
|
+
})
|
|
170
|
+
|
|
171
|
+
describe('applyOptimisticMove', () => {
|
|
172
|
+
it('moves a card to the destination lane and stamps the new stage', () => {
|
|
173
|
+
const g0 = groupByStage(CARDS, 'stage', STAGES)
|
|
174
|
+
const g1 = applyOptimisticMove(g0, 7, 'review', 'done', 'stage')
|
|
175
|
+
// immutable: original untouched
|
|
176
|
+
expect(g0.get('review')!.length).toBe(2)
|
|
177
|
+
expect(g0.get('done')!.length).toBe(2)
|
|
178
|
+
// moved
|
|
179
|
+
expect(g1.get('review')!.length).toBe(1)
|
|
180
|
+
expect(g1.get('done')!.length).toBe(3)
|
|
181
|
+
const moved = g1.get('done')!.find((r) => r.id === 7)
|
|
182
|
+
expect(moved.stage).toBe('done')
|
|
183
|
+
})
|
|
184
|
+
|
|
185
|
+
it('is a no-op when the card is not in the source lane', () => {
|
|
186
|
+
const g0 = groupByStage(CARDS, 'stage', STAGES)
|
|
187
|
+
const g1 = applyOptimisticMove(g0, 999, 'review', 'done', 'stage')
|
|
188
|
+
expect(g1.get('done')!.length).toBe(2)
|
|
189
|
+
})
|
|
190
|
+
})
|
|
191
|
+
|
|
192
|
+
describe('selectCardColumns', () => {
|
|
193
|
+
it('picks the searchable column as title and excludes the group_by column', () => {
|
|
194
|
+
const { title, fields } = selectCardColumns(meta())
|
|
195
|
+
expect(title?.key).toBe('title')
|
|
196
|
+
expect(fields.map((f) => f.key)).not.toContain('stage')
|
|
197
|
+
expect(fields.map((f) => f.key)).toEqual(['assignee', 'priority'])
|
|
198
|
+
})
|
|
199
|
+
})
|
|
200
|
+
|
|
201
|
+
// ---------------------------------------------------------------------------
|
|
202
|
+
// 2. Render smoke + 3. optimistic PUT contract
|
|
203
|
+
// ---------------------------------------------------------------------------
|
|
204
|
+
|
|
205
|
+
function fakeApi(over: Partial<ApiClient> = {}): ApiClient {
|
|
206
|
+
const ok = (data: unknown) => ({ data: { success: true, data } })
|
|
207
|
+
return {
|
|
208
|
+
get: vi.fn(async (url: string) => {
|
|
209
|
+
if (url.startsWith('/metadata/table/')) return ok(meta())
|
|
210
|
+
return ok(CARDS)
|
|
211
|
+
}),
|
|
212
|
+
post: vi.fn(async () => ok(null)),
|
|
213
|
+
put: vi.fn(async () => ok(null)),
|
|
214
|
+
delete: vi.fn(async () => ok(null)),
|
|
215
|
+
...over,
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
describe('DynamicKanban render', () => {
|
|
220
|
+
it('paints all 4 lanes with their card counts and a card title', async () => {
|
|
221
|
+
useMetadataCache.getState().setMetadata('issue', meta())
|
|
222
|
+
render(
|
|
223
|
+
<ApiProvider client={fakeApi()}>
|
|
224
|
+
<DynamicKanban model="issue" />
|
|
225
|
+
</ApiProvider>,
|
|
226
|
+
)
|
|
227
|
+
// lane labels
|
|
228
|
+
expect(await screen.findByText('Backlog')).toBeTruthy()
|
|
229
|
+
expect(screen.getByText('In Progress')).toBeTruthy()
|
|
230
|
+
expect(screen.getByText('Review')).toBeTruthy()
|
|
231
|
+
expect(screen.getByText('Done')).toBeTruthy()
|
|
232
|
+
// a card title surfaces through the shared renderer
|
|
233
|
+
expect(await screen.findByText('Fix login bug')).toBeTruthy()
|
|
234
|
+
// a secondary field label is present
|
|
235
|
+
expect(screen.getAllByText('Assignee:').length).toBeGreaterThan(0)
|
|
236
|
+
})
|
|
237
|
+
})
|
|
238
|
+
|
|
239
|
+
// The drag itself is wired by dnd-kit (pointer + layout), which happy-dom can't
|
|
240
|
+
// faithfully simulate. We exercise the exact decision + state path the
|
|
241
|
+
// onDragEnd handler runs: gate by isTransitionAllowed, then applyOptimisticMove
|
|
242
|
+
// + PUT { group_by: dest }. This is the behavior that resolves the
|
|
243
|
+
// "refetch loses scroll" gap.
|
|
244
|
+
describe('optimistic drag contract', () => {
|
|
245
|
+
it('a valid move stamps dest stage and the PUT body carries { stage: dest }', async () => {
|
|
246
|
+
const put = vi.fn(async () => ({ data: { success: true, data: null } }))
|
|
247
|
+
const api = fakeApi({ put })
|
|
248
|
+
|
|
249
|
+
// simulate the handler's core for card 7: review -> done (declared)
|
|
250
|
+
const from = 'review'
|
|
251
|
+
const to = 'done'
|
|
252
|
+
expect(isTransitionAllowed(TRANSITIONS, from, to)).toBe(true)
|
|
253
|
+
|
|
254
|
+
const g0 = groupByStage(CARDS, 'stage', STAGES)
|
|
255
|
+
const g1 = applyOptimisticMove(g0, 7, from, to, 'stage')
|
|
256
|
+
expect(g1.get('done')!.find((r) => r.id === 7)?.stage).toBe('done')
|
|
257
|
+
|
|
258
|
+
await api.put('/data/issue/me/7', { stage: to })
|
|
259
|
+
expect(put).toHaveBeenCalledWith('/data/issue/me/7', { stage: 'done' })
|
|
260
|
+
})
|
|
261
|
+
|
|
262
|
+
it('an undeclared move is rejected before any PUT', () => {
|
|
263
|
+
const from = 'backlog'
|
|
264
|
+
const to = 'done'
|
|
265
|
+
expect(isTransitionAllowed(TRANSITIONS, from, to)).toBe(false)
|
|
266
|
+
// handler returns early — no applyOptimisticMove, no PUT.
|
|
267
|
+
})
|
|
268
|
+
})
|