Skip to content

Commit

Permalink
trigger chained actions on unnamed shadows (#32)
Browse files Browse the repository at this point in the history
protect against undefined component
  • Loading branch information
dqnykamp authored Oct 2, 2023
1 parent af4e6cb commit 0df3cbe
Show file tree
Hide file tree
Showing 2 changed files with 119 additions and 25 deletions.
62 changes: 37 additions & 25 deletions packages/doenetml/src/Core/Core.js
Original file line number Diff line number Diff line change
Expand Up @@ -10681,39 +10681,51 @@ export default class Core {

this.updateInfo.componentsToUpdateActionChaining = {};

let id = componentName;
let actionsToChain = [];

while (id.substring(0, 3) === "/__") {
// if component was has a unreachable component name
// check if it is shadowing another component and use that component name instead
let comp = this._components[id];
let cName = componentName;

if (comp.shadows) {
id = comp.shadows.componentName;
while (true) {
let comp = this._components[cName];
let id = cName;

if (triggeringAction) {
id += "|" + triggeringAction;
}

if (this.actionsChangedToActions[id]) {
actionsToChain.push(...this.actionsChangedToActions[id]);
}

if (comp?.shadows && cName.substring(0, 3) === "/__") {
// We propagate to shadows if the copied component doesn't have a name.
// In this way, if we include $P in a graph,
// then triggerWhenObjectsClicked="P" and triggerWhenObjectsFocused="P"
// will be triggered by that copy (as only as copy not given a name).
// Another use case is defining an <updateValue name="uv">,
// along with other triggered actions using triggerWith="uv",
// inside a <setup> and then including an unamed $uv
// where we want the button to be.
// TODO: if <point copySource="P" /> no longer has a name like "_point1",
// should triggerWhenObjectsClicked="P" be triggered from that point?
// Currently (Oct 2, 2023), it is not triggered.
cName = comp.shadows.componentName;
} else {
break;
}
}

if (triggeringAction) {
id += "|" + triggeringAction;
}

if (this.actionsChangedToActions[id]) {
for (let chainedActionInstructions of this.actionsChangedToActions[
id
]) {
chainedActionInstructions = { ...chainedActionInstructions };
if (chainedActionInstructions.args) {
chainedActionInstructions.args = {
...chainedActionInstructions.args,
};
} else {
chainedActionInstructions.args = {};
}
chainedActionInstructions.args.skipRendererUpdate = true;
await this.performAction(chainedActionInstructions);
for (let chainedActionInstructions of actionsToChain) {
chainedActionInstructions = { ...chainedActionInstructions };
if (chainedActionInstructions.args) {
chainedActionInstructions.args = {
...chainedActionInstructions.args,
};
} else {
chainedActionInstructions.args = {};
}
chainedActionInstructions.args.skipRendererUpdate = true;
await this.performAction(chainedActionInstructions);
}

if (!skipRendererUpdate) {
Expand Down
82 changes: 82 additions & 0 deletions packages/test-cypress/cypress/e2e/tagSpecific/updatevalue.cy.js
Original file line number Diff line number Diff line change
Expand Up @@ -1207,6 +1207,55 @@ describe("UpdateValue Tag Tests", function () {
});
});

it("chained updates, copies don't copy triggers", () => {
cy.window().then(async (win) => {
win.postMessage(
{
doenetML: `
<p>n: <number name="n">1</number></p>
<p>m1: <number name="m1">1</number></p>
<p>m2: <number name="m2">1</number></p>
<p><updateValue name="uv" target="n" newValue="$n+1" /></p>
<p><updateValue name="uv2" copySource="uv" /></p>
<p><updateValue name="uv3" copySource="uv" /></p>
<p name="pmacro">$uv</p>
<updateValue triggerWith="uv" target="m1" newValue="$m1+1" />
<updateValue triggerWith="uv2" target="m2" newValue="$m2+1" />
`,
},
"*",
);
});

cy.get(cesc2("#/n")).should("have.text", "1");
cy.get(cesc2("#/m1")).should("have.text", "1");
cy.get(cesc2("#/m2")).should("have.text", "1");

cy.get(cesc2("#/uv")).click();
cy.get(cesc2("#/n")).should("have.text", "2");
cy.get(cesc2("#/m1")).should("have.text", "2");
cy.get(cesc2("#/m2")).should("have.text", "1");

cy.get(cesc2("#/uv2")).click();
cy.get(cesc2("#/n")).should("have.text", "3");
cy.get(cesc2("#/m1")).should("have.text", "2");
cy.get(cesc2("#/m2")).should("have.text", "2");

cy.get(cesc2("#/uv3")).click();
cy.get(cesc2("#/n")).should("have.text", "4");
cy.get(cesc2("#/m1")).should("have.text", "2");
cy.get(cesc2("#/m2")).should("have.text", "2");

// Note: we expect the macro to trigger the updateValue with triggerWith="uv"
// because it doesn't have a name.
cy.get(cesc2("#/pmacro") + " button").click();
cy.get(cesc2("#/n")).should("have.text", "5");
cy.get(cesc2("#/m1")).should("have.text", "3");
cy.get(cesc2("#/m2")).should("have.text", "2");
});

it("update based on trigger", () => {
cy.window().then(async (win) => {
win.postMessage(
Expand Down Expand Up @@ -3686,4 +3735,37 @@ describe("UpdateValue Tag Tests", function () {
cy.get(cesc("#\\/pDisabled1")).should("have.text", "Disabled 1: true");
cy.get(cesc("#\\/pDisabled2")).should("have.text", "Disabled 2: true");
});

it("handle removed updateValue when shadowing", () => {
cy.window().then(async (win) => {
win.postMessage(
{
doenetML: `
<group copySource="grp" />
<setup>
<group name="grp">
<p><boolean name="show">true</boolean></p>
<conditionalContent>
<case condition="$show">
<updateValue name="uv" target="show" type="boolean" newValue="!$show" />
</case>
</conditionalContent>
</group>
</setup>
`,
},
"*",
);
});

cy.get(".doenet-viewer p").eq(0).should("have.text", "true");

cy.get(".doenet-viewer button").click();
cy.get(".doenet-viewer p").eq(0).should("have.text", "false");
cy.get(".doenet-viewer button").should("not.exist");
});
});

0 comments on commit 0df3cbe

Please sign in to comment.