@open-mercato/ui 0.6.5-develop.4695.1.42ee0ddf0e → 0.6.5-develop.4703.1.59a086a9ee
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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@open-mercato/ui",
|
|
3
|
-
"version": "0.6.5-develop.
|
|
3
|
+
"version": "0.6.5-develop.4703.1.59a086a9ee",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -149,13 +149,13 @@
|
|
|
149
149
|
"remark-gfm": "^4.0.1"
|
|
150
150
|
},
|
|
151
151
|
"peerDependencies": {
|
|
152
|
-
"@open-mercato/shared": "0.6.5-develop.
|
|
152
|
+
"@open-mercato/shared": "0.6.5-develop.4703.1.59a086a9ee",
|
|
153
153
|
"react": ">=18.0.0",
|
|
154
154
|
"react-dom": ">=18.0.0",
|
|
155
155
|
"react-is": ">=18.0.0"
|
|
156
156
|
},
|
|
157
157
|
"devDependencies": {
|
|
158
|
-
"@open-mercato/shared": "0.6.5-develop.
|
|
158
|
+
"@open-mercato/shared": "0.6.5-develop.4703.1.59a086a9ee",
|
|
159
159
|
"@testing-library/dom": "^10.4.1",
|
|
160
160
|
"@testing-library/jest-dom": "^6.9.1",
|
|
161
161
|
"@testing-library/react": "^16.3.1",
|
package/src/backend/CrudForm.tsx
CHANGED
|
@@ -2211,12 +2211,22 @@ export function CrudForm<TValues extends Record<string, unknown>>({
|
|
|
2211
2211
|
|
|
2212
2212
|
const setValue = React.useCallback((id: string, nextValue: unknown) => {
|
|
2213
2213
|
let nextData: CrudFormValues<TValues> | null = null
|
|
2214
|
+
let nextDirty: boolean | null = null
|
|
2214
2215
|
setValues((prev) => {
|
|
2215
2216
|
if (Object.is(prev[id], nextValue)) return prev
|
|
2216
2217
|
nextData = { ...prev, [id]: nextValue } as CrudFormValues<TValues>
|
|
2217
2218
|
valuesRef.current = nextData
|
|
2219
|
+
if (!(embedded && !trackDirtyWhenEmbedded)) {
|
|
2220
|
+
const baseline = dirtyBaselineSnapshotRef.current ?? createDirtySnapshot(prev as Record<string, unknown>)
|
|
2221
|
+
dirtyBaselineSnapshotRef.current = baseline
|
|
2222
|
+
nextDirty = createDirtySnapshot(nextData as Record<string, unknown>) !== baseline
|
|
2223
|
+
}
|
|
2218
2224
|
return nextData
|
|
2219
2225
|
})
|
|
2226
|
+
if (nextDirty !== null) {
|
|
2227
|
+
isDirtyRef.current = nextDirty
|
|
2228
|
+
setHasUnsavedChanges(nextDirty)
|
|
2229
|
+
}
|
|
2220
2230
|
const clearedMessages: string[] = []
|
|
2221
2231
|
setErrors((prev) => {
|
|
2222
2232
|
if (!Object.keys(prev).length) return prev
|
|
@@ -2281,7 +2291,7 @@ export function CrudForm<TValues extends Record<string, unknown>>({
|
|
|
2281
2291
|
}).catch((err) => {
|
|
2282
2292
|
console.error('[CrudForm] Error in onFieldChange:', err)
|
|
2283
2293
|
})
|
|
2284
|
-
}, [extendedInjectionEventsEnabled, flash, t, translateValidationMessage, triggerInjectionEvent])
|
|
2294
|
+
}, [embedded, extendedInjectionEventsEnabled, flash, t, trackDirtyWhenEmbedded, translateValidationMessage, triggerInjectionEvent])
|
|
2285
2295
|
|
|
2286
2296
|
const onBlurRequest = React.useCallback((fieldId: string) => {
|
|
2287
2297
|
void validateFieldOnBlur(fieldId)
|
|
@@ -62,6 +62,32 @@ describe('CrudForm unsaved navigation guard', () => {
|
|
|
62
62
|
expect(event.returnValue).toBe('')
|
|
63
63
|
})
|
|
64
64
|
|
|
65
|
+
it('marks the form dirty on the first edit even when no baseline has been initialized yet', async () => {
|
|
66
|
+
const onDirtyChange = jest.fn()
|
|
67
|
+
const { container } = renderWithProviders(
|
|
68
|
+
<CrudForm title="Form" fields={fields} onSubmit={() => {}} onDirtyChange={onDirtyChange} />,
|
|
69
|
+
{
|
|
70
|
+
dict: {
|
|
71
|
+
'ui.forms.actions.save': 'Save',
|
|
72
|
+
'ui.forms.confirmUnsavedChanges': 'Unsaved changes',
|
|
73
|
+
},
|
|
74
|
+
},
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
const input = container.querySelector('[data-crud-field-id="name"] input[type="text"]') as HTMLInputElement
|
|
78
|
+
await act(async () => {
|
|
79
|
+
fireEvent.change(input, { target: { value: 'Alice updated' } })
|
|
80
|
+
})
|
|
81
|
+
|
|
82
|
+
expect(onDirtyChange).toHaveBeenLastCalledWith(true)
|
|
83
|
+
|
|
84
|
+
const event = new Event('beforeunload', { cancelable: true }) as BeforeUnloadEvent
|
|
85
|
+
Object.defineProperty(event, 'returnValue', { writable: true, value: undefined })
|
|
86
|
+
window.dispatchEvent(event)
|
|
87
|
+
|
|
88
|
+
expect(event.defaultPrevented).toBe(true)
|
|
89
|
+
})
|
|
90
|
+
|
|
65
91
|
it('blocks router-driven history pushes until the user confirms leaving', async () => {
|
|
66
92
|
confirmDialogMock.mockResolvedValueOnce(false)
|
|
67
93
|
|