diff --git a/packages/doenetml-worker/src/components/BooleanInput.js b/packages/doenetml-worker/src/components/BooleanInput.js index 49f428d9c..f7be01803 100644 --- a/packages/doenetml-worker/src/components/BooleanInput.js +++ b/packages/doenetml-worker/src/components/BooleanInput.js @@ -4,6 +4,7 @@ import { returnAnchorStateVariableDefinition, } from "../utils/graphical"; import { + returnLabelAttributes, returnLabelStateVariableDefinitions, returnWrapNonLabelsSugarFunction, } from "../utils/label"; @@ -48,12 +49,6 @@ export default class BooleanInput extends Input { defaultValue: false, public: true, }; - attributes.labelIsName = { - createComponentOfType: "boolean", - createStateVariable: "labelIsName", - defaultValue: false, - public: true, - }; attributes.asToggleButton = { createComponentOfType: "boolean", createStateVariable: "asToggleButton", @@ -73,6 +68,8 @@ export default class BooleanInput extends Input { forRenderer: true, }; + Object.assign(attributes, returnLabelAttributes()); + Object.assign(attributes, returnAnchorAttributes()); return attributes; diff --git a/packages/doenetml-worker/src/components/CallAction.js b/packages/doenetml-worker/src/components/CallAction.js index a4b94581c..68d8f7f9e 100644 --- a/packages/doenetml-worker/src/components/CallAction.js +++ b/packages/doenetml-worker/src/components/CallAction.js @@ -4,7 +4,10 @@ import { returnAnchorAttributes, returnAnchorStateVariableDefinition, } from "../utils/graphical"; -import { returnLabelStateVariableDefinitions } from "../utils/label"; +import { + returnLabelAttributes, + returnLabelStateVariableDefinitions, +} from "../utils/label"; import { addStandardTriggeringStateVariableDefinitions, returnStandardTriggeringAttributes, @@ -56,13 +59,6 @@ export default class CallAction extends InlineComponent { // attributes.width = {default: 300}; // attributes.height = {default: 50}; - attributes.labelIsName = { - createComponentOfType: "boolean", - createStateVariable: "labelIsName", - defaultValue: false, - public: true, - }; - attributes.actionName = { createComponentOfType: "text", createStateVariable: "actionName", @@ -78,6 +74,8 @@ export default class CallAction extends InlineComponent { forRenderer: true, }; + Object.assign(attributes, returnLabelAttributes()); + Object.assign(attributes, returnAnchorAttributes()); let triggerAttributes = returnStandardTriggeringAttributes( diff --git a/packages/doenetml-worker/src/components/ChoiceInput.js b/packages/doenetml-worker/src/components/ChoiceInput.js index 58d640de2..b25b00cbc 100644 --- a/packages/doenetml-worker/src/components/ChoiceInput.js +++ b/packages/doenetml-worker/src/components/ChoiceInput.js @@ -2,7 +2,10 @@ import Input from "./abstract/Input"; import me from "math-expressions"; import { enumerateCombinations, enumeratePermutations } from "@doenet/utils"; import { setUpVariantSeedAndRng } from "../utils/variants"; -import { returnLabelStateVariableDefinitions } from "../utils/label"; +import { + returnLabelAttributes, + returnLabelStateVariableDefinitions, +} from "../utils/label"; export default class Choiceinput extends Input { constructor(args) { @@ -102,12 +105,7 @@ export default class Choiceinput extends Input { fallBackToParentStateVariable: "submitLabelNoCorrectness", }; - attributes.labelIsName = { - createComponentOfType: "boolean", - createStateVariable: "labelIsName", - defaultValue: false, - public: true, - }; + Object.assign(attributes, returnLabelAttributes()); return attributes; } diff --git a/packages/doenetml-worker/src/components/Copy.js b/packages/doenetml-worker/src/components/Copy.js index 6dec195c6..fc6ade24c 100644 --- a/packages/doenetml-worker/src/components/Copy.js +++ b/packages/doenetml-worker/src/components/Copy.js @@ -1830,6 +1830,7 @@ export default class Copy extends CompositeComponent { serializedReplacements = [ await replacementSourceComponent.serialize({ copyAll: !link, + componentSourceAttributesToIgnore: ["labelIsName"], copyVariants: !link, primitiveSourceAttributesToIgnore: sourceAttributesToIgnore, copyPrimaryEssential, diff --git a/packages/doenetml-worker/src/components/Function.js b/packages/doenetml-worker/src/components/Function.js index 9f8600206..7782141aa 100644 --- a/packages/doenetml-worker/src/components/Function.js +++ b/packages/doenetml-worker/src/components/Function.js @@ -16,7 +16,10 @@ import { returnRoundingAttributes, returnRoundingStateVariableDefinitions, } from "../utils/rounding"; -import { returnWrapNonLabelsSugarFunction } from "../utils/label"; +import { + returnLabelAttributes, + returnWrapNonLabelsSugarFunction, +} from "../utils/label"; import { find_local_global_maxima, find_local_global_minima, @@ -79,12 +82,8 @@ export default class Function extends InlineComponent { // include attributes of graphical components // for case when function is adapted into a curve - attributes.labelIsName = { - createComponentOfType: "boolean", - createStateVariable: "labelIsName", - defaultValue: false, - public: true, - }; + Object.assign(attributes, returnLabelAttributes()); + attributes.applyStyleToLabel = { createComponentOfType: "boolean", createStateVariable: "applyStyleToLabel", diff --git a/packages/doenetml-worker/src/components/MathInput.js b/packages/doenetml-worker/src/components/MathInput.js index 16f21718f..fc841cbb3 100644 --- a/packages/doenetml-worker/src/components/MathInput.js +++ b/packages/doenetml-worker/src/components/MathInput.js @@ -7,6 +7,7 @@ import { returnRoundingStateVariableDefinitions, } from "../utils/rounding"; import { + returnLabelAttributes, returnLabelStateVariableDefinitions, returnWrapNonLabelsSugarFunction, } from "../utils/label"; @@ -130,12 +131,9 @@ export default class MathInput extends Input { public: true, forRenderer: true, }; - attributes.labelIsName = { - createComponentOfType: "boolean", - createStateVariable: "labelIsName", - defaultValue: false, - public: true, - }; + + Object.assign(attributes, returnLabelAttributes()); + return attributes; } diff --git a/packages/doenetml-worker/src/components/Slider.js b/packages/doenetml-worker/src/components/Slider.js index 0f8ba9ad9..ca410eb9d 100644 --- a/packages/doenetml-worker/src/components/Slider.js +++ b/packages/doenetml-worker/src/components/Slider.js @@ -1,7 +1,10 @@ import { roundForDisplay } from "../utils/math"; import BaseComponent from "./abstract/BaseComponent"; import me from "math-expressions"; -import { returnLabelStateVariableDefinitions } from "../utils/label"; +import { + returnLabelAttributes, + returnLabelStateVariableDefinitions, +} from "../utils/label"; import { returnRoundingAttributeComponentShadowing, returnRoundingAttributes, @@ -50,12 +53,6 @@ export default class Slider extends BaseComponent { createStateVariable: "initialValue", defaultValue: null, }; - attributes.labelIsName = { - createComponentOfType: "boolean", - createStateVariable: "labelIsName", - defaultValue: false, - public: true, - }; attributes.showControls = { createComponentOfType: "boolean", createStateVariable: "showControls", @@ -99,6 +96,8 @@ export default class Slider extends BaseComponent { forRenderer: true, }; + Object.assign(attributes, returnLabelAttributes()); + Object.assign(attributes, returnRoundingAttributes()); attributes.bindValueTo = { diff --git a/packages/doenetml-worker/src/components/TextInput.js b/packages/doenetml-worker/src/components/TextInput.js index 8f801059e..0d5d71e35 100644 --- a/packages/doenetml-worker/src/components/TextInput.js +++ b/packages/doenetml-worker/src/components/TextInput.js @@ -4,6 +4,7 @@ import { returnAnchorStateVariableDefinition, } from "../utils/graphical"; import { + returnLabelAttributes, returnLabelStateVariableDefinitions, returnWrapNonLabelsSugarFunction, } from "../utils/label"; @@ -73,12 +74,6 @@ export default class Textinput extends Input { forRenderer: true, public: true, }; - attributes.labelIsName = { - createComponentOfType: "boolean", - createStateVariable: "labelIsName", - defaultValue: false, - public: true, - }; attributes.draggable = { createComponentOfType: "boolean", createStateVariable: "draggable", @@ -89,6 +84,8 @@ export default class Textinput extends Input { Object.assign(attributes, returnAnchorAttributes()); + Object.assign(attributes, returnLabelAttributes()); + return attributes; } diff --git a/packages/doenetml-worker/src/components/TriggerSet.js b/packages/doenetml-worker/src/components/TriggerSet.js index cf73ae85c..7e81cd5fb 100644 --- a/packages/doenetml-worker/src/components/TriggerSet.js +++ b/packages/doenetml-worker/src/components/TriggerSet.js @@ -3,7 +3,10 @@ import { returnAnchorAttributes, returnAnchorStateVariableDefinition, } from "../utils/graphical"; -import { returnLabelStateVariableDefinitions } from "../utils/label"; +import { + returnLabelAttributes, + returnLabelStateVariableDefinitions, +} from "../utils/label"; import { addStandardTriggeringStateVariableDefinitions, returnStandardTriggeringAttributes, @@ -30,13 +33,6 @@ export default class triggerSet extends InlineComponent { // attributes.width = {default: 300}; // attributes.height = {default: 50}; - attributes.labelIsName = { - createComponentOfType: "boolean", - createStateVariable: "labelIsName", - defaultValue: false, - public: true, - }; - attributes.draggable = { createComponentOfType: "boolean", createStateVariable: "draggable", @@ -47,6 +43,8 @@ export default class triggerSet extends InlineComponent { Object.assign(attributes, returnAnchorAttributes()); + Object.assign(attributes, returnLabelAttributes()); + let triggerAttributes = returnStandardTriggeringAttributes( "triggerActionsIfTriggerNewlyTrue", ); diff --git a/packages/doenetml-worker/src/components/UpdateValue.js b/packages/doenetml-worker/src/components/UpdateValue.js index bfb29b78f..84dee19fd 100644 --- a/packages/doenetml-worker/src/components/UpdateValue.js +++ b/packages/doenetml-worker/src/components/UpdateValue.js @@ -3,7 +3,10 @@ import { returnAnchorAttributes, returnAnchorStateVariableDefinition, } from "../utils/graphical"; -import { returnLabelStateVariableDefinitions } from "../utils/label"; +import { + returnLabelAttributes, + returnLabelStateVariableDefinitions, +} from "../utils/label"; import { normalizeMathExpression } from "@doenet/utils"; import { addStandardTriggeringStateVariableDefinitions, @@ -33,13 +36,6 @@ export default class UpdateValue extends InlineComponent { // attributes.width = {default: 300}; // attributes.height = {default: 50}; - attributes.labelIsName = { - createComponentOfType: "boolean", - createStateVariable: "labelIsName", - defaultValue: false, - public: true, - }; - attributes.type = { createPrimitiveOfType: "string", createStateVariable: "type", @@ -98,6 +94,8 @@ export default class UpdateValue extends InlineComponent { Object.assign(attributes, returnAnchorAttributes()); + Object.assign(attributes, returnLabelAttributes()); + let triggerAttributes = returnStandardTriggeringAttributes( "updateValueIfTriggerNewlyTrue", ); diff --git a/packages/doenetml-worker/src/components/abstract/BaseComponent.js b/packages/doenetml-worker/src/components/abstract/BaseComponent.js index 1f0e8860c..d057e0408 100644 --- a/packages/doenetml-worker/src/components/abstract/BaseComponent.js +++ b/packages/doenetml-worker/src/components/abstract/BaseComponent.js @@ -1202,6 +1202,14 @@ export default class BaseComponent { primitiveSourceAttributesToIgnore = []; } + let componentSourceAttributesToIgnore; + if (parameters.componentSourceAttributesToIgnore) { + componentSourceAttributesToIgnore = + parameters.componentSourceAttributesToIgnore; + } else { + componentSourceAttributesToIgnore = []; + } + if (includeDefiningChildren) { if ( this.constructor.serializeReplacementsForChildren && @@ -1246,8 +1254,15 @@ export default class BaseComponent { for (let attrName in this.attributes) { let attribute = this.attributes[attrName]; if (attribute.component) { - // only copy attribute components if copy all - if (parameters.copyAll) { + // only copy attribute components if copy all and not set to be ignored + // Note that, unlike for the primitive case, below, + // attributeToIgnore supersedes copyAll. + // As of Nov 2024, the only componentSourceAttributesToIgnore + // is labelIsName when copying without link. + if ( + parameters.copyAll && + !componentSourceAttributesToIgnore.includes(attrName) + ) { serializedComponent.attributes[attrName] = { component: await attribute.component.serialize( parametersForChildren, diff --git a/packages/doenetml-worker/src/components/abstract/GraphicalComponent.js b/packages/doenetml-worker/src/components/abstract/GraphicalComponent.js index 67771552a..b88229d81 100644 --- a/packages/doenetml-worker/src/components/abstract/GraphicalComponent.js +++ b/packages/doenetml-worker/src/components/abstract/GraphicalComponent.js @@ -1,18 +1,18 @@ import BaseComponent from "./BaseComponent"; import { returnSelectedStyleStateVariableDefinition } from "@doenet/utils"; -import { returnLabelStateVariableDefinitions } from "../../utils/label"; +import { + returnLabelAttributes, + returnLabelStateVariableDefinitions, +} from "../../utils/label"; export default class GraphicalComponent extends BaseComponent { static componentType = "_graphical"; static createAttributesObject() { let attributes = super.createAttributesObject(); - attributes.labelIsName = { - createComponentOfType: "boolean", - createStateVariable: "labelIsName", - defaultValue: false, - public: true, - }; + + Object.assign(attributes, returnLabelAttributes()); + attributes.applyStyleToLabel = { createComponentOfType: "boolean", createStateVariable: "applyStyleToLabel", diff --git a/packages/doenetml-worker/src/test/graphical/labels.test.ts b/packages/doenetml-worker/src/test/graphical/labels.test.ts new file mode 100644 index 000000000..904dfce85 --- /dev/null +++ b/packages/doenetml-worker/src/test/graphical/labels.test.ts @@ -0,0 +1,893 @@ +import { describe, expect, it, vi } from "vitest"; +import { createTestCore, returnAllStateVariables } from "../utils/test-core"; +import { updateTextInputValue } from "../utils/actions"; +import Core from "../../Core"; + +const Mock = vi.fn(); +vi.stubGlobal("postMessage", Mock); +vi.mock("hyperformula"); + +describe("Label tests", async () => { + async function test_labelIsName_preserved_shadowed_or_no_link(core: Core) { + const stateVariables = await returnAllStateVariables(core); + + expect(stateVariables["/Plabel"].stateValues.value).eq("P"); + expect(stateVariables["/Qlabel"].stateValues.value).eq("Q"); + expect(stateVariables["/Rlabel"].stateValues.value).eq("R"); + expect(stateVariables["/Slabel"].stateValues.value).eq("S"); + expect(stateVariables["/g2Plabel"].stateValues.value).eq("P"); + expect(stateVariables["/g2Qlabel"].stateValues.value).eq("Q"); + expect(stateVariables["/g2Rlabel"].stateValues.value).eq("R"); + expect(stateVariables["/g2Slabel"].stateValues.value).eq("S"); + expect(stateVariables["/Q3label"].stateValues.value).eq("Q"); + expect(stateVariables["/S3label"].stateValues.value).eq("S"); + expect(stateVariables["/Q4label"].stateValues.value).eq("Q"); + expect(stateVariables["/S4label"].stateValues.value).eq("S"); + expect(stateVariables["/g5Plabel"].stateValues.value).eq("P"); + expect(stateVariables["/g5Qlabel"].stateValues.value).eq("Q"); + expect(stateVariables["/g5Rlabel"].stateValues.value).eq("R"); + expect(stateVariables["/g5Slabel"].stateValues.value).eq("S"); + expect(stateVariables["/g5Plabel"].stateValues.value).eq("P"); + expect(stateVariables["/g6Qlabel"].stateValues.value).eq("Q"); + expect(stateVariables["/g6Slabel"].stateValues.value).eq("S"); + expect(stateVariables["/g7Qlabel"].stateValues.value).eq("Q"); + expect(stateVariables["/g7Slabel"].stateValues.value).eq("S"); + + let P2Name = stateVariables["/g2"].activeChildren[0].componentName; + let Q2Name = stateVariables["/g2"].activeChildren[1].componentName; + let R2Name = stateVariables["/g2"].activeChildren[2].componentName; + let S2Name = stateVariables["/g2"].activeChildren[3].componentName; + let P3Name = stateVariables["/g3"].activeChildren[0].componentName; + let Q3Name = stateVariables["/g3"].activeChildren[1].componentName; + let R3Name = stateVariables["/g3"].activeChildren[2].componentName; + let S3Name = stateVariables["/g3"].activeChildren[3].componentName; + let P4Name = stateVariables["/g4"].activeChildren[0].componentName; + let Q4Name = stateVariables["/g4"].activeChildren[1].componentName; + let R4Name = stateVariables["/g4"].activeChildren[2].componentName; + let S4Name = stateVariables["/g4"].activeChildren[3].componentName; + let P5Name = stateVariables["/g5"].activeChildren[0].componentName; + let Q5Name = stateVariables["/g5"].activeChildren[1].componentName; + let R5Name = stateVariables["/g5"].activeChildren[2].componentName; + let S5Name = stateVariables["/g5"].activeChildren[3].componentName; + let P6Name = stateVariables["/g6"].activeChildren[0].componentName; + let Q6Name = stateVariables["/g6"].activeChildren[1].componentName; + let R6Name = stateVariables["/g6"].activeChildren[2].componentName; + let S6Name = stateVariables["/g6"].activeChildren[3].componentName; + let P7Name = stateVariables["/g7"].activeChildren[0].componentName; + let Q7Name = stateVariables["/g7"].activeChildren[1].componentName; + let R7Name = stateVariables["/g7"].activeChildren[2].componentName; + let S7Name = stateVariables["/g7"].activeChildren[3].componentName; + + expect(stateVariables["/P"].stateValues.label).eq("P"); + expect(stateVariables["/P"].stateValues.labelForGraph).eq("P"); + expect(stateVariables["/Q"].stateValues.label).eq("Q"); + expect(stateVariables["/Q"].stateValues.labelForGraph).eq("Q"); + expect(stateVariables["/R"].stateValues.label).eq("R"); + expect(stateVariables["/R"].stateValues.labelForGraph).eq("R"); + expect(stateVariables["/S"].stateValues.label).eq("S"); + expect(stateVariables["/S"].stateValues.labelForGraph).eq("S"); + expect(stateVariables[P2Name].stateValues.label).eq("P"); + expect(stateVariables[P2Name].stateValues.labelForGraph).eq("P"); + expect(stateVariables[Q2Name].stateValues.label).eq(`Q`); + expect(stateVariables[Q2Name].stateValues.labelForGraph).eq(`Q`); + expect(stateVariables[R2Name].stateValues.label).eq(`R`); + expect(stateVariables[R2Name].stateValues.labelForGraph).eq(`R`); + expect(stateVariables[S2Name].stateValues.label).eq(`S`); + expect(stateVariables[S2Name].stateValues.labelForGraph).eq(`S`); + expect(stateVariables["/Q3"].stateValues.label).eq(`Q`); + expect(stateVariables["/Q3"].stateValues.labelForGraph).eq(`Q`); + expect(stateVariables["/S3"].stateValues.label).eq(`S`); + expect(stateVariables["/S3"].stateValues.labelForGraph).eq(`S`); + expect(stateVariables[P3Name].stateValues.label).eq("P"); + expect(stateVariables[P3Name].stateValues.labelForGraph).eq("P"); + expect(stateVariables[Q3Name].stateValues.label).eq(`Q`); + expect(stateVariables[Q3Name].stateValues.labelForGraph).eq(`Q`); + expect(stateVariables[R3Name].stateValues.label).eq(`R`); + expect(stateVariables[R3Name].stateValues.labelForGraph).eq(`R`); + expect(stateVariables[S3Name].stateValues.label).eq(`S`); + expect(stateVariables[S3Name].stateValues.labelForGraph).eq(`S`); + expect(stateVariables["/Q4"].stateValues.label).eq(`Q`); + expect(stateVariables["/Q4"].stateValues.labelForGraph).eq(`Q`); + expect(stateVariables["/S4"].stateValues.label).eq(`S`); + expect(stateVariables["/S4"].stateValues.labelForGraph).eq(`S`); + expect(stateVariables[P4Name].stateValues.label).eq("P"); + expect(stateVariables[P4Name].stateValues.labelForGraph).eq("P"); + expect(stateVariables[Q4Name].stateValues.label).eq(`Q`); + expect(stateVariables[Q4Name].stateValues.labelForGraph).eq(`Q`); + expect(stateVariables[R4Name].stateValues.label).eq(`R`); + expect(stateVariables[R4Name].stateValues.labelForGraph).eq(`R`); + expect(stateVariables[S4Name].stateValues.label).eq(`S`); + expect(stateVariables[S4Name].stateValues.labelForGraph).eq(`S`); + expect(stateVariables[P5Name].stateValues.label).eq("P"); + expect(stateVariables[P5Name].stateValues.labelForGraph).eq("P"); + expect(stateVariables[Q5Name].stateValues.label).eq(`Q`); + expect(stateVariables[Q5Name].stateValues.labelForGraph).eq(`Q`); + expect(stateVariables[R5Name].stateValues.label).eq(`R`); + expect(stateVariables[R5Name].stateValues.labelForGraph).eq(`R`); + expect(stateVariables[S5Name].stateValues.label).eq(`S`); + expect(stateVariables[S5Name].stateValues.labelForGraph).eq(`S`); + expect(stateVariables[P6Name].stateValues.label).eq("P"); + expect(stateVariables[P6Name].stateValues.labelForGraph).eq("P"); + expect(stateVariables[Q6Name].stateValues.label).eq(`Q`); + expect(stateVariables[Q6Name].stateValues.labelForGraph).eq(`Q`); + expect(stateVariables[R6Name].stateValues.label).eq(`R`); + expect(stateVariables[R6Name].stateValues.labelForGraph).eq(`R`); + expect(stateVariables[S6Name].stateValues.label).eq(`S`); + expect(stateVariables[S6Name].stateValues.labelForGraph).eq(`S`); + expect(stateVariables[P7Name].stateValues.label).eq("P"); + expect(stateVariables[P7Name].stateValues.labelForGraph).eq("P"); + expect(stateVariables[Q7Name].stateValues.label).eq(`Q`); + expect(stateVariables[Q7Name].stateValues.labelForGraph).eq(`Q`); + expect(stateVariables[R7Name].stateValues.label).eq(`R`); + expect(stateVariables[R7Name].stateValues.labelForGraph).eq(`R`); + expect(stateVariables[S7Name].stateValues.label).eq(`S`); + expect(stateVariables[S7Name].stateValues.labelForGraph).eq(`S`); + } + + it("labels from labelIsName are preserved when shadowed", async () => { + let core = await createTestCore({ + doenetML: ` + + (1,2) + (3,4) + (5,6) + (7,8) + + + + + + $P + $Q{name="Q3"} + + + + + + $P{labelIsName="false"} + $Q{name="Q4" labelIsName="false"} + + + + + + + + +

P label:

+

Q label:

+

R label:

+

S label:

+

g2/P label:

+

g2/Q label:

+

g2/R label:

+

g2/S label:

+

Q3 label:

+

S3 label:

+

Q4 label:

+

S4 label:

+

g5/P label:

+

g5/Q label:

+

g5/R label:

+

g5/S label:

+

g6/Q3 label:

+

g6/S3 label:

+

g7/Q4 label:

+

g7/S4 label:

+ + `, + }); + + await test_labelIsName_preserved_shadowed_or_no_link(core); + }); + + it("labels from labelIsName are preserved when copy with link=false", async () => { + let core = await createTestCore({ + doenetML: ` + + (1,2) + (3,4) + (5,6) + (7,8) + + + + + + $P{link="false"} + $Q{name="Q3" link="false"} + + + + + + $P{labelIsName="false" link="false"} + $Q{name="Q4" labelIsName="false" link="false"} + + + + + + + + +

P label:

+

Q label:

+

R label:

+

S label:

+

g2/P label:

+

g2/Q label:

+

g2/R label:

+

g2/S label:

+

Q3 label:

+

S3 label:

+

Q4 label:

+

S4 label:

+

g5/P label:

+

g5/Q label:

+

g5/R label:

+

g5/S label:

+

g6/Q3 label:

+

g6/S3 label:

+

g7/Q4 label:

+

g7/S4 label:

+ + `, + }); + + await test_labelIsName_preserved_shadowed_or_no_link(core); + }); + + it("labelIsName in map", async () => { + let core = await createTestCore({ + doenetML: ` + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + $g1{name="g7"} + $g2{name="g8"} + $g3{name="g9"} + $g4{name="g10"} + $g5{name="g11"} + $g6{name="g12"} + `, + }); + + let stateVariables = await returnAllStateVariables(core); + + let g1ChildNames = stateVariables["/g1"].activeChildren.map( + (x) => x.componentName, + ); + let g2ChildNames = stateVariables["/g2"].activeChildren.map( + (x) => x.componentName, + ); + let g3ChildNames = stateVariables["/g3"].activeChildren.map( + (x) => x.componentName, + ); + let g4ChildNames = stateVariables["/g4"].activeChildren.map( + (x) => x.componentName, + ); + let g5ChildNames = stateVariables["/g5"].activeChildren.map( + (x) => x.componentName, + ); + let g6ChildNames = stateVariables["/g6"].activeChildren.map( + (x) => x.componentName, + ); + let g7ChildNames = stateVariables["/g7"].activeChildren.map( + (x) => x.componentName, + ); + let g8ChildNames = stateVariables["/g8"].activeChildren.map( + (x) => x.componentName, + ); + let g9ChildNames = stateVariables["/g9"].activeChildren.map( + (x) => x.componentName, + ); + let g10ChildNames = stateVariables["/g10"].activeChildren.map( + (x) => x.componentName, + ); + let g11ChildNames = stateVariables["/g11"].activeChildren.map( + (x) => x.componentName, + ); + let g12ChildNames = stateVariables["/g12"].activeChildren.map( + (x) => x.componentName, + ); + + let g1ChildLabels = Array(5).fill(""); + let g2ChildLabels = Array(5).fill("P"); + let g3ChildLabels = ["A", "B", "C", "", ""]; + let g4ChildLabels = ["A", "B", "C", "P", "P"]; + let g5ChildLabels = Array(5).fill(""); + let g6ChildLabels = Array(5).fill("P"); + + for (let [ind, name] of g1ChildNames.entries()) { + expect(stateVariables[name].stateValues.label).eq( + g1ChildLabels[ind], + ); + } + for (let [ind, name] of g2ChildNames.entries()) { + expect(stateVariables[name].stateValues.label).eq( + g2ChildLabels[ind], + ); + } + for (let [ind, name] of g3ChildNames.entries()) { + expect(stateVariables[name].stateValues.label).eq( + g3ChildLabels[ind], + ); + } + for (let [ind, name] of g4ChildNames.entries()) { + expect(stateVariables[name].stateValues.label).eq( + g4ChildLabels[ind], + ); + } + for (let [ind, name] of g5ChildNames.entries()) { + expect(stateVariables[name].stateValues.label).eq( + g5ChildLabels[ind], + ); + } + for (let [ind, name] of g6ChildNames.entries()) { + expect(stateVariables[name].stateValues.label).eq( + g6ChildLabels[ind], + ); + } + + for (let [ind, name] of g7ChildNames.entries()) { + expect(stateVariables[name].stateValues.label).eq( + g1ChildLabels[ind], + ); + } + for (let [ind, name] of g8ChildNames.entries()) { + expect(stateVariables[name].stateValues.label).eq( + g2ChildLabels[ind], + ); + } + for (let [ind, name] of g9ChildNames.entries()) { + expect(stateVariables[name].stateValues.label).eq( + g3ChildLabels[ind], + ); + } + for (let [ind, name] of g10ChildNames.entries()) { + expect(stateVariables[name].stateValues.label).eq( + g4ChildLabels[ind], + ); + } + for (let [ind, name] of g11ChildNames.entries()) { + expect(stateVariables[name].stateValues.label).eq( + g5ChildLabels[ind], + ); + } + for (let [ind, name] of g12ChildNames.entries()) { + expect(stateVariables[name].stateValues.label).eq( + g6ChildLabels[ind], + ); + } + }); + + it("labelIsName in newNamespace", async () => { + let core = await createTestCore({ + doenetML: ` + + (5,6) + + `, + }); + + let stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/g/P"].stateValues.label).eq("P"); + }); + + it("labelIsName converts case", async () => { + let core = await createTestCore({ + doenetML: ` + + (5,6) + (1,3) + (-2,1) + (7,-5) + (-6,-8) + (9,0) + + `, + }); + + let stateVariables = await returnAllStateVariables(core); + expect(stateVariables["/the_first_point"].stateValues.label).eq( + "the first point", + ); + expect(stateVariables["/the-second-point"].stateValues.label).eq( + "the second point", + ); + expect(stateVariables["/theThirdPoint"].stateValues.label).eq( + "the third point", + ); + expect(stateVariables["/TheFourthPoint"].stateValues.label).eq( + "The Fourth Point", + ); + expect(stateVariables["/the-FIFTH_Point"].stateValues.label).eq( + "the FIFTH Point", + ); + expect(stateVariables["/the_SiXiTH-Point"].stateValues.label).eq( + "the SiXiTH Point", + ); + }); + + it("labelIsName and copies", async () => { + // Note: we add groups just so that we can reference the children + // inside the DoenetML + let core = await createTestCore({ + doenetML: ` + + + + + + + + $A{name="C"} + + + + + + $A{name="E" labelIsName} + + + + + + $A{labelIsName} + + +

+

+

+

+

+

+

+ +

+

+

+

+

+

+

+ `, + }); + + async function check_items({ lA, lD, lE, l6, l7 }) { + const stateVariables = await returnAllStateVariables(core); + const p6Name = + stateVariables["/g6"].activeChildren[0].componentName; + const p7Name = + stateVariables["/g7"].activeChildren[0].componentName; + + expect(stateVariables["/lA"].stateValues.value).eq(lA); + expect(stateVariables["/lB"].stateValues.value).eq(lA); + expect(stateVariables["/lC"].stateValues.value).eq(lA); + expect(stateVariables["/lD"].stateValues.value).eq(lD); + expect(stateVariables["/lE"].stateValues.value).eq(lE); + expect(stateVariables["/lp6"].stateValues.value).eq(l6); + expect(stateVariables["/lp7"].stateValues.value).eq(l7); + + expect(stateVariables["/A"].stateValues.label).eq(lA); + expect(stateVariables["/A"].stateValues.labelForGraph).eq(lA); + expect(stateVariables["/B"].stateValues.label).eq(lA); + expect(stateVariables["/B"].stateValues.labelForGraph).eq(lA); + expect(stateVariables["/C"].stateValues.label).eq(lA); + expect(stateVariables["/C"].stateValues.labelForGraph).eq(lA); + expect(stateVariables["/D"].stateValues.label).eq(lD); + expect(stateVariables["/D"].stateValues.labelForGraph).eq(lD); + expect(stateVariables["/E"].stateValues.label).eq(lE); + expect(stateVariables["/E"].stateValues.labelForGraph).eq(lE); + expect(stateVariables[p6Name].stateValues.label).eq(l6); + expect(stateVariables[p6Name].stateValues.labelForGraph).eq(l6); + expect(stateVariables[p7Name].stateValues.label).eq(l7); + expect(stateVariables[p7Name].stateValues.labelForGraph).eq(l7); + } + + let lA = "A", + lD = "D", + lE = "E", + l6 = "A", + l7 = "A"; + await check_items({ lA, lD, lE, l6, l7 }); + + lA = "F"; + await updateTextInputValue({ text: lA, name: "/tiA", core }); + await check_items({ lA, lD, lE, l6, l7 }); + + lA = "G"; + await updateTextInputValue({ name: "/tiB", text: lA, core }); + await check_items({ lA, lD, lE, l6, l7 }); + + lA = "H"; + await updateTextInputValue({ name: "/tiC", text: lA, core }); + await check_items({ lA, lD, lE, l6, l7 }); + + lD = "I"; + await updateTextInputValue({ name: "/tiD", text: lD, core }); + await check_items({ lA, lD, lE, l6, l7 }); + + lE = "J"; + await updateTextInputValue({ name: "/tiE", text: lE, core }); + await check_items({ lA, lD, lE, l6, l7 }); + + l6 = "K"; + await updateTextInputValue({ name: "/tip6", text: l6, core }); + await check_items({ lA, lD, lE, l6, l7 }); + + l7 = "L"; + await updateTextInputValue({ name: "/tip7", text: l7, core }); + await check_items({ lA, lD, lE, l6, l7 }); + }); + + it("copy and add labelIsName", async () => { + let core = await createTestCore({ + doenetML: ` + + + + + + + + $A{name="C" labelIsName} + + + + + + $A{labelIsName} + + +

+

+

+

+

+ +

+

+

+

+

+`, + }); + + async function check_items({ lA, lB, lC, l4, l5 }) { + const stateVariables = await returnAllStateVariables(core); + const p4Name = + stateVariables["/g4"].activeChildren[0].componentName; + const p5Name = + stateVariables["/g5"].activeChildren[0].componentName; + + expect(stateVariables["/lA"].stateValues.value).eq(lA); + expect(stateVariables["/lB"].stateValues.value).eq(lB); + expect(stateVariables["/lC"].stateValues.value).eq(lC); + expect(stateVariables["/lp4"].stateValues.value).eq(l4); + expect(stateVariables["/lp5"].stateValues.value).eq(l5); + + expect(stateVariables["/A"].stateValues.label).eq(lA); + expect(stateVariables["/A"].stateValues.labelForGraph).eq(lA); + expect(stateVariables["/B"].stateValues.label).eq(lB); + expect(stateVariables["/B"].stateValues.labelForGraph).eq(lB); + expect(stateVariables["/C"].stateValues.label).eq(lC); + expect(stateVariables["/C"].stateValues.labelForGraph).eq(lC); + expect(stateVariables[p4Name].stateValues.label).eq(l4); + expect(stateVariables[p4Name].stateValues.labelForGraph).eq(l4); + expect(stateVariables[p5Name].stateValues.label).eq(l5); + expect(stateVariables[p5Name].stateValues.labelForGraph).eq(l5); + } + + let lA = "", + lB = "B", + lC = "C", + l4 = "A", + l5 = "A"; + await check_items({ lA, lB, lC, l4, l5 }); + + lA = "F"; + await updateTextInputValue({ text: lA, name: "/tiA", core }); + await check_items({ lA, lB, lC, l4, l5 }); + + lB = "G"; + await updateTextInputValue({ name: "/tiB", text: lB, core }); + await check_items({ lA, lB, lC, l4, l5 }); + + lC = "H"; + await updateTextInputValue({ name: "/tiC", text: lC, core }); + await check_items({ lA, lB, lC, l4, l5 }); + + l4 = "I"; + await updateTextInputValue({ name: "/tip4", text: l4, core }); + await check_items({ lA, lB, lC, l4, l5 }); + + l5 = "J"; + await updateTextInputValue({ name: "/tip5", text: l5, core }); + await check_items({ lA, lB, lC, l4, l5 }); + }); + + async function test_label_labelIsName_copies( + doenetMLForA: string, + useLabelCopies = false, + ) { + const labelD = useLabelCopies + ? `