fix: smooth out schema-version edges
This commit is contained in:
+46
-10
@@ -53,6 +53,11 @@ import {
|
|||||||
restoreAppState,
|
restoreAppState,
|
||||||
restoreElements,
|
restoreElements,
|
||||||
} from "@excalidraw/excalidraw/data/restore";
|
} from "@excalidraw/excalidraw/data/restore";
|
||||||
|
import {
|
||||||
|
migrateElementsBySchema,
|
||||||
|
resolveSchemaVersion,
|
||||||
|
SCHEMA_VERSIONS,
|
||||||
|
} from "@excalidraw/excalidraw/data/schema";
|
||||||
import { newElementWith } from "@excalidraw/element";
|
import { newElementWith } from "@excalidraw/element";
|
||||||
import { isInitializedImageElement } from "@excalidraw/element";
|
import { isInitializedImageElement } from "@excalidraw/element";
|
||||||
import clsx from "clsx";
|
import clsx from "clsx";
|
||||||
@@ -236,10 +241,20 @@ const initializeScene = async (opts: {
|
|||||||
> & {
|
> & {
|
||||||
scrollToContent?: boolean;
|
scrollToContent?: boolean;
|
||||||
} = {
|
} = {
|
||||||
elements: restoreElements(localDataState?.elements, null, {
|
elements: restoreElements(
|
||||||
repairBindings: true,
|
migrateElementsBySchema(
|
||||||
deleteInvisibleElements: true,
|
localDataState?.elements,
|
||||||
}),
|
resolveSchemaVersion(
|
||||||
|
localDataState?.schemaVersion,
|
||||||
|
SCHEMA_VERSIONS.initial,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
null,
|
||||||
|
{
|
||||||
|
repairBindings: true,
|
||||||
|
deleteInvisibleElements: true,
|
||||||
|
},
|
||||||
|
),
|
||||||
appState: restoreAppState(localDataState?.appState, null),
|
appState: restoreAppState(localDataState?.appState, null),
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -262,11 +277,21 @@ const initializeScene = async (opts: {
|
|||||||
|
|
||||||
scene = {
|
scene = {
|
||||||
elements: bumpElementVersions(
|
elements: bumpElementVersions(
|
||||||
restoreElements(imported.elements, null, {
|
restoreElements(
|
||||||
repairBindings: true,
|
migrateElementsBySchema(
|
||||||
deleteInvisibleElements: true,
|
imported.elements,
|
||||||
}),
|
resolveSchemaVersion(
|
||||||
localDataState?.elements,
|
imported.schemaVersion,
|
||||||
|
SCHEMA_VERSIONS.initial,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
null,
|
||||||
|
{
|
||||||
|
repairBindings: true,
|
||||||
|
deleteInvisibleElements: true,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
scene.elements,
|
||||||
),
|
),
|
||||||
appState: restoreAppState(
|
appState: restoreAppState(
|
||||||
imported.appState,
|
imported.appState,
|
||||||
@@ -549,9 +574,20 @@ const ExcalidrawWrapper = () => {
|
|||||||
if (isBrowserStorageStateNewer(STORAGE_KEYS.VERSION_DATA_STATE)) {
|
if (isBrowserStorageStateNewer(STORAGE_KEYS.VERSION_DATA_STATE)) {
|
||||||
const localDataState = importFromLocalStorage();
|
const localDataState = importFromLocalStorage();
|
||||||
const username = importUsernameFromLocalStorage();
|
const username = importUsernameFromLocalStorage();
|
||||||
|
const migratedElements = migrateElementsBySchema(
|
||||||
|
localDataState?.elements,
|
||||||
|
resolveSchemaVersion(
|
||||||
|
localDataState?.schemaVersion,
|
||||||
|
SCHEMA_VERSIONS.initial,
|
||||||
|
),
|
||||||
|
);
|
||||||
setLangCode(getPreferredLanguage());
|
setLangCode(getPreferredLanguage());
|
||||||
excalidrawAPI.updateScene({
|
excalidrawAPI.updateScene({
|
||||||
...localDataState,
|
elements: restoreElements(migratedElements, null, {
|
||||||
|
repairBindings: true,
|
||||||
|
deleteInvisibleElements: true,
|
||||||
|
}),
|
||||||
|
appState: restoreAppState(localDataState?.appState, null),
|
||||||
captureUpdate: CaptureUpdateAction.NEVER,
|
captureUpdate: CaptureUpdateAction.NEVER,
|
||||||
});
|
});
|
||||||
LibraryIndexedDBAdapter.load().then((data) => {
|
LibraryIndexedDBAdapter.load().then((data) => {
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ export const ROOM_ID_BYTES = 10;
|
|||||||
export const STORAGE_KEYS = {
|
export const STORAGE_KEYS = {
|
||||||
LOCAL_STORAGE_ELEMENTS: "excalidraw",
|
LOCAL_STORAGE_ELEMENTS: "excalidraw",
|
||||||
LOCAL_STORAGE_APP_STATE: "excalidraw-state",
|
LOCAL_STORAGE_APP_STATE: "excalidraw-state",
|
||||||
|
LOCAL_STORAGE_SCHEMA_VERSION: "excalidraw-schema-version",
|
||||||
LOCAL_STORAGE_COLLAB: "excalidraw-collab",
|
LOCAL_STORAGE_COLLAB: "excalidraw-collab",
|
||||||
LOCAL_STORAGE_THEME: "excalidraw-theme",
|
LOCAL_STORAGE_THEME: "excalidraw-theme",
|
||||||
LOCAL_STORAGE_DEBUG: "excalidraw-debug",
|
LOCAL_STORAGE_DEBUG: "excalidraw-debug",
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ import {
|
|||||||
|
|
||||||
import { appJotaiStore, atom } from "excalidraw-app/app-jotai";
|
import { appJotaiStore, atom } from "excalidraw-app/app-jotai";
|
||||||
import { getNonDeletedElements } from "@excalidraw/element";
|
import { getNonDeletedElements } from "@excalidraw/element";
|
||||||
|
import { SCHEMA_VERSIONS } from "@excalidraw/excalidraw/data/schema";
|
||||||
|
|
||||||
import type { LibraryPersistedData } from "@excalidraw/excalidraw/data/library";
|
import type { LibraryPersistedData } from "@excalidraw/excalidraw/data/library";
|
||||||
import type { ImportedDataState } from "@excalidraw/excalidraw/data/types";
|
import type { ImportedDataState } from "@excalidraw/excalidraw/data/types";
|
||||||
@@ -94,6 +95,10 @@ const saveDataStateToLocalStorage = (
|
|||||||
STORAGE_KEYS.LOCAL_STORAGE_APP_STATE,
|
STORAGE_KEYS.LOCAL_STORAGE_APP_STATE,
|
||||||
JSON.stringify(_appState),
|
JSON.stringify(_appState),
|
||||||
);
|
);
|
||||||
|
localStorage.setItem(
|
||||||
|
STORAGE_KEYS.LOCAL_STORAGE_SCHEMA_VERSION,
|
||||||
|
`${SCHEMA_VERSIONS.latest}`,
|
||||||
|
);
|
||||||
updateBrowserStateVersion(STORAGE_KEYS.VERSION_DATA_STATE);
|
updateBrowserStateVersion(STORAGE_KEYS.VERSION_DATA_STATE);
|
||||||
if (localStorageQuotaExceeded) {
|
if (localStorageQuotaExceeded) {
|
||||||
appJotaiStore.set(localStorageQuotaExceededAtom, false);
|
appJotaiStore.set(localStorageQuotaExceededAtom, false);
|
||||||
|
|||||||
@@ -196,6 +196,7 @@ const legacy_decodeFromBackend = async ({
|
|||||||
return {
|
return {
|
||||||
elements: data.elements || null,
|
elements: data.elements || null,
|
||||||
appState: data.appState || null,
|
appState: data.appState || null,
|
||||||
|
schemaVersion: data.schemaVersion,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -226,6 +227,7 @@ export const importFromBackend = async (
|
|||||||
return {
|
return {
|
||||||
elements: data.elements || null,
|
elements: data.elements || null,
|
||||||
appState: data.appState || null,
|
appState: data.appState || null,
|
||||||
|
schemaVersion: data.schemaVersion,
|
||||||
};
|
};
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
console.warn(
|
console.warn(
|
||||||
|
|||||||
@@ -2,6 +2,10 @@ import {
|
|||||||
clearAppStateForLocalStorage,
|
clearAppStateForLocalStorage,
|
||||||
getDefaultAppState,
|
getDefaultAppState,
|
||||||
} from "@excalidraw/excalidraw/appState";
|
} from "@excalidraw/excalidraw/appState";
|
||||||
|
import {
|
||||||
|
resolveSchemaVersion,
|
||||||
|
SCHEMA_VERSIONS,
|
||||||
|
} from "@excalidraw/excalidraw/data/schema";
|
||||||
|
|
||||||
import type { ExcalidrawElement } from "@excalidraw/element/types";
|
import type { ExcalidrawElement } from "@excalidraw/element/types";
|
||||||
import type { AppState } from "@excalidraw/excalidraw/types";
|
import type { AppState } from "@excalidraw/excalidraw/types";
|
||||||
@@ -37,10 +41,14 @@ export const importUsernameFromLocalStorage = (): string | null => {
|
|||||||
export const importFromLocalStorage = () => {
|
export const importFromLocalStorage = () => {
|
||||||
let savedElements = null;
|
let savedElements = null;
|
||||||
let savedState = null;
|
let savedState = null;
|
||||||
|
let savedSchemaVersion = null;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
savedElements = localStorage.getItem(STORAGE_KEYS.LOCAL_STORAGE_ELEMENTS);
|
savedElements = localStorage.getItem(STORAGE_KEYS.LOCAL_STORAGE_ELEMENTS);
|
||||||
savedState = localStorage.getItem(STORAGE_KEYS.LOCAL_STORAGE_APP_STATE);
|
savedState = localStorage.getItem(STORAGE_KEYS.LOCAL_STORAGE_APP_STATE);
|
||||||
|
savedSchemaVersion = localStorage.getItem(
|
||||||
|
STORAGE_KEYS.LOCAL_STORAGE_SCHEMA_VERSION,
|
||||||
|
);
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
// Unable to access localStorage
|
// Unable to access localStorage
|
||||||
console.error(error);
|
console.error(error);
|
||||||
@@ -70,7 +78,12 @@ export const importFromLocalStorage = () => {
|
|||||||
// Do nothing because appState is already null
|
// Do nothing because appState is already null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return { elements, appState };
|
const schemaVersion = resolveSchemaVersion(
|
||||||
|
Number(savedSchemaVersion),
|
||||||
|
SCHEMA_VERSIONS.initial,
|
||||||
|
);
|
||||||
|
|
||||||
|
return { elements, appState, schemaVersion };
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getElementsStorageSize = () => {
|
export const getElementsStorageSize = () => {
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import {
|
|||||||
chunk,
|
chunk,
|
||||||
getExportSource,
|
getExportSource,
|
||||||
} from "@excalidraw/common";
|
} from "@excalidraw/common";
|
||||||
|
import { SCHEMA_VERSIONS } from "../data/schema";
|
||||||
|
|
||||||
import { EditorLocalStorage } from "../data/EditorLocalStorage";
|
import { EditorLocalStorage } from "../data/EditorLocalStorage";
|
||||||
import { canvasToBlob, resizeImageFile } from "../data/blob";
|
import { canvasToBlob, resizeImageFile } from "../data/blob";
|
||||||
@@ -280,6 +281,7 @@ const PublishLibrary = ({
|
|||||||
const libContent: ExportedLibraryData = {
|
const libContent: ExportedLibraryData = {
|
||||||
type: EXPORT_DATA_TYPES.excalidrawLibrary,
|
type: EXPORT_DATA_TYPES.excalidrawLibrary,
|
||||||
version: VERSIONS.excalidrawLibrary,
|
version: VERSIONS.excalidrawLibrary,
|
||||||
|
schemaVersion: SCHEMA_VERSIONS.latest,
|
||||||
source: getExportSource(),
|
source: getExportSource(),
|
||||||
libraryItems: clonedLibItems,
|
libraryItems: clonedLibItems,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -235,7 +235,11 @@ export const parseLibraryJSON = (
|
|||||||
throw new Error("Invalid library");
|
throw new Error("Invalid library");
|
||||||
}
|
}
|
||||||
const libraryItems = data.libraryItems || data.library;
|
const libraryItems = data.libraryItems || data.library;
|
||||||
return restoreLibraryItems(libraryItems, defaultStatus);
|
return restoreLibraryItems(
|
||||||
|
libraryItems,
|
||||||
|
defaultStatus,
|
||||||
|
resolveSchemaVersion(data.schemaVersion, SCHEMA_VERSIONS.initial),
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const loadLibraryFromBlob = async (
|
export const loadLibraryFromBlob = async (
|
||||||
|
|||||||
@@ -131,6 +131,7 @@ export const serializeLibraryAsJSON = (libraryItems: LibraryItems) => {
|
|||||||
const data: ExportedLibraryData = {
|
const data: ExportedLibraryData = {
|
||||||
type: EXPORT_DATA_TYPES.excalidrawLibrary,
|
type: EXPORT_DATA_TYPES.excalidrawLibrary,
|
||||||
version: VERSIONS.excalidrawLibrary,
|
version: VERSIONS.excalidrawLibrary,
|
||||||
|
schemaVersion: SCHEMA_VERSIONS.latest,
|
||||||
source: getExportSource(),
|
source: getExportSource(),
|
||||||
libraryItems,
|
libraryItems,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ import { t } from "../i18n";
|
|||||||
|
|
||||||
import { loadLibraryFromBlob } from "./blob";
|
import { loadLibraryFromBlob } from "./blob";
|
||||||
import { restoreLibraryItems } from "./restore";
|
import { restoreLibraryItems } from "./restore";
|
||||||
|
import { resolveSchemaVersion, SCHEMA_VERSIONS } from "./schema";
|
||||||
|
|
||||||
import type App from "../components/App";
|
import type App from "../components/App";
|
||||||
|
|
||||||
@@ -65,9 +66,10 @@ type LibraryUpdate = {
|
|||||||
updatedItems: Map<LibraryItem["id"], LibraryItem>;
|
updatedItems: Map<LibraryItem["id"], LibraryItem>;
|
||||||
};
|
};
|
||||||
|
|
||||||
// an object so that we can later add more properties to it without breaking,
|
export type LibraryPersistedData = {
|
||||||
// such as schema version
|
libraryItems: LibraryItems;
|
||||||
export type LibraryPersistedData = { libraryItems: LibraryItems };
|
schemaVersion?: number;
|
||||||
|
};
|
||||||
|
|
||||||
const onLibraryUpdateEmitter = new Emitter<
|
const onLibraryUpdateEmitter = new Emitter<
|
||||||
[update: LibraryUpdate, libraryItems: LibraryItems]
|
[update: LibraryUpdate, libraryItems: LibraryItems]
|
||||||
@@ -89,7 +91,9 @@ export interface LibraryPersistenceAdapter {
|
|||||||
* purposes, in which case host app can implement more aggressive caching.
|
* purposes, in which case host app can implement more aggressive caching.
|
||||||
*/
|
*/
|
||||||
source: LibraryAdatapterSource;
|
source: LibraryAdatapterSource;
|
||||||
}): MaybePromise<{ libraryItems: LibraryItems_anyVersion } | null>;
|
}): MaybePromise<
|
||||||
|
{ libraryItems: LibraryItems_anyVersion; schemaVersion?: number } | null
|
||||||
|
>;
|
||||||
/** Should persist to the database as is (do no change the data structure). */
|
/** Should persist to the database as is (do no change the data structure). */
|
||||||
save(libraryData: LibraryPersistedData): MaybePromise<void>;
|
save(libraryData: LibraryPersistedData): MaybePromise<void>;
|
||||||
}
|
}
|
||||||
@@ -99,7 +103,9 @@ export interface LibraryMigrationAdapter {
|
|||||||
* loads data from legacy data source. Returns `null` if no data is
|
* loads data from legacy data source. Returns `null` if no data is
|
||||||
* to be migrated.
|
* to be migrated.
|
||||||
*/
|
*/
|
||||||
load(): MaybePromise<{ libraryItems: LibraryItems_anyVersion } | null>;
|
load(): MaybePromise<
|
||||||
|
{ libraryItems: LibraryItems_anyVersion; schemaVersion?: number } | null
|
||||||
|
>;
|
||||||
|
|
||||||
/** clears entire storage afterwards */
|
/** clears entire storage afterwards */
|
||||||
clear(): MaybePromise<void>;
|
clear(): MaybePromise<void>;
|
||||||
@@ -554,7 +560,13 @@ class AdapterTransaction {
|
|||||||
new Promise<LibraryItems>(async (resolve, reject) => {
|
new Promise<LibraryItems>(async (resolve, reject) => {
|
||||||
try {
|
try {
|
||||||
const data = await adapter.load({ source });
|
const data = await adapter.load({ source });
|
||||||
resolve(restoreLibraryItems(data?.libraryItems || [], "published"));
|
resolve(
|
||||||
|
restoreLibraryItems(
|
||||||
|
data?.libraryItems || [],
|
||||||
|
"published",
|
||||||
|
resolveSchemaVersion(data?.schemaVersion, SCHEMA_VERSIONS.initial),
|
||||||
|
),
|
||||||
|
);
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
reject(error);
|
reject(error);
|
||||||
}
|
}
|
||||||
@@ -662,7 +674,10 @@ const persistLibraryUpdate = async (
|
|||||||
const version = getLibraryItemsHash(nextLibraryItems);
|
const version = getLibraryItemsHash(nextLibraryItems);
|
||||||
|
|
||||||
if (version !== lastSavedLibraryItemsHash) {
|
if (version !== lastSavedLibraryItemsHash) {
|
||||||
await adapter.save({ libraryItems: nextLibraryItems });
|
await adapter.save({
|
||||||
|
libraryItems: nextLibraryItems,
|
||||||
|
schemaVersion: SCHEMA_VERSIONS.latest,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
lastSavedLibraryItemsHash = version;
|
lastSavedLibraryItemsHash = version;
|
||||||
@@ -862,6 +877,10 @@ export const useHandleLibrary = (
|
|||||||
restoredData = restoreLibraryItems(
|
restoredData = restoreLibraryItems(
|
||||||
libraryData.libraryItems || [],
|
libraryData.libraryItems || [],
|
||||||
"published",
|
"published",
|
||||||
|
resolveSchemaVersion(
|
||||||
|
libraryData.schemaVersion,
|
||||||
|
SCHEMA_VERSIONS.initial,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
// we don't queue this operation because it's running inside
|
// we don't queue this operation because it's running inside
|
||||||
|
|||||||
@@ -81,6 +81,7 @@ import {
|
|||||||
getNormalizedGridStep,
|
getNormalizedGridStep,
|
||||||
getNormalizedZoom,
|
getNormalizedZoom,
|
||||||
} from "../scene";
|
} from "../scene";
|
||||||
|
import { migrateElementsBySchema, SCHEMA_VERSIONS } from "./schema";
|
||||||
|
|
||||||
import type {
|
import type {
|
||||||
AppState,
|
AppState,
|
||||||
@@ -956,9 +957,15 @@ export const restoreAppState = (
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
const restoreLibraryItem = (libraryItem: LibraryItem) => {
|
const restoreLibraryItem = (
|
||||||
|
libraryItem: LibraryItem,
|
||||||
|
schemaVersion: number,
|
||||||
|
) => {
|
||||||
const elements = restoreElements(
|
const elements = restoreElements(
|
||||||
getNonDeletedElements(libraryItem.elements),
|
migrateElementsBySchema(
|
||||||
|
getNonDeletedElements(libraryItem.elements),
|
||||||
|
schemaVersion,
|
||||||
|
),
|
||||||
null,
|
null,
|
||||||
);
|
);
|
||||||
return elements.length ? { ...libraryItem, elements } : null;
|
return elements.length ? { ...libraryItem, elements } : null;
|
||||||
@@ -967,6 +974,7 @@ const restoreLibraryItem = (libraryItem: LibraryItem) => {
|
|||||||
export const restoreLibraryItems = (
|
export const restoreLibraryItems = (
|
||||||
libraryItems: ImportedDataState["libraryItems"] = [],
|
libraryItems: ImportedDataState["libraryItems"] = [],
|
||||||
defaultStatus: LibraryItem["status"],
|
defaultStatus: LibraryItem["status"],
|
||||||
|
schemaVersion: number = SCHEMA_VERSIONS.latest,
|
||||||
) => {
|
) => {
|
||||||
const restoredItems: LibraryItem[] = [];
|
const restoredItems: LibraryItem[] = [];
|
||||||
for (const item of libraryItems) {
|
for (const item of libraryItems) {
|
||||||
@@ -977,7 +985,7 @@ export const restoreLibraryItems = (
|
|||||||
elements: item,
|
elements: item,
|
||||||
id: randomId(),
|
id: randomId(),
|
||||||
created: Date.now(),
|
created: Date.now(),
|
||||||
});
|
}, schemaVersion);
|
||||||
if (restoredItem) {
|
if (restoredItem) {
|
||||||
restoredItems.push(restoredItem);
|
restoredItems.push(restoredItem);
|
||||||
}
|
}
|
||||||
@@ -991,7 +999,7 @@ export const restoreLibraryItems = (
|
|||||||
id: _item.id || randomId(),
|
id: _item.id || randomId(),
|
||||||
status: _item.status || defaultStatus,
|
status: _item.status || defaultStatus,
|
||||||
created: _item.created || Date.now(),
|
created: _item.created || Date.now(),
|
||||||
});
|
}, schemaVersion);
|
||||||
if (restoredItem) {
|
if (restoredItem) {
|
||||||
restoredItems.push(restoredItem);
|
restoredItems.push(restoredItem);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -54,6 +54,7 @@ export interface ImportedDataState {
|
|||||||
export interface ExportedLibraryData {
|
export interface ExportedLibraryData {
|
||||||
type: string;
|
type: string;
|
||||||
version: typeof VERSIONS.excalidrawLibrary;
|
version: typeof VERSIONS.excalidrawLibrary;
|
||||||
|
schemaVersion: number;
|
||||||
source: string;
|
source: string;
|
||||||
libraryItems: LibraryItems;
|
libraryItems: LibraryItems;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,12 @@
|
|||||||
import { pointFrom } from "@excalidraw/math";
|
import { pointFrom } from "@excalidraw/math";
|
||||||
import { vi } from "vitest";
|
import { vi } from "vitest";
|
||||||
|
|
||||||
import { DEFAULT_SIDEBAR, FONT_FAMILY, ROUNDNESS } from "@excalidraw/common";
|
import {
|
||||||
|
DEFAULT_ELEMENT_PROPS,
|
||||||
|
DEFAULT_SIDEBAR,
|
||||||
|
FONT_FAMILY,
|
||||||
|
ROUNDNESS,
|
||||||
|
} from "@excalidraw/common";
|
||||||
|
|
||||||
import { newElementWith } from "@excalidraw/element";
|
import { newElementWith } from "@excalidraw/element";
|
||||||
import * as sizeHelpers from "@excalidraw/element";
|
import * as sizeHelpers from "@excalidraw/element";
|
||||||
@@ -19,6 +24,7 @@ import type { NormalizedZoomValue } from "@excalidraw/excalidraw/types";
|
|||||||
|
|
||||||
import { API } from "../helpers/api";
|
import { API } from "../helpers/api";
|
||||||
import * as restore from "../../data/restore";
|
import * as restore from "../../data/restore";
|
||||||
|
import { SCHEMA_VERSIONS } from "../../data/schema";
|
||||||
import { getDefaultAppState } from "../../appState";
|
import { getDefaultAppState } from "../../appState";
|
||||||
|
|
||||||
import type { ImportedDataState } from "../../data/types";
|
import type { ImportedDataState } from "../../data/types";
|
||||||
@@ -96,6 +102,53 @@ describe("restoreElements", () => {
|
|||||||
expect(restoredFrame.backgroundColor).toBe("#ffc9c9");
|
expect(restoredFrame.backgroundColor).toBe("#ffc9c9");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("should restore library frame backgrounds by default", () => {
|
||||||
|
const frame = API.createElement({
|
||||||
|
type: "frame",
|
||||||
|
backgroundColor: "#a5d8ff",
|
||||||
|
});
|
||||||
|
|
||||||
|
const restoredLibraryItems = restore.restoreLibraryItems(
|
||||||
|
[
|
||||||
|
{
|
||||||
|
id: "library-item-1",
|
||||||
|
status: "published",
|
||||||
|
elements: [frame],
|
||||||
|
created: Date.now(),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"published",
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(
|
||||||
|
(restoredLibraryItems[0].elements[0] as ExcalidrawElement).backgroundColor,
|
||||||
|
).toBe("#a5d8ff");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should normalize legacy library frame backgrounds when schema is old", () => {
|
||||||
|
const frame = API.createElement({
|
||||||
|
type: "frame",
|
||||||
|
backgroundColor: "#a5d8ff",
|
||||||
|
});
|
||||||
|
|
||||||
|
const restoredLibraryItems = restore.restoreLibraryItems(
|
||||||
|
[
|
||||||
|
{
|
||||||
|
id: "library-item-1",
|
||||||
|
status: "published",
|
||||||
|
elements: [frame],
|
||||||
|
created: Date.now(),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"published",
|
||||||
|
SCHEMA_VERSIONS.initial,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(
|
||||||
|
(restoredLibraryItems[0].elements[0] as ExcalidrawElement).backgroundColor,
|
||||||
|
).toBe(DEFAULT_ELEMENT_PROPS.backgroundColor);
|
||||||
|
});
|
||||||
|
|
||||||
it("should restore text element correctly passing value for each attribute", () => {
|
it("should restore text element correctly passing value for each attribute", () => {
|
||||||
const textElement = API.createElement({
|
const textElement = API.createElement({
|
||||||
type: "text",
|
type: "text",
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import type { ExcalidrawGenericElement } from "@excalidraw/element/types";
|
|||||||
import { parseLibraryJSON } from "../data/blob";
|
import { parseLibraryJSON } from "../data/blob";
|
||||||
import { serializeLibraryAsJSON } from "../data/json";
|
import { serializeLibraryAsJSON } from "../data/json";
|
||||||
import { distributeLibraryItemsOnSquareGrid } from "../data/library";
|
import { distributeLibraryItemsOnSquareGrid } from "../data/library";
|
||||||
|
import { SCHEMA_VERSIONS } from "../data/schema";
|
||||||
import { Excalidraw } from "../index";
|
import { Excalidraw } from "../index";
|
||||||
|
|
||||||
import { API } from "./helpers/api";
|
import { API } from "./helpers/api";
|
||||||
@@ -167,6 +168,13 @@ describe("library", () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("should include schema version when serializing library", () => {
|
||||||
|
const serialized = serializeLibraryAsJSON([]);
|
||||||
|
const parsed = JSON.parse(serialized);
|
||||||
|
|
||||||
|
expect(parsed.schemaVersion).toBe(SCHEMA_VERSIONS.latest);
|
||||||
|
});
|
||||||
|
|
||||||
// NOTE: mocked to test logic, not actual drag&drop via UI
|
// NOTE: mocked to test logic, not actual drag&drop via UI
|
||||||
it("drop library item onto canvas", async () => {
|
it("drop library item onto canvas", async () => {
|
||||||
expect(h.elements).toEqual([]);
|
expect(h.elements).toEqual([]);
|
||||||
|
|||||||
Reference in New Issue
Block a user