feat: inline variables outside current component/slot (#2778)

Ref https://github.com/webstudio-is/webstudio/issues/2532

Important constrain on the way to custom components to not use data
sources defined in other components when copy paste.

## Steps for reproduction

1. Test covers it
This commit is contained in:
Bogdan Chadkin authored and GitHub committed 2024-01-18 23:37:50 +07:00
1 parent 8d073e598d
commit a76c29fc05
4 files changed
+76 -2

No files matched your search

@@ -187,6 +187,7 @@ export const { emitCommand, subscribeCommands } = createCommandsEmitter({
slice,
availableDataSources: findAvailableDataSources(
$dataSources.get(),
$instances.get(),
parentInstanceSelector
),
beforeTransactionEnd: (rootInstanceId, draft) => {
@@ -27,7 +27,7 @@ import {
$registeredComponentMetas,
$selectedPageId,
} from "../nano-states";
import { onCopy, onPaste } from "./plugin-instance";
import { onCopy, onCut, onPaste } from "./plugin-instance";
import { createDefaultPages } from "@webstudio-is/project-build";
const expectString = expect.any(String) as unknown as string;
@@ -629,3 +629,66 @@ test("wrap siblings with span when instance is rich text container", () => {
])
);
});
test("inline data source not available in portal when copy paste inside the portal", () => {
const instances = toMap([
createInstance("body", "Body", [
{ type: "id", value: "box" },
{ type: "id", value: "portal" },
]),
createInstance("box", "Box", []),
createInstance("portal", portalComponent, [
{ type: "id", value: "fragment" },
]),
createInstance("fragment", "Fragment", []),
]);
$instances.set(instances);
const dataSources = toMap<DataSource>([
{
id: "variableId",
scopeInstanceId: "body",
name: "variableName",
type: "variable",
value: { type: "string", value: "value" },
},
]);
$dataSources.set(dataSources);
const props = toMap<Prop>([
{
id: "propId",
instanceId: "box",
name: "data-value",
type: "expression",
value: "$ws$dataSource$variableId",
},
]);
$props.set(props);
$selectedInstanceSelector.set(["box", "body"]);
const clipboardData = onCut() ?? "";
$selectedInstanceSelector.set(["fragment", "portal", "body"]);
onPaste(clipboardData);
expect($instances.get()).toEqual(
toMap([
createInstance("body", "Body", [{ type: "id", value: "portal" }]),
createInstance("portal", portalComponent, [
{ type: "id", value: "fragment" },
]),
createInstance("fragment", "Fragment", [
{ type: "id", value: expectString },
]),
createInstance(expectString, "Box", []),
])
);
expect($dataSources.get()).toEqual(dataSources);
expect($props.get()).toEqual(
toMap<Prop>([
{
id: expectString,
instanceId: expectString,
name: "data-value",
type: "expression",
value: `"value"`,
},
])
);
});
@@ -206,6 +206,7 @@ export const onPaste = (clipboardData: string): boolean => {
slice: data,
availableDataSources: findAvailableDataSources(
$dataSources.get(),
$instances.get(),
instanceSelector
),
beforeTransactionEnd: (rootInstanceId, draft) => {
+10 -1
View File
@@ -722,9 +722,18 @@ export const getInstancesSlice = (rootInstanceId: string) => {
export const findAvailableDataSources = (
dataSources: DataSources,
instances: Instances,
instanceSelector: InstanceSelector
) => {
const instanceIds = new Set(instanceSelector);
// inline data sources not scoped to current portal
const instanceIds = new Set();
for (const instanceId of instanceSelector) {
const instance = instances.get(instanceId);
if (instance?.component === portalComponent) {
break;
}
instanceIds.add(instanceId);
}
const availableDataSources = new Set<DataSource["id"]>();
for (const { id, scopeInstanceId } of dataSources.values()) {
if (scopeInstanceId && instanceIds.has(scopeInstanceId)) {