diff --git a/cypress/e2e/ActivityViewer/activityVariants.cy.js b/cypress/e2e/ActivityViewer/activityVariants.cy.js index b21e7b9529..448677502d 100644 --- a/cypress/e2e/ActivityViewer/activityVariants.cy.js +++ b/cypress/e2e/ActivityViewer/activityVariants.cy.js @@ -13,27 +13,25 @@ describe("Activity variants tests", function () { cy.wait(100); cy.get("#testRunner_toggleControls").click(); - let activityDefinition; + let doenetML; let attemptNumber = 0; for (let ind = 1; ind <= 200; ind += 97) { cy.window().then(async (win) => { attemptNumber++; - activityDefinition = ` + doenetML = ` - - - ${attemptNumber} - Enter : - $n - - + + ${attemptNumber} + Enter : + $n + `; win.postMessage( { - activityDefinition, + doenetML, requestedVariantIndex: ind, attemptNumber, }, @@ -46,7 +44,7 @@ describe("Activity variants tests", function () { let activityData = win.returnActivityData(); expect(activityData.requestedVariantIndex).eq(ind); - expect(activityData.variantIndex).eq(((ind - 1) % 1000) + 1); + expect(activityData.variantIndex).eq(((ind - 1) % 100) + 1); expect(activityData.variantsByPage).eqls([((ind - 1) % 100) + 1]); let stateVariables = await win.returnAllStateVariables1(); @@ -82,7 +80,7 @@ describe("Activity variants tests", function () { cy.window().then(async (win) => { win.postMessage( { - activityDefinition, + doenetML, requestedVariantIndex: ind, attemptNumber, }, @@ -121,38 +119,36 @@ describe("Activity variants tests", function () { cy.wait(100); cy.get("#testRunner_toggleControls").click(); - let activityDefinition = ` + let doenetML = ` - - - - a - Enter - - : - $color - - + + + a + Enter + + : + $color + `; for (let ind = 1; ind <= 4; ind++) { if (ind > 1) { + cy.reload(); cy.get("#testRunner_toggleControls").click(); cy.get("#testRunner_newAttempt").click(); cy.wait(100); cy.get("#testRunner_toggleControls").click(); - cy.reload(); } cy.window().then(async (win) => { win.postMessage( { - activityDefinition, + doenetML, requestedVariantIndex: ind, }, "*", @@ -167,7 +163,7 @@ describe("Activity variants tests", function () { let activityData = win.returnActivityData(); expect(activityData.requestedVariantIndex).eq(ind); - expect(activityData.variantIndex).eq(((ind - 1) % 100) + 1); + expect(activityData.variantIndex).eq(((ind - 1) % 3) + 1); expect(activityData.variantsByPage).eqls([((ind - 1) % 3) + 1]); let stateVariables = await win.returnAllStateVariables1(); @@ -198,7 +194,7 @@ describe("Activity variants tests", function () { cy.window().then(async (win) => { win.postMessage( { - activityDefinition, + doenetML, requestedVariantIndex: 1, }, "*", @@ -237,40 +233,38 @@ describe("Activity variants tests", function () { cy.wait(100); cy.get("#testRunner_toggleControls").click(); - let activityDefinition = ` + let doenetML = ` - - - - - a - Enter - - : - $color - - - + + + + a + Enter + + : + $color + + `; for (let ind = 1; ind <= 4; ind++) { if (ind > 1) { + cy.reload(); cy.get("#testRunner_toggleControls").click(); cy.get("#testRunner_newAttempt").click(); cy.wait(100); cy.get("#testRunner_toggleControls").click(); - cy.reload(); } cy.window().then(async (win) => { win.postMessage( { - activityDefinition, + doenetML, requestedVariantIndex: ind, }, "*", @@ -285,7 +279,7 @@ describe("Activity variants tests", function () { let activityData = win.returnActivityData(); expect(activityData.requestedVariantIndex).eq(ind); - expect(activityData.variantIndex).eq(((ind - 1) % 1000) + 1); + expect(activityData.variantIndex).eq(((ind - 1) % 3) + 1); expect(activityData.variantsByPage).eqls([((ind - 1) % 3) + 1]); let stateVariables = await win.returnAllStateVariables1(); @@ -316,7 +310,7 @@ describe("Activity variants tests", function () { cy.window().then(async (win) => { win.postMessage( { - activityDefinition, + doenetML, requestedVariantIndex: 1, }, "*", @@ -350,20 +344,18 @@ describe("Activity variants tests", function () { }); it("Two pages few variants, page variants enumerated", () => { - let activityDefinition = ` + let doenetML = ` - - - a - - - - - b - - - - + + a + + + + + b + + + `; @@ -375,7 +367,7 @@ describe("Activity variants tests", function () { cy.window().then(async (win) => { win.postMessage( { - activityDefinition, + doenetML, requestedVariantIndex: ind, }, "*", @@ -392,7 +384,7 @@ describe("Activity variants tests", function () { let activityData = win.returnActivityData(); expect(activityData.requestedVariantIndex).eq(ind); - expect(activityData.variantIndex).eq(((ind - 1) % 1000) + 1); + expect(activityData.variantIndex).eq(((ind - 1) % 6) + 1); expect(activityData.variantsByPage.length).eq(2); expect(activityData.variantsByPage[0]).eq( ((activityData.variantIndex - 1) % 2) + 1, @@ -422,43 +414,41 @@ describe("Activity variants tests", function () { } }); - it("Two pages, numberOfVariants not specified, defaults to 1000", () => { + it("Two pages, numVariants not specified, defaults to 1000", () => { cy.get("#testRunner_toggleControls").click(); cy.get("#testRunner_allowLocalState").click(); cy.wait(100); cy.get("#testRunner_toggleControls").click(); - let activityDefinition = ` + let doenetML = ` - - - a - Enter : - $n - - - b - - Enter : - $l - - + + a + Enter : + $n + + + b + + Enter : + $l + `; for (let ind = 1; ind <= 2000; ind += 970) { if (ind > 1) { + cy.reload(); cy.get("#testRunner_toggleControls").click(); cy.get("#testRunner_newAttempt").click(); cy.wait(100); cy.get("#testRunner_toggleControls").click(); - cy.reload(); } cy.window().then(async (win) => { win.postMessage( { - activityDefinition, + doenetML, requestedVariantIndex: ind, }, "*", @@ -551,7 +541,7 @@ describe("Activity variants tests", function () { cy.window().then(async (win) => { win.postMessage( { - activityDefinition, + doenetML, requestedVariantIndex: 1, }, "*", @@ -643,22 +633,20 @@ describe("Activity variants tests", function () { } }); - it("Two pages, specify numberOfVariants is 2", () => { - let activityDefinition = ` - - - - a - Enter : - $n - - - b - - Enter : - $l - - + it("Two pages, specify numVariants is 2", () => { + let doenetML = ` + + + a + Enter : + $n + + + b + + Enter : + $l + `; @@ -673,7 +661,7 @@ describe("Activity variants tests", function () { cy.window().then(async (win) => { win.postMessage( { - activityDefinition, + doenetML, requestedVariantIndex: ind, }, "*", @@ -776,8 +764,8 @@ describe("Activity variants tests", function () { } }); - it("Shuffle and select orders, numberOfVariants not specified", () => { - let activityDefinition = ` + it("Shuffle and select orders, numVariants not specified", () => { + let doenetML = ` @@ -811,7 +799,7 @@ describe("Activity variants tests", function () { cy.window().then(async (win) => { win.postMessage( { - activityDefinition, + doenetML, requestedVariantIndex: ind, }, "*", @@ -893,20 +881,18 @@ describe("Activity variants tests", function () { }); it("Two pages, unique variants, variants to exclude", () => { - let activityDefinition = ` + let doenetML = ` - - - a - - - - - b - - - - + + a + + + + + b + + + `; @@ -918,7 +904,7 @@ describe("Activity variants tests", function () { cy.window().then(async (win) => { win.postMessage( { - activityDefinition, + doenetML, requestedVariantIndex: ind, }, "*", @@ -937,10 +923,8 @@ describe("Activity variants tests", function () { cy.window().then(async (win) => { let activityData = win.returnActivityData(); - console.log("activityData", activityData); - expect(activityData.requestedVariantIndex).eq(ind); - expect(activityData.variantIndex).eq(((ind - 1) % 1000) + 1); + expect(activityData.variantIndex).eq(((ind - 1) % 6) + 1); expect(activityData.variantsByPage.length).eq(2); expect(activityData.variantsByPage[0]).eq( ((activityData.variantIndex - 1) % 2) + 1, @@ -971,20 +955,18 @@ describe("Activity variants tests", function () { }); it("Two pages, unique variants, variants to include", () => { - let activityDefinition = ` + let doenetML = ` - - - a - - - - - b - - - - + + a + + + + + b + + + `; @@ -996,7 +978,7 @@ describe("Activity variants tests", function () { cy.window().then(async (win) => { win.postMessage( { - activityDefinition, + doenetML, requestedVariantIndex: ind, }, "*", @@ -1015,10 +997,8 @@ describe("Activity variants tests", function () { cy.window().then(async (win) => { let activityData = win.returnActivityData(); - console.log("activityData", activityData); - expect(activityData.requestedVariantIndex).eq(ind); - expect(activityData.variantIndex).eq(((ind - 1) % 1000) + 1); + expect(activityData.variantIndex).eq(((ind - 1) % 6) + 1); expect(activityData.variantsByPage.length).eq(2); expect(activityData.variantsByPage[0]).eq( ((activityData.variantIndex - 1) % 2) + 1, @@ -1049,34 +1029,32 @@ describe("Activity variants tests", function () { }); it("Two pages, named variants, variants to exclude", () => { - let activityDefinition = ` + let doenetML = ` - - - a - - - - - b - - - - + + a + + + + + b + + + `; @@ -1088,7 +1066,7 @@ describe("Activity variants tests", function () { cy.window().then(async (win) => { win.postMessage( { - activityDefinition, + doenetML, requestedVariantIndex: ind, }, "*", @@ -1108,7 +1086,7 @@ describe("Activity variants tests", function () { let activityData = win.returnActivityData(); expect(activityData.requestedVariantIndex).eq(ind); - expect(activityData.variantIndex).eq(((ind - 1) % 1000) + 1); + expect(activityData.variantIndex).eq(((ind - 1) % 6) + 1); expect(activityData.variantsByPage.length).eq(2); expect(activityData.variantsByPage[0]).eq( ((activityData.variantIndex - 1) % 2) + 1, @@ -1139,34 +1117,32 @@ describe("Activity variants tests", function () { }); it("Two pages, named variants, variants to include", () => { - let activityDefinition = ` + let doenetML = ` - - - a - - - - - b - - - - + + a + + + + + b + + + `; @@ -1178,7 +1154,7 @@ describe("Activity variants tests", function () { cy.window().then(async (win) => { win.postMessage( { - activityDefinition, + doenetML, requestedVariantIndex: ind, }, "*", @@ -1198,7 +1174,7 @@ describe("Activity variants tests", function () { let activityData = win.returnActivityData(); expect(activityData.requestedVariantIndex).eq(ind); - expect(activityData.variantIndex).eq(((ind - 1) % 1000) + 1); + expect(activityData.variantIndex).eq(((ind - 1) % 6) + 1); expect(activityData.variantsByPage.length).eq(2); expect(activityData.variantsByPage[0]).eq( ((activityData.variantIndex - 1) % 2) + 1, @@ -1228,31 +1204,29 @@ describe("Activity variants tests", function () { } }); - it("Two pages, variants from problem, variant to exclude", () => { - let activityDefinition = ` + it("Two pages, variants from problem, variants to exclude", () => { + let doenetML = ` - - - - a - - - - - - - b - - - - - + + + a + + + + + + + b + + + + `; @@ -1264,7 +1238,7 @@ describe("Activity variants tests", function () { cy.window().then(async (win) => { win.postMessage( { - activityDefinition, + doenetML, requestedVariantIndex: ind, }, "*", @@ -1284,7 +1258,7 @@ describe("Activity variants tests", function () { let activityData = win.returnActivityData(); expect(activityData.requestedVariantIndex).eq(ind); - expect(activityData.variantIndex).eq(((ind - 1) % 1000) + 1); + expect(activityData.variantIndex).eq(((ind - 1) % 6) + 1); expect(activityData.variantsByPage.length).eq(2); expect(activityData.variantsByPage[0]).eq( ((activityData.variantIndex - 1) % 2) + 1, @@ -1314,31 +1288,29 @@ describe("Activity variants tests", function () { } }); - it("Two pages, variants from problem, variant to include", () => { - let activityDefinition = ` + it("Two pages, variants from problem, variants to include", () => { + let doenetML = ` - - - - a - - - - - - - b - - - - - + + + a + + + + + + + b + + + + `; @@ -1350,7 +1322,7 @@ describe("Activity variants tests", function () { cy.window().then(async (win) => { win.postMessage( { - activityDefinition, + doenetML, requestedVariantIndex: ind, }, "*", @@ -1370,7 +1342,7 @@ describe("Activity variants tests", function () { let activityData = win.returnActivityData(); expect(activityData.requestedVariantIndex).eq(ind); - expect(activityData.variantIndex).eq(((ind - 1) % 1000) + 1); + expect(activityData.variantIndex).eq(((ind - 1) % 6) + 1); expect(activityData.variantsByPage.length).eq(2); expect(activityData.variantsByPage[0]).eq( ((activityData.variantIndex - 1) % 2) + 1, @@ -1401,32 +1373,30 @@ describe("Activity variants tests", function () { }); it("Two pages, variants from document and problem, variants to exclude in problem", () => { - let activityDefinition = ` + let doenetML = ` - - - - - a - - - - - - - - b - - - - - + + + + a + + + + + + + + b + + + + `; @@ -1438,7 +1408,7 @@ describe("Activity variants tests", function () { cy.window().then(async (win) => { win.postMessage( { - activityDefinition, + doenetML, requestedVariantIndex: ind, }, "*", @@ -1458,7 +1428,7 @@ describe("Activity variants tests", function () { let activityData = win.returnActivityData(); expect(activityData.requestedVariantIndex).eq(ind); - expect(activityData.variantIndex).eq(((ind - 1) % 1000) + 1); + expect(activityData.variantIndex).eq(((ind - 1) % 6) + 1); expect(activityData.variantsByPage.length).eq(2); expect(activityData.variantsByPage[0]).eq( ((activityData.variantIndex - 1) % 2) + 1, @@ -1489,32 +1459,30 @@ describe("Activity variants tests", function () { }); it("Two pages, variants from document and problem, variants to include in problem", () => { - let activityDefinition = ` + let doenetML = ` - - - - - a - - - - - - - - b - - - - - + + + + a + + + + + + + + b + + + + `; @@ -1526,7 +1494,7 @@ describe("Activity variants tests", function () { cy.window().then(async (win) => { win.postMessage( { - activityDefinition, + doenetML, requestedVariantIndex: ind, }, "*", @@ -1546,7 +1514,7 @@ describe("Activity variants tests", function () { let activityData = win.returnActivityData(); expect(activityData.requestedVariantIndex).eq(ind); - expect(activityData.variantIndex).eq(((ind - 1) % 1000) + 1); + expect(activityData.variantIndex).eq(((ind - 1) % 6) + 1); expect(activityData.variantsByPage.length).eq(2); expect(activityData.variantsByPage[0]).eq( ((activityData.variantIndex - 1) % 2) + 1, @@ -1577,32 +1545,30 @@ describe("Activity variants tests", function () { }); it("Two pages, variants from document and problem, variant to exclude in document and problem", () => { - let activityDefinition = ` + let doenetML = ` - - - - - a - - - - - - - - b - - - - - + + + + a + + + + + + + + b + + + + `; @@ -1614,7 +1580,7 @@ describe("Activity variants tests", function () { cy.window().then(async (win) => { win.postMessage( { - activityDefinition, + doenetML, requestedVariantIndex: ind, }, "*", @@ -1636,7 +1602,7 @@ describe("Activity variants tests", function () { let activityData = win.returnActivityData(); expect(activityData.requestedVariantIndex).eq(ind); - expect(activityData.variantIndex).eq(((ind - 1) % 1000) + 1); + expect(activityData.variantIndex).eq(((ind - 1) % 6) + 1); expect(activityData.variantsByPage.length).eq(2); expect(activityData.variantsByPage[0]).eq( ((activityData.variantIndex - 1) % 2) + 1, @@ -1673,32 +1639,30 @@ describe("Activity variants tests", function () { }); it("Two pages, variants from document and problem, variant to include in document and problem", () => { - let activityDefinition = ` + let doenetML = ` - - - - - a - - - - - - - - b - - - - - + + + + a + + + + + + + + b + + + + `; @@ -1710,7 +1674,7 @@ describe("Activity variants tests", function () { cy.window().then(async (win) => { win.postMessage( { - activityDefinition, + doenetML, requestedVariantIndex: ind, }, "*", @@ -1732,7 +1696,7 @@ describe("Activity variants tests", function () { let activityData = win.returnActivityData(); expect(activityData.requestedVariantIndex).eq(ind); - expect(activityData.variantIndex).eq(((ind - 1) % 1000) + 1); + expect(activityData.variantIndex).eq(((ind - 1) % 6) + 1); expect(activityData.variantsByPage.length).eq(2); expect(activityData.variantsByPage[0]).eq( ((activityData.variantIndex - 1) % 2) + 1, diff --git a/cypress/e2e/ActivityViewer/compiledActivity.cy.js b/cypress/e2e/ActivityViewer/compiledActivity.cy.js index 96cc42ef82..d803ef8209 100644 --- a/cypress/e2e/ActivityViewer/compiledActivity.cy.js +++ b/cypress/e2e/ActivityViewer/compiledActivity.cy.js @@ -9,15 +9,12 @@ describe("Compiled activity tests", function () { it("Minimal activity definition", () => { // Note: // - missing xmlns attribute on activity document - // - missing behavior attribute on order (sequence is assumed) cy.window().then(async (win) => { win.postMessage( { - activityDefinition: ` + doenetML: ` - - hi - + hi `, @@ -29,15 +26,13 @@ describe("Compiled activity tests", function () { cy.get(cesc("#\\/_document1")).should("contain.text", "hi"); }); - it("Minimal activity definition, with attributes", () => { + it("Minimal activity definition, with xmlns", () => { cy.window().then(async (win) => { win.postMessage( { - activityDefinition: ` + doenetML: ` - - hi - + hi `, @@ -55,7 +50,7 @@ describe("Compiled activity tests", function () { cy.window().then(async (win) => { win.postMessage( { - activityDefinition: ` + doenetML: ` hi `, }, @@ -70,7 +65,7 @@ describe("Compiled activity tests", function () { cy.window().then(async (win) => { win.postMessage( { - activityDefinition: ` + doenetML: ` hi `, }, diff --git a/cypress/e2e/ActivityViewer/relationshipsAmongPages.cy.js b/cypress/e2e/ActivityViewer/relationshipsAmongPages.cy.js index 301c628acf..28a283252b 100644 --- a/cypress/e2e/ActivityViewer/relationshipsAmongPages.cy.js +++ b/cypress/e2e/ActivityViewer/relationshipsAmongPages.cy.js @@ -7,49 +7,47 @@ describe("Relationships among pages tests", function () { }); it("Problem numbering continues across pages", () => { - let activityDefinition = ` + let doenetML = ` - - - -

The first problem

-
-
- - -

The second problem

-
-
- - -

The third problem

-
- -

The fourth problem

-
-
- - -

The fifth problem

-
- - Named problem -

The sixth problem

-
-
- - -

The seventh problem

-
-
-
+ + +

The first problem

+
+
+ + +

The second problem

+
+
+ + +

The third problem

+
+ +

The fourth problem

+
+
+ + +

The fifth problem

+
+ + Named problem +

The sixth problem

+
+
+ + +

The seventh problem

+
+
`; cy.window().then(async (win) => { win.postMessage( { - activityDefinition, + doenetML, }, "*", ); @@ -160,7 +158,7 @@ describe("Relationships among pages tests", function () { cy.window().then(async (win) => { win.postMessage( { - activityDefinition, + doenetML, }, "*", ); @@ -194,67 +192,65 @@ describe("Relationships among pages tests", function () { }); it("Section numbering continues across pages", () => { - let activityDefinition = ` + let doenetML = ` - - -
-

The first section

-
-
- -
-

The second section

- -

First subsection of second section

-
- -

Second subsection of second section

-
-
-
- -
-

The third section

-
-
-

The fourth section

- -

First subsection of fourth section

-
- -

Second subsection of fourth section

-
-
-
- -
-

The fifth section

-
-
- Named section -

The sixth section

- -

First subsection of sixth section

-
- -

Second subsection of sixth section

-
-
-
- -
-

The seventh section

-
-
-
+ +
+

The first section

+
+
+ +
+

The second section

+ +

First subsection of second section

+
+ +

Second subsection of second section

+
+
+
+ +
+

The third section

+
+
+

The fourth section

+ +

First subsection of fourth section

+
+ +

Second subsection of fourth section

+
+
+
+ +
+

The fifth section

+
+
+ Named section +

The sixth section

+ +

First subsection of sixth section

+
+ +

Second subsection of sixth section

+
+
+
+ +
+

The seventh section

+
+
`; cy.window().then(async (win) => { win.postMessage( { - activityDefinition, + doenetML, }, "*", ); @@ -365,7 +361,7 @@ describe("Relationships among pages tests", function () { cy.window().then(async (win) => { win.postMessage( { - activityDefinition, + doenetML, }, "*", ); @@ -400,51 +396,49 @@ describe("Relationships among pages tests", function () { }); it("Links across pages", () => { - let activityDefinition = ` + let doenetML = ` - - -
-

Link to paragraph above aside

-

Link to aside

-

Link to page 2

-

Link to paragraph above page 2 aside

-

Link to page 2 aside

-

Checkbox to make it save state:

- - -

Paragraph above aside

- - - - -
-
- -
-

Link to paragraph above aside

-

Link to aside

-

Link to page 1

-

Link to paragraph above page 1 aside

-

Link to page 1 aside

-

Checkbox to make it save state:

- - - -

Paragraph above aside

- - - - -
-
-
+ +
+

Link to paragraph above aside

+

Link to aside

+

Link to page 2

+

Link to paragraph above page 2 aside

+

Link to page 2 aside

+

Checkbox to make it save state:

+ + +

Paragraph above aside

+ + + + +
+
+ +
+

Link to paragraph above aside

+

Link to aside

+

Link to page 1

+

Link to paragraph above page 1 aside

+

Link to page 1 aside

+

Checkbox to make it save state:

+ + + +

Paragraph above aside

+ + + + +
+
`; @@ -456,7 +450,7 @@ describe("Relationships among pages tests", function () { cy.window().then(async (win) => { win.postMessage( { - activityDefinition, + doenetML, }, "*", ); @@ -543,7 +537,7 @@ describe("Relationships among pages tests", function () { cy.window().then(async (win) => { win.postMessage( { - activityDefinition, + doenetML, }, "*", ); @@ -596,7 +590,7 @@ describe("Relationships among pages tests", function () { cy.window().then(async (win) => { win.postMessage( { - activityDefinition, + doenetML, }, "*", ); @@ -637,7 +631,7 @@ describe("Relationships among pages tests", function () { cy.window().then(async (win) => { win.postMessage( { - activityDefinition, + doenetML, }, "*", ); diff --git a/cypress/e2e/AsStudent/signIn.cy.js b/cypress/e2e/AsStudent/signIn.cy.js index 30e271ad1d..65f3f19852 100644 --- a/cypress/e2e/AsStudent/signIn.cy.js +++ b/cypress/e2e/AsStudent/signIn.cy.js @@ -44,7 +44,13 @@ describe("Student Sign-In Test", function () { const code = result[0].signInCode; cy.get('[data-test="signinCodeInput"]').type(code); cy.get('[data-test="signInButton"]').click(); - cy.get('[data-test="Nav to course"]').should("be.visible"); + cy.get('[data-test="My Courses"]').click(); + cy.get('[data-test="Course Label"]').should( + "have.text", + "Cypress Generated", + ); + cy.get('[data-test="Card Image Link"]').click(); + cy.document().should("contain.text", "Welcome"); }); }); }); diff --git a/cypress/e2e/AssignedActivity/multipageActivities.cy.js b/cypress/e2e/AssignedActivity/multipageActivities.cy.js index 74e72ee2e1..b3159dc476 100644 --- a/cypress/e2e/AssignedActivity/multipageActivities.cy.js +++ b/cypress/e2e/AssignedActivity/multipageActivities.cy.js @@ -97,6 +97,8 @@ describe("Multipage activity tests", function () { cy.get('[data-test="Assign Activity"]').click(); cy.get('[data-test="Unassign Activity"]').should("be.visible"); + cy.wait(100); //TODO: is there a reason we need to wait before clicking paginate? + cy.get('[data-test="Paginate"').click(); cy.wait(100); //TODO: need the UI to let us know this was successful diff --git a/cypress/e2e/AssignedActivity/singlepageActivities.cy.js b/cypress/e2e/AssignedActivity/singlepageActivities.cy.js index b6a05ab869..efd9446aa5 100644 --- a/cypress/e2e/AssignedActivity/singlepageActivities.cy.js +++ b/cypress/e2e/AssignedActivity/singlepageActivities.cy.js @@ -526,14 +526,12 @@ describe("Single page activity tests", function () { cy.url().should("match", /#\\\/insideAside$/); cy.url().should("contain", doenetId); - cy.wait(200); - - cy.get(cesc("#\\/insideAside")).then((el) => { - let rect = el[0].getBoundingClientRect(); - expect(rect.top) - .gt(headerPixels - 1) - .lt(headerPixels + 1); - }); + cy.waitUntil(() => + cy.get(cesc("#\\/insideAside")).then((el) => { + let rect = el[0].getBoundingClientRect(); + return rect.top > headerPixels - 1 && rect.top < headerPixels + 1; + }), + ); cy.wait(1500); // wait for debounce @@ -569,14 +567,12 @@ describe("Single page activity tests", function () { cy.url().should("match", /#\\\/insideAside$/); cy.url().should("contain", doenetId); - cy.wait(200); - - cy.get(cesc("#\\/insideAside")).then((el) => { - let rect = el[0].getBoundingClientRect(); - expect(rect.top) - .gt(headerPixels - 1) - .lt(headerPixels + 1); - }); + cy.waitUntil(() => + cy.get(cesc("#\\/insideAside")).then((el) => { + let rect = el[0].getBoundingClientRect(); + return rect.top > headerPixels - 1 && rect.top < headerPixels + 1; + }), + ); }); it("Go directly to URLs of activity", () => { @@ -634,12 +630,12 @@ describe("Single page activity tests", function () { cy.url().should("match", /#\\\/aside$/); cy.url().should("contain", doenetId); - cy.get(cesc("#\\/aside")).then((el) => { - let rect = el[0].getBoundingClientRect(); - expect(rect.top) - .gt(headerPixels - 1) - .lt(headerPixels + 1); - }); + cy.waitUntil(() => + cy.get(cesc("#\\/aside")).then((el) => { + let rect = el[0].getBoundingClientRect(); + return rect.top > headerPixels - 1 && rect.top < headerPixels + 1; + }), + ); }); it("Update to new version, infinite attempts allowed, separate student signin", () => { @@ -686,9 +682,11 @@ describe("Single page activity tests", function () { cy.get(".navigationRow").eq(0).find(".navigationColumn1").click(); cy.get('[data-test="View Activity"]').click(); - cy.get(cesc("#\\/ans") + " textarea").type("{end}{backspace}1{enter}", { - force: true, - }); + cy.get(cesc("#\\/ans") + " textarea") + .type("{end}{backspace}1{enter}", { + force: true, + }) + .then((x) => console.log("we typed in the 1")); cy.log( "At least for now, hitting enter before core is intialized does not submit response", @@ -702,6 +700,10 @@ describe("Single page activity tests", function () { cy.go("back"); + // Have to wait to make sure Core has saved the changes to continue + // TODO: ideally wouldn't have to wait here + cy.wait(1000); + cy.signin({ userId }); cy.visit(`/course?tool=navigation&courseId=${courseId}`); @@ -1890,6 +1892,7 @@ describe("Single page activity tests", function () { $ti=hello

+ $ti.value, $ti.immediateValue `; cy.createActivity({ diff --git a/cypress/e2e/DoenetML/tagSpecific/choiceinput.cy.js b/cypress/e2e/DoenetML/tagSpecific/choiceinput.cy.js index 05c6690b37..3ce032021d 100644 --- a/cypress/e2e/DoenetML/tagSpecific/choiceinput.cy.js +++ b/cypress/e2e/DoenetML/tagSpecific/choiceinput.cy.js @@ -1094,13 +1094,13 @@ describe("ChoiceInput Tag Tests", function () { cy.log("select cat from first input"); selectedChoices = [" dog ", "caT"]; selectedChoices.sort((a, b) => choices.indexOf(a) - choices.indexOf(b)); - selectedIndex = choices.indexOf(selectedChoices[0]) + 1; + selectedIndex = choices.indexOf("caT") + 1; inputText = selectedChoices.join(", "); cy.get(cesc(`#\\/ci1_choice${selectedIndex}_input`)).click(); checkChoices(selectedChoices, inputText); cy.log("deselect dog from first input"); - selectedIndex = choices.indexOf(selectedChoices[1]) + 1; + selectedIndex = choices.indexOf(" dog ") + 1; selectedChoices = ["caT"]; inputText = selectedChoices.join(", "); cy.get(cesc(`#\\/ci1_choice${selectedIndex}_input`)).click(); diff --git a/cypress/e2e/DoenetML/tagSpecific/codeeditor.cy.js b/cypress/e2e/DoenetML/tagSpecific/codeeditor.cy.js index fe6baf0d7b..23755e53aa 100644 --- a/cypress/e2e/DoenetML/tagSpecific/codeeditor.cy.js +++ b/cypress/e2e/DoenetML/tagSpecific/codeeditor.cy.js @@ -120,10 +120,7 @@ describe("Code Editor Tag Tests", function () { stateVariables["/_codeeditor1"].activeChildren[0].componentName; let updateAnchor = "#" + cesc2(viewerName) + "_updateButton"; - let rendererName = - stateVariables[viewerName].activeChildren[0].componentName; - - cy.get(cesc2(`#${rendererName}/_document1`)).should("exist"); + cy.get(cesc2(`#${viewerName}/_document1`)).should("exist"); cy.get(cesc("#\\/_p1")).should("have.text", ""); cy.get(cesc("#\\/_p2")).should("have.text", ""); @@ -143,8 +140,8 @@ describe("Code Editor Tag Tests", function () { cy.get(cesc("#\\/_p1")).should("have.text", "

Hello!

"); cy.get(cesc("#\\/_p2")).should("have.text", ""); - cy.get(cesc2(`#${rendererName}/_document1`)).should("exist"); - cy.get(cesc2(`#${rendererName}/_p1`)).should("not.exist"); + cy.get(cesc2(`#${viewerName}/_document1`)).should("exist"); + cy.get(cesc2(`#${viewerName}/_p1`)).should("not.exist"); cy.window().then(async (win) => { let stateVariables = await win.returnAllStateVariables1(); @@ -159,8 +156,8 @@ describe("Code Editor Tag Tests", function () { cy.get(cesc("#\\/_p1")).should("have.text", "

Hello!

"); cy.get(cesc("#\\/_p2")).should("have.text", "

Hello!

"); - cy.get(cesc2(`#${rendererName}/_document1`)).should("exist"); - cy.get(cesc2(`#${rendererName}/_p1`)).should("not.exist"); + cy.get(cesc2(`#${viewerName}/_document1`)).should("exist"); + cy.get(cesc2(`#${viewerName}/_p1`)).should("not.exist"); cy.window().then(async (win) => { let stateVariables = await win.returnAllStateVariables1(); @@ -175,7 +172,7 @@ describe("Code Editor Tag Tests", function () { cy.log("click to update content"); cy.get(updateAnchor).click(); - cy.get(cesc2(`#${rendererName}/_p1`)).should("have.text", "Hello!"); + cy.get(cesc2(`#${viewerName}/_p1`)).should("have.text", "Hello!"); cy.get(cesc("#\\/_p1")).should("have.text", "

Hello!

"); cy.get(cesc("#\\/_p2")).should("have.text", "

Hello!

"); @@ -201,8 +198,8 @@ describe("Code Editor Tag Tests", function () { ); cy.get(cesc("#\\/_p2")).should("have.text", "

Hello!

"); - cy.get(cesc2(`#${rendererName}/_p1`)).should("have.text", "Hello!"); - cy.get(cesc2(`#${rendererName}/_p2`)).should("not.exist"); + cy.get(cesc2(`#${viewerName}/_p1`)).should("have.text", "Hello!"); + cy.get(cesc2(`#${viewerName}/_p2`)).should("not.exist"); cy.window().then(async (win) => { let stateVariables = await win.returnAllStateVariables1(); @@ -224,8 +221,8 @@ describe("Code Editor Tag Tests", function () { "

Hello!

\n

1+1

", ); - cy.get(cesc2(`#${rendererName}/_p1`)).should("have.text", "Hello!"); - cy.get(cesc2(`#${rendererName}/_p2`)).should("not.exist"); + cy.get(cesc2(`#${viewerName}/_p1`)).should("have.text", "Hello!"); + cy.get(cesc2(`#${viewerName}/_p2`)).should("not.exist"); cy.window().then(async (win) => { let stateVariables = await win.returnAllStateVariables1(); @@ -240,7 +237,7 @@ describe("Code Editor Tag Tests", function () { cy.log("click to update content"); cy.get(updateAnchor).click(); - cy.get(cesc2(`#${rendererName}/_p2`)).should("contain.text", "2"); + cy.get(cesc2(`#${viewerName}/_p2`)).should("contain.text", "2"); cy.get(cesc("#\\/_p1")).should( "have.text", @@ -251,8 +248,8 @@ describe("Code Editor Tag Tests", function () { "

Hello!

\n

1+1

", ); - cy.get(cesc2(`#${rendererName}/_p1`)).should("have.text", "Hello!"); - cy.get(cesc2(`#${rendererName}/_p2`) + " .mjx-mrow") + cy.get(cesc2(`#${viewerName}/_p1`)).should("have.text", "Hello!"); + cy.get(cesc2(`#${viewerName}/_p2`) + " .mjx-mrow") .eq(0) .should("have.text", "2"); @@ -1483,8 +1480,6 @@ describe("Code Editor Tag Tests", function () { cy.get(cesc("#\\/_codeeditor1") + " .cm-content").type("{end}{enter}"); cy.get(updateAnchor).click(); - cy.get(updateAnchor).click(); - cy.get(cesc2("#/_p1")).should( "have.text", "

Hello!

\n$ti\n", @@ -2143,7 +2138,7 @@ describe("Code Editor Tag Tests", function () { // to wait for page to load cy.get(cesc2("#/_text1")).should("have.text", "a"); - cy.log("Have only one variant despite selectFromSequence child") + cy.log("Have only one variant despite selectFromSequence child"); cy.window().then(async (win) => { let stateVariables = await win.returnAllStateVariables1(); diff --git a/cypress/e2e/DoenetML/tagSpecific/collect.cy.js b/cypress/e2e/DoenetML/tagSpecific/collect.cy.js index cd1823d55c..9e590e099e 100644 --- a/cypress/e2e/DoenetML/tagSpecific/collect.cy.js +++ b/cypress/e2e/DoenetML/tagSpecific/collect.cy.js @@ -3647,7 +3647,7 @@ describe("Collect Tag Tests", function () { let stateVariables = await win.returnAllStateVariables1(); let group1Replacements = stateVariables[ "/_document1" - ].activeChildren.slice(3, 16); + ].activeChildren.slice(2, 15); let collect1Replacements = stateVariables["/pcollect1"].activeChildren; let collect2Replacements = stateVariables["/pcollect2"].activeChildren; let group2Replacements = stateVariables["/pgroup2"].activeChildren; diff --git a/cypress/e2e/DoenetML/tagSpecific/conditionalcontent.cy.js b/cypress/e2e/DoenetML/tagSpecific/conditionalcontent.cy.js index 973475d1b5..9312b67c10 100644 --- a/cypress/e2e/DoenetML/tagSpecific/conditionalcontent.cy.js +++ b/cypress/e2e/DoenetML/tagSpecific/conditionalcontent.cy.js @@ -3368,7 +3368,7 @@ describe("Conditional Content Tag Tests", function () { cy.get(cesc("#\\/_document1")).should( "contain.text", - "\n a\n 1\n before\n nothing: \n after\n ", + "a\n 1\n before\n nothing: \n after\n ", ); cy.window().then(async (win) => { diff --git a/cypress/e2e/DoenetML/tagSpecific/copy.cy.js b/cypress/e2e/DoenetML/tagSpecific/copy.cy.js index 6be0432d66..24ec85963b 100644 --- a/cypress/e2e/DoenetML/tagSpecific/copy.cy.js +++ b/cypress/e2e/DoenetML/tagSpecific/copy.cy.js @@ -3657,6 +3657,7 @@ describe("Copy Tag Tests", function () { win.postMessage( { doenetML, + requestedVariantIndex: 1, }, "*", ); diff --git a/cypress/e2e/DoenetML/tagSpecific/mathdisplay.cy.js b/cypress/e2e/DoenetML/tagSpecific/mathdisplay.cy.js index 718d4dcb92..f048027b2c 100644 --- a/cypress/e2e/DoenetML/tagSpecific/mathdisplay.cy.js +++ b/cypress/e2e/DoenetML/tagSpecific/mathdisplay.cy.js @@ -233,10 +233,10 @@ describe("Math Display Tag Tests", function () { ); cy.get(cesc(`#\\/etm${i}`)).should("not.exist"); cy.get(cesc(`#\\/rm${i}`)).should("have.text", `???`); - cy.get(cesc(`#\\/rm${i}`)).click(); - cy.window().then(async (win) => { - expect(win.scrollY).eq(0); - }); + // cy.get(cesc(`#\\/rm${i}`)).click(); + // cy.window().then(async (win) => { + // expect(win.scrollY).eq(0); + // }); }); } @@ -296,10 +296,10 @@ describe("Math Display Tag Tests", function () { ); cy.get(cesc(`#\\/etn${i}`)).should("not.exist"); cy.get(cesc(`#\\/rn${i}`)).should("have.text", `???`); - cy.get(cesc(`#\\/rn${i}`)).click(); - cy.window().then(async (win) => { - expect(win.scrollY).eq(0); - }); + // cy.get(cesc(`#\\/rn${i}`)).click(); + // cy.window().then(async (win) => { + // expect(win.scrollY).eq(0); + // }); }); } @@ -705,10 +705,10 @@ describe("Math Display Tag Tests", function () { ); cy.get(cesc(`#\\/etm${i}`)).should("not.exist"); cy.get(cesc(`#\\/rm${i}`)).should("have.text", `???`); - cy.get(cesc(`#\\/rm${i}`)).click(); - cy.window().then(async (win) => { - expect(win.scrollY).eq(0); - }); + // cy.get(cesc(`#\\/rm${i}`)).click(); + // cy.window().then(async (win) => { + // expect(win.scrollY).eq(0); + // }); }); } @@ -775,10 +775,10 @@ describe("Math Display Tag Tests", function () { ); cy.get(cesc(`#\\/etn${i}`)).should("not.exist"); cy.get(cesc(`#\\/rn${i}`)).should("have.text", `???`); - cy.get(cesc(`#\\/rn${i}`)).click(); - cy.window().then(async (win) => { - expect(win.scrollY).eq(0); - }); + // cy.get(cesc(`#\\/rn${i}`)).click(); + // cy.window().then(async (win) => { + // expect(win.scrollY).eq(0); + // }); }); } @@ -989,9 +989,9 @@ describe("Math Display Tag Tests", function () { cy.get(cesc(`#\\/rm${i}`)).should("have.text", `???`); cy.get(cesc(`#\\/rm${i}`)).click(); cy.url().should("match", RegExp(`#$`)); - cy.window().then(async (win) => { - expect(win.scrollY).eq(0); - }); + // cy.window().then(async (win) => { + // expect(win.scrollY).eq(0); + // }); }); } @@ -1086,9 +1086,9 @@ describe("Math Display Tag Tests", function () { cy.get(cesc(`#\\/rn${i}`)).should("have.text", `???`); cy.get(cesc(`#\\/rn${i}`)).click(); cy.url().should("match", RegExp(`#$`)); - cy.window().then(async (win) => { - expect(win.scrollY).eq(0); - }); + // cy.window().then(async (win) => { + // expect(win.scrollY).eq(0); + // }); }); } diff --git a/cypress/e2e/DoenetML/tagSpecific/paginator.cy.js b/cypress/e2e/DoenetML/tagSpecific/paginator.cy.js index 3293193446..f6e9691ebe 100644 --- a/cypress/e2e/DoenetML/tagSpecific/paginator.cy.js +++ b/cypress/e2e/DoenetML/tagSpecific/paginator.cy.js @@ -2181,6 +2181,7 @@ describe("Paginator Tag Tests", function () { win.postMessage( { doenetML: allDoenetMLs[0], + requestedVariantIndex: 1, }, "*", ); @@ -2200,6 +2201,7 @@ describe("Paginator Tag Tests", function () { win.postMessage( { doenetML: allDoenetMLs[attemptNumber - 1], + requestedVariantIndex: attemptNumber, }, "*", ); diff --git a/cypress/e2e/DoenetML/tagSpecific/ref.cy.js b/cypress/e2e/DoenetML/tagSpecific/ref.cy.js index 2fb429571c..b9ee80cb21 100644 --- a/cypress/e2e/DoenetML/tagSpecific/ref.cy.js +++ b/cypress/e2e/DoenetML/tagSpecific/ref.cy.js @@ -181,7 +181,7 @@ describe("ref Tag Tests", function () { cy.get(cesc("#\\/_ref1")) .should("have.text", "a Doenet doc") .invoke("attr", "href") - .then((href) => expect(href).eq("/public?doenetId=abcdefg")); + .then((href) => expect(href).eq("/portfolioviewer/abcdefg")); }); it("url with no link text", () => { @@ -471,8 +471,10 @@ describe("ref Tag Tests", function () { ); cy.get(cesc("#\\/inside")).then((el) => { - let rect = el[0].getBoundingClientRect(); - expect(rect.top).gt(-1).lt(1); + cy.waitUntil(() => { + let rect = el[0].getBoundingClientRect(); + return rect.top > -1 && rect.top < 1; + }); }); cy.window().then(async (win) => { let stateVariables = await win.returnAllStateVariables1(); diff --git a/cypress/e2e/DoenetML/tagSpecific/selectrandomnumbers.cy.js b/cypress/e2e/DoenetML/tagSpecific/selectrandomnumbers.cy.js index e215c70a0f..caf6ac2269 100644 --- a/cypress/e2e/DoenetML/tagSpecific/selectrandomnumbers.cy.js +++ b/cypress/e2e/DoenetML/tagSpecific/selectrandomnumbers.cy.js @@ -483,7 +483,7 @@ describe("SelectRandomNumbers Tag Tests", function () { let varX = me.math.variance(samples, "uncorrected"); expect(meanX).closeTo(0, 0.2); - expect(varX).closeTo(1, 0.2); + expect(varX).closeTo(1, 0.5); let firstSelect = stateVariables[ @@ -564,8 +564,8 @@ describe("SelectRandomNumbers Tag Tests", function () { let meanX = me.math.mean(samples); let varX = me.math.variance(samples, "uncorrected"); - expect(meanX).closeTo(0, 2); - expect(varX).closeTo(100, 10); + expect(meanX).closeTo(0, 3); + expect(varX).closeTo(100, 15); let firstSelect = stateVariables[ @@ -647,7 +647,7 @@ describe("SelectRandomNumbers Tag Tests", function () { let varX = me.math.variance(samples, "uncorrected"); expect(meanX).closeTo(-50, 0.5); - expect(varX).closeTo(1, 0.1); + expect(varX).closeTo(1, 0.3); let firstSelect = stateVariables[ @@ -729,7 +729,7 @@ describe("SelectRandomNumbers Tag Tests", function () { let varX = me.math.variance(samples, "uncorrected"); expect(meanX).closeTo(100, 2); - expect(varX).closeTo(100, 20); + expect(varX).closeTo(100, 30); let firstSelect = stateVariables[ @@ -811,7 +811,7 @@ describe("SelectRandomNumbers Tag Tests", function () { let varX = me.math.variance(samples, "uncorrected"); expect(meanX).closeTo(-3, 0.1); - expect(varX).closeTo(0.01, 0.002); + expect(varX).closeTo(0.01, 0.005); let firstSelect = stateVariables[ @@ -896,7 +896,7 @@ describe("SelectRandomNumbers Tag Tests", function () { let meanX = me.math.mean(samples); let varX = me.math.variance(samples, "uncorrected"); - expect(meanX).closeTo(0.5, 0.05); + expect(meanX).closeTo(0.5, 0.15); expect(varX).closeTo((2 ** 2 - 1) / 12, 0.05); let firstSelect = @@ -986,7 +986,7 @@ describe("SelectRandomNumbers Tag Tests", function () { let varX = me.math.variance(samples, "uncorrected"); expect(meanX).closeTo(3, 0.2); - expect(varX).closeTo((6 ** 2 - 1) / 12, 0.5); + expect(varX).closeTo((6 ** 2 - 1) / 12, 1); let firstSelect = stateVariables[ @@ -1074,7 +1074,7 @@ describe("SelectRandomNumbers Tag Tests", function () { let meanX = me.math.mean(samples); let varX = me.math.variance(samples, "uncorrected"); - expect(meanX).closeTo(9, 0.05); + expect(meanX).closeTo(9, 0.1); expect(varX).closeTo((2 ** 2 - 1) / 12, 0.05); let firstSelect = @@ -1163,7 +1163,7 @@ describe("SelectRandomNumbers Tag Tests", function () { let meanX = me.math.mean(samples); let varX = me.math.variance(samples, "uncorrected"); - expect(meanX).closeTo(1, 0.3); + expect(meanX).closeTo(1, 0.4); expect(varX).closeTo((9 ** 2 - 1) / 12, 1); let firstSelect = @@ -1316,7 +1316,7 @@ describe("SelectRandomNumbers Tag Tests", function () { let meanX = me.math.mean(samples); let varX = me.math.variance(samples, "uncorrected"); - expect(meanX).closeTo(1, 0.4); + expect(meanX).closeTo(1, 0.6); expect(varX).closeTo(((5 ** 2 - 1) * 2 ** 2) / 12, 1); let firstSelect = diff --git a/cypress/e2e/DoenetML/tagSpecific/slider.cy.js b/cypress/e2e/DoenetML/tagSpecific/slider.cy.js index 8bd40d8800..99f6961deb 100644 --- a/cypress/e2e/DoenetML/tagSpecific/slider.cy.js +++ b/cypress/e2e/DoenetML/tagSpecific/slider.cy.js @@ -36,7 +36,7 @@ describe("Slider Tag Tests", function () { cy.log("move handle less than half way, stays at 1"); cy.get(cesc("#\\/s-handle")) .trigger("mousedown") - .trigger("mousemove", { clientX: 145, clientY: 0 }) + .trigger("mousemove", { clientX: 140, clientY: 0 }) .trigger("mouseup"); cy.get(cesc("#\\/sv")).should("have.text", "1"); @@ -92,7 +92,7 @@ describe("Slider Tag Tests", function () { ); }); - let numberToPx = (x) => 27 + 30 * x; + let numberToPx = (x) => 20 + 30 * x; let numberToPx2 = (x) => 30 * x; cy.get(cesc("#\\/_text1")).should("have.text", "a"); // to wait for page to load @@ -314,7 +314,7 @@ describe("Slider Tag Tests", function () { ); }); - let numberToPx = (x) => 27 + 30 * x; + let numberToPx = (x) => 20 + 30 * x; cy.get(cesc("#\\/_text1")).should("have.text", "a"); // to wait for page to load @@ -413,7 +413,7 @@ describe("Slider Tag Tests", function () { ); }); - let numberToPx = (x) => 27 + (30 * (x - 100)) / 10; + let numberToPx = (x) => 20 + (30 * (x - 100)) / 10; cy.get(cesc("#\\/_text1")).should("have.text", "a"); // to wait for page to load @@ -556,7 +556,7 @@ describe("Slider Tag Tests", function () { ); }); - let numberToPx = (x) => 27 + 30 * x; + let numberToPx = (x) => 20 + 30 * x; cy.get(cesc("#\\/_text1")).should("have.text", "a"); // to wait for page to load @@ -632,7 +632,7 @@ describe("Slider Tag Tests", function () { ); }); - let numberToPx = (x) => 27 + 30 * x; + let numberToPx = (x) => 20 + 30 * x; cy.get(cesc("#\\/_text1")).should("have.text", "a"); // to wait for page to load @@ -828,7 +828,7 @@ describe("Slider Tag Tests", function () { ); }); - let numberToPx = (x) => 27 + 30 * x; + let numberToPx = (x) => 20 + 30 * x; let numberToPx2 = (x) => 30 * x; cy.get(cesc("#\\/_text1")).should("have.text", "a"); // to wait for page to load @@ -878,7 +878,7 @@ describe("Slider Tag Tests", function () { ); }); - let numberToPx = (x) => 27 + 30 * x; + let numberToPx = (x) => 20 + 30 * x; let numberToPx2 = (x) => 30 * x; cy.get(cesc("#\\/_text1")).should("have.text", "a"); // to wait for page to load diff --git a/cypress/e2e/DoenetML/variants/uniquevariants.cy.js b/cypress/e2e/DoenetML/variants/uniquevariants.cy.js index 4b15ec50bd..3ab7ed92ae 100644 --- a/cypress/e2e/DoenetML/variants/uniquevariants.cy.js +++ b/cypress/e2e/DoenetML/variants/uniquevariants.cy.js @@ -3529,7 +3529,7 @@ describe("Specifying unique variant tests", function () { it("no variant control, problem with 3 selects", () => { // Catch bug in enumerateCombinations - // where was indirectly overwriting numberOfVariantsByDescendant + // where was indirectly overwriting numVariantsByDescendant let values = [135, 246, 145, 236, 136, 245, 146, 235]; cy.log("get each value exactly one"); diff --git a/cypress/e2e/People/people.cy.js b/cypress/e2e/People/people.cy.js index 36b662776a..7c946f8216 100644 --- a/cypress/e2e/People/people.cy.js +++ b/cypress/e2e/People/people.cy.js @@ -9,7 +9,7 @@ describe("People Test", function () { email: "test@gmail.com", section: "testsect", externalId: "testExID", - roleId: "studentRoleId", + roleId: "courseid1SId", }; // generate people in cypress/fixtures/peopleExample.csv diff --git a/doenet_docker/volumes/db_init/db_template.sql b/doenet_docker/volumes/db_init/db_template.sql index 4b49e9bc15..48f7ebd76c 100644 --- a/doenet_docker/volumes/db_init/db_template.sql +++ b/doenet_docker/volumes/db_init/db_template.sql @@ -56,7 +56,7 @@ CREATE TABLE `activity_state` ( LOCK TABLES `activity_state` WRITE; /*!40000 ALTER TABLE `activity_state` DISABLE KEYS */; -INSERT INTO `activity_state` VALUES (1,'cyuserId','_Ga07DeeWjhH6Y4UpWlakE',1,'StbBhgrC0UT1kf31HTUzI','bafkreieszxhhjdhin3wbdvaudhiumb2ygqbnd6cfwyz4hqjdcbgrw6cebq','Salix caprea',379,'{\"orderWithCids\":[{\"type\":\"page\",\"cid\":\"bafkreiemblagflvpgbvw2zgurtswwcltj6mkolerhebcymztdmrsoabz6a\"}],\"variantsByPage\":[1],\"itemWeights\":[1],\"numberOfVariants\":1000}','{\"currentPage\":1}'); +INSERT INTO `activity_state` VALUES (1,'cyuserId','_Ga07DeeWjhH6Y4UpWlakE',1,'StbBhgrC0UT1kf31HTUzI','bafkreieszxhhjdhin3wbdvaudhiumb2ygqbnd6cfwyz4hqjdcbgrw6cebq','Salix caprea',379,'{\"orderWithCids\":[{\"type\":\"page\",\"cid\":\"bafkreiemblagflvpgbvw2zgurtswwcltj6mkolerhebcymztdmrsoabz6a\"}],\"variantsByPage\":[1],\"itemWeights\":[1],\"numVariants\":1000}','{\"currentPage\":1}'); /*!40000 ALTER TABLE `activity_state` ENABLE KEYS */; UNLOCK TABLES; diff --git a/public/api/initAssignmentAttempt.php b/public/api/initAssignmentAttempt.php index 5d584e7414..b6e5e90878 100644 --- a/public/api/initAssignmentAttempt.php +++ b/public/api/initAssignmentAttempt.php @@ -16,7 +16,7 @@ : ""; $_POST = json_decode(file_get_contents("php://input"), true); -$doenetId = mysqli_real_escape_string($conn, $_POST["doenetId"]); +$doenetId = mysqli_real_escape_string($conn, $_POST["activityId"]); $attemptNumber = mysqli_real_escape_string($conn, $_POST["attemptNumber"]); $weights = array_map(function ($item) use ($conn) { diff --git a/public/api/loadActivityState.php b/public/api/loadActivityState.php index d713d47fe6..802c3a524d 100644 --- a/public/api/loadActivityState.php +++ b/public/api/loadActivityState.php @@ -17,7 +17,7 @@ $cid = mysqli_real_escape_string($conn, $_REQUEST["cid"]); $attemptNumber = mysqli_real_escape_string($conn, $_REQUEST["attemptNumber"]); -$doenetId = mysqli_real_escape_string($conn, $_REQUEST["doenetId"]); +$doenetId = mysqli_real_escape_string($conn, $_REQUEST["activityId"]); $allowLoadState = mysqli_real_escape_string($conn, $_REQUEST["allowLoadState"]); $effectiveUserId = $requestorUserId; diff --git a/public/api/loadPageState.php b/public/api/loadPageState.php index 681200ae94..2e32b96f1e 100644 --- a/public/api/loadPageState.php +++ b/public/api/loadPageState.php @@ -18,7 +18,7 @@ $cid = mysqli_real_escape_string($conn, $_REQUEST['cid']); $pageNumber = mysqli_real_escape_string($conn, $_REQUEST['pageNumber']); $attemptNumber = mysqli_real_escape_string($conn, $_REQUEST['attemptNumber']); -$doenetId = mysqli_real_escape_string($conn, $_REQUEST['doenetId']); +$doenetId = mysqli_real_escape_string($conn, $_REQUEST['activityId']); $requestedVariantIndex = mysqli_real_escape_string( $conn, $_REQUEST['requestedVariantIndex'] diff --git a/public/api/parseActivityDefinitionRenamePages.php b/public/api/parseActivityDefinitionRenamePages.php index 609b65bb8a..52cf160bf2 100644 --- a/public/api/parseActivityDefinitionRenamePages.php +++ b/public/api/parseActivityDefinitionRenamePages.php @@ -46,8 +46,8 @@ function parse_activity_definition_rename_pages($activity_xml) { $activity_definition["shuffleItemWeights"] = true; } - if ($activity_object["numberOfVariants"]) { - $activity_definition["numberOfVariants"] = (string)$activity_object["itemWeights"]; + if ($activity_object["numVariants"]) { + $activity_definition["numVariants"] = (string)$activity_object["itemWeights"]; } diff --git a/public/api/recordEvent.php b/public/api/recordEvent.php index 7c926bec19..688e310ca1 100644 --- a/public/api/recordEvent.php +++ b/public/api/recordEvent.php @@ -14,7 +14,7 @@ $examDoenetId = array_key_exists("doenetId",$jwtArray) ? $jwtArray['doenetId'] : ""; $_POST = json_decode(file_get_contents("php://input"),true); -$doenetId = mysqli_real_escape_string($conn,$_POST["doenetId"]); +$doenetId = mysqli_real_escape_string($conn,$_POST["activityId"]); $activityCid = mysqli_real_escape_string($conn,$_POST["activityCid"]); $pageCid = mysqli_real_escape_string($conn,$_POST["pageCid"]); $pageNumber = mysqli_real_escape_string($conn,$_POST["pageNumber"]); diff --git a/public/api/reportSolutionViewed.php b/public/api/reportSolutionViewed.php index 8bff9eee0e..1c813effa7 100644 --- a/public/api/reportSolutionViewed.php +++ b/public/api/reportSolutionViewed.php @@ -12,7 +12,7 @@ $examDoenetId = array_key_exists("doenetId",$jwtArray) ? $jwtArray['doenetId'] : ""; $_POST = json_decode(file_get_contents("php://input"),true); -$doenetId = mysqli_real_escape_string($conn,$_POST["doenetId"]); +$doenetId = mysqli_real_escape_string($conn,$_POST["activityId"]); $attemptNumber = mysqli_real_escape_string($conn,$_POST["attemptNumber"]); $itemNumber = mysqli_real_escape_string($conn,$_POST["itemNumber"]); diff --git a/public/api/saveActivityState.php b/public/api/saveActivityState.php index 7bfad5f37f..9f999092e7 100644 --- a/public/api/saveActivityState.php +++ b/public/api/saveActivityState.php @@ -15,7 +15,7 @@ $device = $jwtArray["deviceName"]; $_POST = json_decode(file_get_contents("php://input"), true); -$doenetId = mysqli_real_escape_string($conn, $_POST["doenetId"]); +$doenetId = mysqli_real_escape_string($conn, $_POST["activityId"]); $cid = mysqli_real_escape_string($conn, $_POST["cid"]); $attemptNumber = mysqli_real_escape_string($conn, $_POST["attemptNumber"]); $variantIndex = mysqli_real_escape_string($conn, $_POST["variantIndex"]); diff --git a/public/api/saveCompleted.php b/public/api/saveCompleted.php index e2386a75e3..e03fb1560e 100644 --- a/public/api/saveCompleted.php +++ b/public/api/saveCompleted.php @@ -10,7 +10,7 @@ $jwtArray = include "jwtArray.php"; $userId = $jwtArray['userId']; -$doenetId = mysqli_real_escape_string($conn,$_REQUEST["doenetId"]); +$doenetId = mysqli_real_escape_string($conn,$_REQUEST["activityId"]); $isCompleted = mysqli_real_escape_string($conn,$_REQUEST["isCompleted"]); $success = TRUE; diff --git a/public/api/saveCreditForItem.php b/public/api/saveCreditForItem.php index 08bb5cdf38..01dbab73cd 100644 --- a/public/api/saveCreditForItem.php +++ b/public/api/saveCreditForItem.php @@ -19,7 +19,7 @@ : ''; $_POST = json_decode(file_get_contents('php://input'), true); -$doenetId = mysqli_real_escape_string($conn, $_POST['doenetId']); +$doenetId = mysqli_real_escape_string($conn, $_POST['activityId']); $attemptNumber = mysqli_real_escape_string($conn, $_POST['attemptNumber']); $credit = mysqli_real_escape_string($conn, $_POST['credit']); $itemNumber = mysqli_real_escape_string($conn, $_POST['itemNumber']); diff --git a/public/api/savePageState.php b/public/api/savePageState.php index c7bfe26601..44b09eda73 100644 --- a/public/api/savePageState.php +++ b/public/api/savePageState.php @@ -15,7 +15,7 @@ $device = $jwtArray["deviceName"]; $_POST = json_decode(file_get_contents("php://input"), true); -$doenetId = mysqli_real_escape_string($conn, $_POST["doenetId"]); +$doenetId = mysqli_real_escape_string($conn, $_POST["activityId"]); $cid = mysqli_real_escape_string($conn, $_POST["cid"]); $pageNumber = mysqli_real_escape_string($conn, $_POST["pageNumber"]); $attemptNumber = mysqli_real_escape_string($conn, $_POST["attemptNumber"]); diff --git a/src/Core/ComponentTypes.js b/src/Core/ComponentTypes.js index f911b95c6a..350891fa81 100644 --- a/src/Core/ComponentTypes.js +++ b/src/Core/ComponentTypes.js @@ -155,7 +155,6 @@ import RegionBetweenCurveXAxis from "./components/RegionBetweenCurveXAxis"; import RegionHalfPlane from "./components/RegionHalfPlane"; import CodeEditor from "./components/CodeEditor"; import CodeViewer from "./components/CodeViewer"; -import RenderDoenetML from "./components/RenderDoenetML"; import HasSameFactoring from "./components/HasSameFactoring"; import DataFrame from "./components/DataFrame"; import SummaryStatistics from "./components/SummaryStatistics"; @@ -346,7 +345,6 @@ const componentTypeArray = [ RegionHalfPlane, CodeEditor, CodeViewer, - RenderDoenetML, HasSameFactoring, DataFrame, SummaryStatistics, diff --git a/src/Core/Core.js b/src/Core/Core.js index cd2f0a8bbb..f8ed8467d7 100644 --- a/src/Core/Core.js +++ b/src/Core/Core.js @@ -25,7 +25,7 @@ import createComponentInfoObjects from "./utils/componentInfoObjects"; import { get as idb_get, set as idb_set } from "idb-keyval"; import { toastType } from "../Tools/_framework/ToastTypes"; import axios from "axios"; -import { gatherVariantComponents, getNumberOfVariants } from "./utils/variants"; +import { gatherVariantComponents, getNumVariants } from "./utils/variants"; import { assignDoenetMLRange, findAllNewlines, @@ -38,8 +38,10 @@ import { export default class Core { constructor({ doenetML, - doenetId, - activityCid, + preliminarySerializedComponents, + activityId, + cid, + cidForActivity: activityCid, pageNumber, attemptNumber = 1, itemNumber = 1, @@ -54,17 +56,20 @@ export default class Core { stateVariableChanges = {}, coreId, updateDataOnContentChange, + apiURLs = {}, }) { // console.time('core'); this.coreId = coreId; - this.doenetId = doenetId; + this.activityId = activityId; this.activityCid = activityCid; this.pageNumber = pageNumber; this.attemptNumber = attemptNumber; this.itemNumber = itemNumber; this.activityVariantIndex = activityVariantIndex; this.doenetML = doenetML; + this.cid = cid; + this.apiURLs = apiURLs; this.serverSaveId = serverSaveId; this.updateDataOnContentChange = updateDataOnContentChange; @@ -192,15 +197,13 @@ export default class Core { } }; - cidFromText(doenetML) - .then((cid) => - serializeFunctions.expandDoenetMLsToFullSerializedComponents({ - cids: [cid], - doenetMLs: [doenetML], - componentInfoObjects: this.componentInfoObjects, - flags: this.flags, - }), - ) + serializeFunctions + .expandDoenetMLsToFullSerializedComponents({ + doenetMLs: [doenetML], + preliminarySerializedComponents: [preliminarySerializedComponents], + componentInfoObjects: this.componentInfoObjects, + flags: this.flags, + }) .then(this.finishCoreConstruction) .catch((e) => { // throw e; @@ -213,13 +216,11 @@ export default class Core { } async finishCoreConstruction({ - cids, fullSerializedComponents, allDoenetMLs, errors, warnings, }) { - this.cid = cids[0]; this.allDoenetMLs = allDoenetMLs; this.doenetMLNewlines = findAllNewlines(allDoenetMLs[0]); @@ -321,10 +322,10 @@ export default class Core { // console.timeEnd('serialize doenetML'); - let numVariants = getNumberOfVariants({ + let numVariants = getNumVariants({ serializedComponent: serializedComponents[0], componentInfoObjects: this.componentInfoObjects, - }).numberOfVariants; + }).numVariants; if (!this.requestedVariant) { // don't have full variant, just requested variant index @@ -10296,7 +10297,7 @@ export default class Core { } const payload = { - doenetId: this.doenetId, + activityId: this.activityId, activityCid: this.activityCid, pageCid: this.cid, pageNumber: this.pageNumber, @@ -10321,7 +10322,7 @@ export default class Core { }; try { - let resp = await axios.post("/api/recordEvent.php", payload); + let resp = await axios.post(this.apiURLs.recordEvent, payload); // console.log(">>>>resp from record event", resp.data) } catch (e) { console.error(`Error saving event: ${e.message}`); @@ -11725,7 +11726,7 @@ export default class Core { if (this.flags.allowLocalState) { await idb_set( - `${this.doenetId}|${this.pageNumber}|${this.attemptNumber}|${this.cid}`, + `${this.activityId}|${this.pageNumber}|${this.attemptNumber}|${this.cid}`, { coreState: this.cumulativeStateVariableChanges, rendererState: this.rendererState, @@ -11757,7 +11758,7 @@ export default class Core { ), pageNumber: this.pageNumber, attemptNumber: this.attemptNumber, - doenetId: this.doenetId, + activityId: this.activityId, saveId, serverSaveId: this.serverSaveId, updateDataOnContentChange: this.updateDataOnContentChange, @@ -11810,7 +11811,7 @@ export default class Core { try { resp = await axios.post( - "/api/savePageState.php", + this.apiURLs.savePageState, this.pageStateToBeSavedToDatabase, ); } catch (e) { @@ -11858,7 +11859,7 @@ export default class Core { if (this.flags.allowLocalState) { await idb_set( - `${this.doenetId}|${this.pageNumber}|${this.attemptNumber}|${this.cid}|ServerSaveId`, + `${this.activityId}|${this.pageNumber}|${this.attemptNumber}|${this.cid}|ServerSaveId`, data.saveId, ); } @@ -11872,7 +11873,7 @@ export default class Core { ) { if (this.flags.allowLocalState) { await idb_set( - `${this.doenetId}|${this.pageNumber}|${data.attemptNumber}|${data.cid}`, + `${this.activityId}|${this.pageNumber}|${data.attemptNumber}|${data.cid}`, { coreState: JSON.parse( data.coreState, @@ -11923,7 +11924,7 @@ export default class Core { } const payload = { - doenetId: this.doenetId, + activityId: this.activityId, attemptNumber: this.attemptNumber, credit: pageCreditAchieved, itemNumber: this.itemNumber, @@ -11932,7 +11933,7 @@ export default class Core { console.log("payload for save credit for item", payload); axios - .post("/api/saveCreditForItem.php", payload) + .post(this.apiURLs.saveCreditForItem, payload) .then((resp) => { // console.log('>>>>saveCreditForItem resp', resp.data); @@ -12163,8 +12164,8 @@ export default class Core { } try { - const resp = await axios.post("/api/reportSolutionViewed.php", { - doenetId: this.doenetId, + const resp = await axios.post(this.apiURLs.reportSolutionViewed, { + activityId: this.activityId, itemNumber: this.itemNumber, pageNumber: this.pageNumber, attemptNumber: this.attemptNumber, diff --git a/src/Core/components/ChoiceInput.js b/src/Core/components/ChoiceInput.js index d50bf2c77d..f14c664536 100644 --- a/src/Core/components/ChoiceInput.js +++ b/src/Core/components/ChoiceInput.js @@ -1446,19 +1446,18 @@ export default class Choiceinput extends Input { return { success: false }; } - let numberOfVariants = result.numberOfVariants * numberOfPermutations; + let numVariants = result.numVariants * numberOfPermutations; // adjust variants info added by call to super - serializedComponent.variants.numberOfVariants = numberOfVariants; + serializedComponent.variants.numVariants = numVariants; serializedComponent.variants.uniqueVariantData = { - numberOfVariantsByDescendant: - serializedComponent.variants.uniqueVariantData - .numberOfVariantsByDescendant, + numVariantsByDescendant: + serializedComponent.variants.uniqueVariantData.numVariantsByDescendant, numberOfPermutations, numChoices, }; - return { success: true, numberOfVariants }; + return { success: true, numVariants }; } static getUniqueVariant({ @@ -1466,15 +1465,15 @@ export default class Choiceinput extends Input { variantIndex, componentInfoObjects, }) { - let numberOfVariants = serializedComponent.variants?.numberOfVariants; - if (numberOfVariants === undefined) { + let numVariants = serializedComponent.variants?.numVariants; + if (numVariants === undefined) { return { success: false }; } if ( !Number.isInteger(variantIndex) || variantIndex < 1 || - variantIndex > numberOfVariants + variantIndex > numVariants ) { return { success: false }; } @@ -1487,9 +1486,8 @@ export default class Choiceinput extends Input { }); } - let numberOfVariantsByDescendant = - serializedComponent.variants.uniqueVariantData - .numberOfVariantsByDescendant; + let numVariantsByDescendant = + serializedComponent.variants.uniqueVariantData.numVariantsByDescendant; let descendantVariantComponents = serializedComponent.variants.descendantVariantComponents; let numberOfPermutations = @@ -1497,7 +1495,7 @@ export default class Choiceinput extends Input { let numChoices = serializedComponent.variants.uniqueVariantData.numChoices; // treat permutations as another descendant variant component - let numbersOfOptions = [...numberOfVariantsByDescendant]; + let numbersOfOptions = [...numVariantsByDescendant]; numbersOfOptions.push(numberOfPermutations); let indicesForEachOption = enumerateCombinations({ @@ -1525,10 +1523,10 @@ export default class Choiceinput extends Input { for ( let descendantNum = 0; - descendantNum < numberOfVariantsByDescendant.length; + descendantNum < numVariantsByDescendant.length; descendantNum++ ) { - if (numberOfVariantsByDescendant[descendantNum] > 1) { + if (numVariantsByDescendant[descendantNum] > 1) { let descendant = descendantVariantComponents[descendantNum]; let compClass = componentInfoObjects.allComponentClasses[descendant.componentType]; diff --git a/src/Core/components/CodeEditor.js b/src/Core/components/CodeEditor.js index efdd6cd5e3..3bf23645c4 100644 --- a/src/Core/components/CodeEditor.js +++ b/src/Core/components/CodeEditor.js @@ -22,6 +22,8 @@ export default class CodeEditor extends BlockComponent { static processWhenJustUpdatedForNewComponent = true; + static ignoreErrorsFromChildren = true; + static get stateVariablesShadowedForReference() { return ["value"]; } @@ -115,7 +117,6 @@ export default class CodeEditor extends BlockComponent { let addCodeViewer = function ({ matchedChildren, componentAttributes }) { let codeViewer = { componentType: "codeViewer", - children: [{ componentType: "renderDoenetML" }], }; //Update depends on this being the 1st index position @@ -501,6 +502,7 @@ export default class CodeEditor extends BlockComponent { stateVariableDefinitions.childIndicesToRender = { stateVariablesDeterminingDependencies: ["allChildren"], + additionalStateVariablesDefined: ["viewerChildName"], returnDependencies({ stateValues }) { let dependencies = {}; if (stateValues.allChildren[0]?.componentType === "codeViewer") { @@ -509,17 +511,50 @@ export default class CodeEditor extends BlockComponent { componentName: stateValues.allChildren[0].componentName, attributeName: "createdFromSugar", }; + dependencies.viewerChildName = { + dependencyType: "value", + value: stateValues.allChildren[0].componentName, + }; } return dependencies; }, definition({ dependencyValues }) { let childIndicesToRender = []; + let viewerChildName = null; if (dependencyValues.firstCodeViewerFromSugar) { childIndicesToRender.push(0); + viewerChildName = dependencyValues.viewerChildName; } - return { setValue: { childIndicesToRender } }; + return { setValue: { childIndicesToRender, viewerChildName } }; + }, + }; + + stateVariableDefinitions.errorsAndWarnings = { + forRenderer: true, + stateVariablesDeterminingDependencies: ["viewerChildName", "showResults"], + returnDependencies({ stateValues }) { + if (stateValues.viewerChildName && stateValues.showResults) { + return { + errorsAndWarnings: { + dependencyType: "stateVariable", + componentName: stateValues.viewerChildName, + variableName: "errorsAndWarnings", + }, + }; + } else { + return {}; + } + }, + definition({ dependencyValues }) { + if (dependencyValues.errorsAndWarnings) { + return { + setValue: { errorsAndWarnings: dependencyValues.errorsAndWarnings }, + }; + } else { + return { setValue: { errorsAndWarnings: null } }; + } }, }; @@ -533,19 +568,30 @@ export default class CodeEditor extends BlockComponent { skipRendererUpdate = false, }) { if (!(await this.stateValues.disabled)) { + let updateInstructions = [ + { + updateType: "updateValue", + componentName: this.componentName, + stateVariable: "immediateValue", + value: text, + }, + { + updateType: "setComponentNeedingUpdateValue", + componentName: this.componentName, + }, + ]; + + let viewerChildName = await this.stateValues.viewerChildName; + if (viewerChildName) { + updateInstructions.push({ + updateType: "updateValue", + componentName: viewerChildName, + stateVariable: "codeChanged", + value: true, + }); + } return await this.coreFunctions.performUpdate({ - updateInstructions: [ - { - updateType: "updateValue", - componentName: this.componentName, - stateVariable: "immediateValue", - value: text, - }, - { - updateType: "setComponentNeedingUpdateValue", - componentName: this.componentName, - }, - ], + updateInstructions, actionId, sourceInformation, skipRendererUpdate, @@ -636,7 +682,7 @@ export default class CodeEditor extends BlockComponent { } } - async updateComponents() { + async updateComponents(args) { if ( this.definingChildren[0]?.componentType === "codeViewer" && this.definingChildren[0].doenetAttributes?.createdFromSugar @@ -644,13 +690,7 @@ export default class CodeEditor extends BlockComponent { await this.coreFunctions.performAction({ componentName: this.definingChildren[0].componentName, actionName: "updateComponents", - // event: { - // verb: "selected", - // object: { - // componentName: this.componentName, - // componentType: this.componentType, - // }, - // }, + args, }); } } diff --git a/src/Core/components/CodeViewer.js b/src/Core/components/CodeViewer.js index ab16141d73..9990e6af21 100644 --- a/src/Core/components/CodeViewer.js +++ b/src/Core/components/CodeViewer.js @@ -6,6 +6,7 @@ export default class CodeViewer extends BlockComponent { Object.assign(this.actions, { updateComponents: this.updateComponents.bind(this), + setErrorsAndWarnings: this.setErrorsAndWarnings.bind(this), recordVisibilityChange: this.recordVisibilityChange.bind(this), }); } @@ -40,53 +41,6 @@ export default class CodeViewer extends BlockComponent { return attributes; } - static returnSugarInstructions() { - let sugarInstructions = super.returnSugarInstructions(); - - let addRenderDoenetML = function ({ - matchedChildren, - componentAttributes, - }) { - if (matchedChildren.length > 0) { - return { success: false }; - } - - let renderDoenetML = { - componentType: "renderDoenetML", - }; - - if (componentAttributes.codeSource) { - renderDoenetML.attributes = { - codeSource: { - targetComponentNames: componentAttributes.codeSource, - }, - }; - } - - if (componentAttributes.renderedName) { - renderDoenetML.props = { name: componentAttributes.renderedName }; - } - - return { - success: true, - newChildren: [renderDoenetML], - }; - }; - sugarInstructions.push({ - replacementFunction: addRenderDoenetML, - }); - return sugarInstructions; - } - - static returnChildGroups() { - return [ - { - group: "children", - componentTypes: ["_base"], - }, - ]; - } - static returnStateVariableDefinitions() { let stateVariableDefinitions = super.returnStateVariableDefinitions(); @@ -258,26 +212,164 @@ export default class CodeViewer extends BlockComponent { }, }; + stateVariableDefinitions.doenetMLFromSource = { + stateVariablesDeterminingDependencies: ["codeSource"], + returnDependencies: ({ stateValues }) => ({ + doenetML: { + dependencyType: "stateVariable", + componentName: stateValues.codeSource, + variableName: "text", + variablesOptional: true, + }, + }), + definition({ dependencyValues }) { + let doenetML = ""; + + if (dependencyValues.doenetML) { + doenetML = dependencyValues.doenetML; + if (typeof doenetML !== "string") { + doenetML = ""; + } + } + + return { setValue: { doenetMLFromSource: doenetML } }; + }, + }; + + stateVariableDefinitions.doenetML = { + forRenderer: true, + hasEssential: true, + doNotShadowEssential: true, + provideEssentialValuesInDefinition: true, + returnDependencies: () => ({ + doenetMLFromSource: { + dependencyType: "stateVariable", + variableName: "doenetMLFromSource", + }, + }), + definition({ dependencyValues, essentialValues }) { + let result = { + useEssentialOrDefaultValue: { + doenetML: { + defaultValue: dependencyValues.doenetMLFromSource, + }, + }, + }; + if (essentialValues.doenetML === undefined) { + result.setEssentialValue = { + doenetML: dependencyValues.doenetMLFromSource, + }; + } + return result; + }, + inverseDefinition({ desiredStateVariableValues }) { + return { + success: true, + instructions: [ + { + setEssentialValue: "doenetML", + value: desiredStateVariableValues.doenetML, + }, + ], + }; + }, + }; + + stateVariableDefinitions.codeChanged = { + public: true, + shadowingInstructions: { + createComponentOfType: "boolean", + }, + hasEssential: true, + forRenderer: true, + defaultValue: false, + returnDependencies: () => ({}), + definition: () => ({ useEssentialOrDefaultValue: { codeChanged: true } }), + inverseDefinition({ desiredStateVariableValues }) { + return { + success: true, + instructions: [ + { + setEssentialValue: "codeChanged", + value: Boolean(desiredStateVariableValues.codeChanged), + }, + ], + }; + }, + }; + + stateVariableDefinitions.errorsAndWarnings = { + hasEssential: true, + defaultValue: { errors: [], warnings: [] }, + returnDependencies: () => ({}), + definition: () => ({ + useEssentialOrDefaultValue: { errorsAndWarnings: true }, + }), + inverseDefinition({ desiredStateVariableValues }) { + return { + success: true, + instructions: [ + { + setEssentialValue: "errorsAndWarnings", + value: desiredStateVariableValues.errorsAndWarnings, + }, + ], + }; + }, + }; + return stateVariableDefinitions; } - async updateComponents() { - if ( - this.definingChildren.length === 1 && - this.definingChildren[0].componentType === "renderDoenetML" - ) { - await this.coreFunctions.performAction({ - componentName: this.definingChildren[0].componentName, - actionName: "updateComponents", - // event: { - // verb: "selected", - // object: { - // componentName: this.componentName, - // componentType: this.componentType, - // }, - // }, - }); - } + async updateComponents({ + actionId, + sourceInformation = {}, + skipRendererUpdate = false, + }) { + let updateInstructions = [ + { + updateType: "updateValue", + componentName: this.componentName, + stateVariable: "doenetML", + value: await this.stateValues.doenetMLFromSource, + }, + { + updateType: "updateValue", + componentName: this.componentName, + stateVariable: "codeChanged", + value: false, + }, + ]; + + await this.coreFunctions.performUpdate({ + updateInstructions, + actionId, + sourceInformation, + skipRendererUpdate, + }); + } + + async setErrorsAndWarnings({ + actionId, + sourceInformation = {}, + skipRendererUpdate = false, + errorsAndWarnings, + }) { + let updateInstructions = [ + { + updateType: "updateValue", + componentName: this.componentName, + stateVariable: "errorsAndWarnings", + value: errorsAndWarnings, + }, + ]; + + await this.coreFunctions.performUpdate({ + updateInstructions, + actionId, + sourceInformation, + skipRendererUpdate, + }); } recordVisibilityChange({ isVisible }) { diff --git a/src/Core/components/Document.js b/src/Core/components/Document.js index 8966a22c6c..fea84037ad 100644 --- a/src/Core/components/Document.js +++ b/src/Core/components/Document.js @@ -765,7 +765,7 @@ export default class Document extends BaseComponent { }) { // console.log("****Variant for document*****") - let numVariants = serializedComponent.variants.numberOfVariants; + let numVariants = serializedComponent.variants.numVariants; let variantIndex; // check if desiredVariant was specified diff --git a/src/Core/components/Map.js b/src/Core/components/Map.js index fb7e541d32..746db1a0b2 100644 --- a/src/Core/components/Map.js +++ b/src/Core/components/Map.js @@ -800,10 +800,10 @@ export default class Map extends CompositeComponent { serializedComponent, componentInfoObjects, }) { - let numberOfVariants = serializedComponent.variants?.numberOfVariants; + let numVariants = serializedComponent.variants?.numVariants; - if (numberOfVariants !== undefined) { - return { success: true, numberOfVariants }; + if (numVariants !== undefined) { + return { success: true, numVariants }; } let descendantVariantComponents = gatherVariantComponents({ @@ -811,7 +811,7 @@ export default class Map extends CompositeComponent { componentInfoObjects, }); - let numberOfVariantsByDescendant = []; + let numVariantsByDescendant = []; for (let descendant of descendantVariantComponents) { let descendantClass = componentInfoObjects.allComponentClasses[descendant.componentType]; @@ -822,20 +822,20 @@ export default class Map extends CompositeComponent { if (!result.success) { return { success: false }; } - numberOfVariantsByDescendant.push(result.numberOfVariants); + numVariantsByDescendant.push(result.numVariants); } if ( - numberOfVariantsByDescendant.length === 1 && - numberOfVariantsByDescendant[0] === 1 + numVariantsByDescendant.length === 1 && + numVariantsByDescendant[0] === 1 ) { // just have a template with one variant // so will have a single variant even if don't know how many times the template is repeated - serializedComponent.variants.numberOfVariants = 1; + serializedComponent.variants.numVariants = 1; return { success: true, - numberOfVariants: 1, + numVariants: 1, }; } @@ -847,15 +847,15 @@ export default class Map extends CompositeComponent { variantIndex, componentInfoObjects, }) { - let numberOfVariants = serializedComponent.variants?.numberOfVariants; - if (numberOfVariants === undefined) { + let numVariants = serializedComponent.variants?.numVariants; + if (numVariants === undefined) { return { success: false }; } if ( !Number.isInteger(variantIndex) || variantIndex < 1 || - variantIndex > numberOfVariants + variantIndex > numVariants ) { return { success: false }; } diff --git a/src/Core/components/Ref.js b/src/Core/components/Ref.js index d65d2ad565..b654b1429b 100644 --- a/src/Core/components/Ref.js +++ b/src/Core/components/Ref.js @@ -151,7 +151,7 @@ export default class Ref extends InlineComponent { forRenderer: true, additionalStateVariablesDefined: [ { - variableName: "doenetId", + variableName: "activityId", forRenderer: true, }, { @@ -185,7 +185,7 @@ export default class Ref extends InlineComponent { return { setValue: { cid: null, - doenetId: null, + activityId: null, variantIndex: null, edit: null, draft: null, @@ -195,19 +195,38 @@ export default class Ref extends InlineComponent { } let cid = null, - doenetId = null, + activityId = null, variantIndex = null; let draft = null, edit = null, hash = null; + let warnings = []; + let result = dependencyValues.uri.match(/[:&]cid=([^&^#]+)/i); if (result) { cid = result[1]; } - result = dependencyValues.uri.match(/[:&]doenetid=([^&^#]+)/i); + result = dependencyValues.uri.match(/[:&]activityId=([^&^#]+)/i); + if (result) { + activityId = result[1]; + } + result = dependencyValues.uri.match(/[:&]doenetId=([^&^#]+)/i); if (result) { - doenetId = result[1]; + if (activityId) { + warnings.push({ + message: + "The deprecated URI parameter doenetId is ignored as activityId is present.", + level: 1, + }); + } else { + warnings.push({ + message: + "The doenetId URI parameters is deprecated. Use activityId instead. Its will be ignored starting with the next major version (0.7). Version 0.6 will be phased out in summer 2024.", + level: 1, + }); + activityId = result[1]; + } } result = dependencyValues.uri.match(/[:&]variant=([^&^#]+)/i); if (result) { @@ -237,9 +256,10 @@ export default class Ref extends InlineComponent { hash = result[1]; } - // console.log('url parameter results', { cid, doenetId, variantIndex, edit, draft, hash }) - - return { setValue: { cid, doenetId, variantIndex, edit, draft, hash } }; + return { + setValue: { cid, activityId, variantIndex, edit, draft, hash }, + sendWarnings: warnings, + }; }, }; @@ -323,7 +343,7 @@ export default class Ref extends InlineComponent { } let cid = await this.stateValues.cid; - let doenetId = await this.stateValues.doenetId; + let activityId = await this.stateValues.activityId; let variantIndex = await this.stateValues.variantIndex; let edit = await this.stateValues.edit; let hash = await this.stateValues.hash; @@ -335,7 +355,7 @@ export default class Ref extends InlineComponent { this.coreFunctions.navigateToTarget({ cid, - doenetId, + activityId, variantIndex, edit, hash, diff --git a/src/Core/components/RenderDoenetML.js b/src/Core/components/RenderDoenetML.js deleted file mode 100644 index a96efd1ac7..0000000000 --- a/src/Core/components/RenderDoenetML.js +++ /dev/null @@ -1,177 +0,0 @@ -import BlockComponent from "./abstract/BlockComponent"; - -export default class RenderDoenetML extends BlockComponent { - constructor(args) { - super(args); - - Object.assign(this.actions, { - updateComponents: this.updateComponents.bind(this), - }); - } - static componentType = "renderDoenetML"; - - static excludeFromSchema = true; - - static createAttributesObject() { - let attributes = super.createAttributesObject(); - - attributes.assignNamesSkip = { - createPrimitiveOfType: "number", - }; - - attributes.codeSource = { - createTargetComponentNames: true, - }; - - return attributes; - } - - static returnStateVariableDefinitions() { - let stateVariableDefinitions = super.returnStateVariableDefinitions(); - - stateVariableDefinitions.codeSourceComponentName = { - returnDependencies: () => ({ - codeSource: { - dependencyType: "attributeTargetComponentNames", - attributeName: "codeSource", - }, - }), - definition({ dependencyValues }) { - let codeSourceComponentName; - - if (dependencyValues.codeSource?.length === 1) { - codeSourceComponentName = dependencyValues.codeSource[0].absoluteName; - } else { - codeSourceComponentName = null; - } - - return { setValue: { codeSourceComponentName } }; - }, - }; - - stateVariableDefinitions.codeSource = { - returnDependencies: () => ({ - codeSourceComponentName: { - dependencyType: "stateVariable", - variableName: "codeSourceComponentName", - }, - parentCodeSource: { - dependencyType: "parentStateVariable", - parentComponentType: "codeViewer", - variableName: "codeSource", - }, - }), - definition: function ({ dependencyValues }) { - if (dependencyValues.codeSourceComponentName) { - return { - setValue: { codeSource: dependencyValues.codeSourceComponentName }, - }; - } else if (dependencyValues.parentCodeSource) { - return { - setValue: { codeSource: dependencyValues.parentCodeSource }, - }; - } else { - return { setValue: { codeSource: null } }; - } - }, - }; - - stateVariableDefinitions.doenetMLFromSource = { - stateVariablesDeterminingDependencies: ["codeSource"], - returnDependencies: ({ stateValues }) => ({ - doenetML: { - dependencyType: "stateVariable", - componentName: stateValues.codeSource, - variableName: "text", - variablesOptional: true, - }, - }), - definition({ dependencyValues }) { - let doenetML = ""; - - if (dependencyValues.doenetML) { - doenetML = dependencyValues.doenetML; - if (typeof doenetML !== "string") { - doenetML = ""; - } - } - - return { setValue: { doenetMLFromSource: doenetML } }; - }, - }; - - stateVariableDefinitions.doenetML = { - forRenderer: true, - hasEssential: true, - doNotShadowEssential: true, - provideEssentialValuesInDefinition: true, - returnDependencies: () => ({ - doenetMLFromSource: { - dependencyType: "stateVariable", - variableName: "doenetMLFromSource", - }, - }), - definition({ dependencyValues, essentialValues }) { - let result = { - useEssentialOrDefaultValue: { - doenetML: { - defaultValue: dependencyValues.doenetMLFromSource, - }, - }, - }; - if (essentialValues.doenetML === undefined) { - result.setEssentialValue = { - doenetML: dependencyValues.doenetMLFromSource, - }; - } - return result; - }, - inverseDefinition({ desiredStateVariableValues }) { - return { - success: true, - instructions: [ - { - setEssentialValue: "doenetML", - value: desiredStateVariableValues.doenetML, - }, - ], - }; - }, - }; - - return stateVariableDefinitions; - } - - async updateComponents({ - actionId, - sourceInformation = {}, - skipRendererUpdate = false, - }) { - let updateInstructions = [ - { - updateType: "updateValue", - componentName: this.componentName, - stateVariable: "doenetML", - value: await this.stateValues.doenetMLFromSource, - }, - ]; - - await this.coreFunctions.performUpdate({ - updateInstructions, - actionId, - sourceInformation, - skipRendererUpdate, - // event: { - // verb: "selected", - // object: { - // componentName: this.componentName, - // componentType: this.componentType, - // }, - // result: { - // response: newValue, - // responseText: newValue.toString(), - // } - // }, - }); - } -} diff --git a/src/Core/components/Select.js b/src/Core/components/Select.js index eaac2ac3a4..553b4226e3 100644 --- a/src/Core/components/Select.js +++ b/src/Core/components/Select.js @@ -693,10 +693,10 @@ export default class Select extends CompositeComponent { serializedComponent, componentInfoObjects, }) { - let numberOfVariants = serializedComponent.variants?.numberOfVariants; + let numVariants = serializedComponent.variants?.numVariants; - if (numberOfVariants !== undefined) { - return { success: true, numberOfVariants }; + if (numVariants !== undefined) { + return { success: true, numVariants }; } let numToSelect = 1, @@ -783,7 +783,7 @@ export default class Select extends CompositeComponent { serializedComponent.variants.descendantVariantComponents = descendantVariantComponents; - let numberOfVariantsByChild = []; + let numVariantsByChild = []; for (let descendant of descendantVariantComponents) { let descendantClass = componentInfoObjects.allComponentClasses[descendant.componentType]; @@ -794,31 +794,31 @@ export default class Select extends CompositeComponent { if (!result.success) { return { success: false }; } - numberOfVariantsByChild.push(result.numberOfVariants); + numVariantsByChild.push(result.numVariants); } let uniqueVariantData = { - numberOfVariantsByChild, + numVariantsByChild, numToSelect, withReplacement, }; - if (numberOfVariantsByChild.length === 0) { - numberOfVariants = 1; + if (numVariantsByChild.length === 0) { + numVariants = 1; } else if (withReplacement || numToSelect === 1) { - let numberOfOptionsPerSelection = numberOfVariantsByChild.reduce( + let numberOfOptionsPerSelection = numVariantsByChild.reduce( (a, c) => a + c, ); - numberOfVariants = Math.pow(numberOfOptionsPerSelection, numToSelect); + numVariants = Math.pow(numberOfOptionsPerSelection, numToSelect); } else { - let numChildren = numberOfVariantsByChild.length; + let numChildren = numVariantsByChild.length; if (numToSelect > numChildren) { return { success: false }; } - let firstNumber = numberOfVariantsByChild[0]; - let allSameNumber = numberOfVariantsByChild + let firstNumber = numVariantsByChild[0]; + let allSameNumber = numVariantsByChild .slice(1) .every((x) => x === firstNumber); @@ -827,25 +827,24 @@ export default class Select extends CompositeComponent { for (let n = numChildren - 1; n > numChildren - numToSelect; n--) { numberOfPermutations *= n; } - numberOfVariants = - numberOfPermutations * Math.pow(firstNumber, numToSelect); + numVariants = numberOfPermutations * Math.pow(firstNumber, numToSelect); } else { // have select without replacement where options have different numbers of variants - numberOfVariants = countOptions(numberOfVariantsByChild, numToSelect); + numVariants = countOptions(numVariantsByChild, numToSelect); } } - if (!(numberOfVariants > 0)) { + if (!(numVariants > 0)) { return { success: false }; } - serializedComponent.variants.numberOfVariants = numberOfVariants; + serializedComponent.variants.numVariants = numVariants; serializedComponent.variants.uniqueVariants = true; serializedComponent.variants.uniqueVariantData = uniqueVariantData; return { success: true, - numberOfVariants, + numVariants, }; } @@ -854,27 +853,27 @@ export default class Select extends CompositeComponent { variantIndex, componentInfoObjects, }) { - let numberOfVariants = serializedComponent.variants?.numberOfVariants; - if (numberOfVariants === undefined) { + let numVariants = serializedComponent.variants?.numVariants; + if (numVariants === undefined) { return { success: false }; } if ( !Number.isInteger(variantIndex) || variantIndex < 1 || - variantIndex > numberOfVariants + variantIndex > numVariants ) { return { success: false }; } let uniqueVariantData = serializedComponent.variants.uniqueVariantData; - let numberOfVariantsByChild = uniqueVariantData.numberOfVariantsByChild; + let numVariantsByChild = uniqueVariantData.numVariantsByChild; let numToSelect = uniqueVariantData.numToSelect; let withReplacement = uniqueVariantData.withReplacement; let numChildren = serializedComponent.children.length; let childrenToSelect = serializedComponent.children; - if (numberOfVariantsByChild.length === 0) { + if (numVariantsByChild.length === 0) { return { success: true, desiredVariant: { indices: [] } }; } @@ -899,13 +898,13 @@ export default class Select extends CompositeComponent { let combinationsAvailable = combinations.map((x) => ({ combination: x, numberOfPossibilities: x.reduce( - (a, c) => a * numberOfVariantsByChild[c], + (a, c) => a * numVariantsByChild[c], 1, ), })); // console.log(combinationsAvailable); - // console.log(numberOfVariantsByChild); + // console.log(numVariantsByChild); // The variants, in order, will // select the first possibility from each combination @@ -958,7 +957,7 @@ export default class Select extends CompositeComponent { let indicesForEachChild = enumerateCombinations({ numberOfOptionsByIndex: selectedCombination.map( - (x) => numberOfVariantsByChild[x], + (x) => numVariantsByChild[x], ), maxNumber: variantIndexOfSelected, })[variantIndexOfSelected - 1].map((x) => x + 1); @@ -973,7 +972,7 @@ export default class Select extends CompositeComponent { let haveNontrivialSubvariants = false; for (let [ind, childNum] of selectedCombination.entries()) { - if (numberOfVariantsByChild[childNum] > 1) { + if (numVariantsByChild[childNum] > 1) { let child = childrenToSelect[childNum]; let compClass = componentInfoObjects.allComponentClasses[child.componentType]; diff --git a/src/Core/components/SelectFromSequence.js b/src/Core/components/SelectFromSequence.js index 6e42f7cb78..53bb5f35a2 100644 --- a/src/Core/components/SelectFromSequence.js +++ b/src/Core/components/SelectFromSequence.js @@ -657,39 +657,39 @@ export default class SelectFromSequence extends Sequence { serializedComponent.variants.uniqueVariantData = uniqueVariantData; - let numberOfVariants; + let numVariants; if (withReplacement || numToSelect === 1) { - numberOfVariants = Math.pow(nOptions, numToSelect); + numVariants = Math.pow(nOptions, numToSelect); } else { - numberOfVariants = nOptions; + numVariants = nOptions; for (let n = nOptions - 1; n > nOptions - numToSelect; n--) { - numberOfVariants *= n; + numVariants *= n; } } - if (!(numberOfVariants > 0)) { + if (!(numVariants > 0)) { return { success: false }; } - serializedComponent.variants.numberOfVariants = numberOfVariants; + serializedComponent.variants.numVariants = numVariants; return { success: true, - numberOfVariants: numberOfVariants, + numVariants: numVariants, }; } static getUniqueVariant({ serializedComponent, variantIndex }) { - let numberOfVariants = serializedComponent.variants?.numberOfVariants; - if (numberOfVariants === undefined) { + let numVariants = serializedComponent.variants?.numVariants; + if (numVariants === undefined) { return { success: false }; } if ( !Number.isInteger(variantIndex) || variantIndex < 1 || - variantIndex > numberOfVariants + variantIndex > numVariants ) { return { success: false }; } diff --git a/src/Core/components/SelectPrimeNumbers.js b/src/Core/components/SelectPrimeNumbers.js index 2b4787612c..a8b0446d1b 100644 --- a/src/Core/components/SelectPrimeNumbers.js +++ b/src/Core/components/SelectPrimeNumbers.js @@ -447,39 +447,39 @@ export default class SelectPrimeNumbers extends CompositeComponent { serializedComponent.variants.uniqueVariantData = uniqueVariantData; - let numberOfVariants; + let numVariants; if (withReplacement || numToSelect === 1) { - numberOfVariants = Math.pow(primes.length, numToSelect); + numVariants = Math.pow(primes.length, numToSelect); } else { - numberOfVariants = primes.length; + numVariants = primes.length; for (let n = primes.length - 1; n > primes.length - numToSelect; n--) { - numberOfVariants *= n; + numVariants *= n; } } - if (!(numberOfVariants > 0)) { + if (!(numVariants > 0)) { return { success: false }; } - serializedComponent.variants.numberOfVariants = numberOfVariants; + serializedComponent.variants.numVariants = numVariants; return { success: true, - numberOfVariants: numberOfVariants, + numVariants: numVariants, }; } static getUniqueVariant({ serializedComponent, variantIndex }) { - let numberOfVariants = serializedComponent.variants?.numberOfVariants; - if (numberOfVariants === undefined) { + let numVariants = serializedComponent.variants?.numVariants; + if (numVariants === undefined) { return { success: false }; } if ( !Number.isInteger(variantIndex) || variantIndex < 1 || - variantIndex > numberOfVariants + variantIndex > numVariants ) { return { success: false }; } diff --git a/src/Core/components/Shuffle.js b/src/Core/components/Shuffle.js index f36522359f..57073f4c72 100644 --- a/src/Core/components/Shuffle.js +++ b/src/Core/components/Shuffle.js @@ -390,23 +390,22 @@ export default class Shuffle extends CompositeComponent { return { success: false }; } - let numberOfVariants = result.numberOfVariants * numberOfPermutations; + let numVariants = result.numVariants * numberOfPermutations; - if (!(numberOfVariants > 0)) { + if (!(numVariants > 0)) { return { success: false }; } // adjust variants info added by call to super - serializedComponent.variants.numberOfVariants = numberOfVariants; + serializedComponent.variants.numVariants = numVariants; serializedComponent.variants.uniqueVariantData = { - numberOfVariantsByDescendant: - serializedComponent.variants.uniqueVariantData - .numberOfVariantsByDescendant, + numVariantsByDescendant: + serializedComponent.variants.uniqueVariantData.numVariantsByDescendant, numberOfPermutations, numComponents, }; - return { success: true, numberOfVariants }; + return { success: true, numVariants }; } static getUniqueVariant({ @@ -414,22 +413,21 @@ export default class Shuffle extends CompositeComponent { variantIndex, componentInfoObjects, }) { - let numberOfVariants = serializedComponent.variants?.numberOfVariants; - if (numberOfVariants === undefined) { + let numVariants = serializedComponent.variants?.numVariants; + if (numVariants === undefined) { return { success: false }; } if ( !Number.isInteger(variantIndex) || variantIndex < 1 || - variantIndex > numberOfVariants + variantIndex > numVariants ) { return { success: false }; } - let numberOfVariantsByDescendant = - serializedComponent.variants.uniqueVariantData - .numberOfVariantsByDescendant; + let numVariantsByDescendant = + serializedComponent.variants.uniqueVariantData.numVariantsByDescendant; let descendantVariantComponents = serializedComponent.variants.descendantVariantComponents; let numberOfPermutations = @@ -438,7 +436,7 @@ export default class Shuffle extends CompositeComponent { serializedComponent.variants.uniqueVariantData.numComponents; // treat permutations as another descendant variant component - let numbersOfOptions = [...numberOfVariantsByDescendant]; + let numbersOfOptions = [...numVariantsByDescendant]; numbersOfOptions.push(numberOfPermutations); let indicesForEachOption = enumerateCombinations({ @@ -466,10 +464,10 @@ export default class Shuffle extends CompositeComponent { for ( let descendantNum = 0; - descendantNum < numberOfVariantsByDescendant.length; + descendantNum < numVariantsByDescendant.length; descendantNum++ ) { - if (numberOfVariantsByDescendant[descendantNum] > 1) { + if (numVariantsByDescendant[descendantNum] > 1) { let descendant = descendantVariantComponents[descendantNum]; let compClass = componentInfoObjects.allComponentClasses[descendant.componentType]; diff --git a/src/Core/components/abstract/BaseComponent.js b/src/Core/components/abstract/BaseComponent.js index b19305a031..ba18738dde 100644 --- a/src/Core/components/abstract/BaseComponent.js +++ b/src/Core/components/abstract/BaseComponent.js @@ -1414,10 +1414,10 @@ export default class BaseComponent { serializedComponent, componentInfoObjects, }) { - let numberOfVariants = serializedComponent.variants?.numberOfVariants; + let numVariants = serializedComponent.variants?.numVariants; - if (numberOfVariants !== undefined) { - return { success: true, numberOfVariants }; + if (numVariants !== undefined) { + return { success: true, numVariants }; } let descendantVariantComponents = []; @@ -1438,9 +1438,9 @@ export default class BaseComponent { // number of variants is the product of // number of variants for each descendantVariantComponent - numberOfVariants = 1; + numVariants = 1; - let numberOfVariantsByDescendant = []; + let numVariantsByDescendant = []; for (let descendant of descendantVariantComponents) { let descendantClass = componentInfoObjects.allComponentClasses[descendant.componentType]; @@ -1451,20 +1451,20 @@ export default class BaseComponent { if (!result.success) { return { success: false }; } - numberOfVariantsByDescendant.push(result.numberOfVariants); - numberOfVariants *= result.numberOfVariants; + numVariantsByDescendant.push(result.numVariants); + numVariants *= result.numVariants; } - if (!(numberOfVariants > 0)) { + if (!(numVariants > 0)) { return { success: false }; } - serializedComponent.variants.numberOfVariants = numberOfVariants; + serializedComponent.variants.numVariants = numVariants; serializedComponent.variants.uniqueVariantData = { - numberOfVariantsByDescendant, + numVariantsByDescendant, }; - return { success: true, numberOfVariants }; + return { success: true, numVariants }; } static getUniqueVariant({ @@ -1472,24 +1472,23 @@ export default class BaseComponent { variantIndex, componentInfoObjects, }) { - let numberOfVariants = serializedComponent.variants?.numberOfVariants; - if (numberOfVariants === undefined) { + let numVariants = serializedComponent.variants?.numVariants; + if (numVariants === undefined) { return { success: false }; } if ( !Number.isInteger(variantIndex) || variantIndex < 1 || - variantIndex > numberOfVariants + variantIndex > numVariants ) { return { success: false }; } let haveNontrivialSubvariants = false; - let numberOfVariantsByDescendant = - serializedComponent.variants.uniqueVariantData - .numberOfVariantsByDescendant; + let numVariantsByDescendant = + serializedComponent.variants.uniqueVariantData.numVariantsByDescendant; let descendantVariantComponents = serializedComponent.variants.descendantVariantComponents; @@ -1497,7 +1496,7 @@ export default class BaseComponent { if (descendantVariantComponents.length > 0) { let indicesForEachDescendant = enumerateCombinations({ - numberOfOptionsByIndex: numberOfVariantsByDescendant, + numberOfOptionsByIndex: numVariantsByDescendant, maxNumber: variantIndex, })[variantIndex - 1].map((x) => x + 1); @@ -1506,10 +1505,10 @@ export default class BaseComponent { for ( let descendantNum = 0; - descendantNum < numberOfVariantsByDescendant.length; + descendantNum < numVariantsByDescendant.length; descendantNum++ ) { - if (numberOfVariantsByDescendant[descendantNum] > 1) { + if (numVariantsByDescendant[descendantNum] > 1) { let descendant = descendantVariantComponents[descendantNum]; let compClass = componentInfoObjects.allComponentClasses[descendant.componentType]; diff --git a/src/Core/components/abstract/SectioningComponent.js b/src/Core/components/abstract/SectioningComponent.js index 74484437d0..6935353a20 100644 --- a/src/Core/components/abstract/SectioningComponent.js +++ b/src/Core/components/abstract/SectioningComponent.js @@ -1007,7 +1007,7 @@ export class SectioningComponent extends BlockComponent { return; } - let numVariants = serializedComponent.variants.numberOfVariants; + let numVariants = serializedComponent.variants.numVariants; let variantIndex; // check if desiredVariant was specified diff --git a/src/Core/utils/returnAllPossibleVariants.js b/src/Core/utils/returnAllPossibleVariants.js index 6ea55b9790..9c4147e30a 100644 --- a/src/Core/utils/returnAllPossibleVariants.js +++ b/src/Core/utils/returnAllPossibleVariants.js @@ -1,23 +1,18 @@ import { numberToLetters } from "./sequence.js"; import * as serializeFunctions from "./serializedStateProcessing.js"; import createComponentInfoObjects from "./componentInfoObjects.js"; -import { retrieveTextFileForCid } from "./retrieveTextFile.js"; -import { cidFromText } from "./cid.js"; -import { getNumberOfVariants } from "./variants.js"; - -export async function returnAllPossibleVariants({ cid, doenetML }) { - if (doenetML === undefined) { - doenetML = await retrieveTextFileForCid(cid, "doenet"); - } else if (!cid) { - cid = await cidFromText(doenetML); - } +import { getNumVariants } from "./variants.js"; +export async function returnAllPossibleVariants({ + doenetML, + serializedComponents: preliminarySerializedComponents, +}) { let componentInfoObjects = createComponentInfoObjects(); let { fullSerializedComponents } = await serializeFunctions.expandDoenetMLsToFullSerializedComponents({ - contentIds: [cid], doenetMLs: [doenetML], + preliminarySerializedComponents: [preliminarySerializedComponents], componentInfoObjects, }); @@ -27,12 +22,12 @@ export async function returnAllPossibleVariants({ cid, doenetML }) { let document = serializedComponents[0]; - let results = getNumberOfVariants({ + let results = getNumVariants({ serializedComponent: document, componentInfoObjects, }); - let numVariants = results.numberOfVariants; + let numVariants = results.numVariants; let allPossibleVariants; @@ -65,7 +60,7 @@ export async function returnAllPossibleVariants({ cid, doenetML }) { ); } - return { allPossibleVariants, doenetML, cid }; + return allPossibleVariants; } function indexToLowercaseLetters(index) { diff --git a/src/Core/utils/serializedStateProcessing.js b/src/Core/utils/serializedStateProcessing.js index 3cf6424872..1bff153eeb 100644 --- a/src/Core/utils/serializedStateProcessing.js +++ b/src/Core/utils/serializedStateProcessing.js @@ -9,8 +9,8 @@ import { retrieveTextFileForCid } from "./retrieveTextFile"; import { returnDeprecationMessage } from "./doenetMLversion"; export async function expandDoenetMLsToFullSerializedComponents({ - cids, doenetMLs, + preliminarySerializedComponents = [], componentInfoObjects, nPreviousDoenetMLs = 0, }) { @@ -23,11 +23,20 @@ export async function expandDoenetMLsToFullSerializedComponents({ for (let [ind, doenetML] of doenetMLs.entries()) { let errorsForDoenetML = []; let warningsForDoenetML = []; - - let result = parseAndCompile(doenetML); - let serializedComponents = result.components; - errorsForDoenetML.push(...result.errors); - warningsForDoenetML.push(...result.warnings); + let result; + + // if we happened to send in the parsed preliminary serialized components, + // then we don't need to parse the DoenetML again + let serializedComponents; + if (preliminarySerializedComponents[ind]) { + serializedComponents = JSON.parse( + JSON.stringify(preliminarySerializedComponents[ind]), + ); + } else { + result = parseAndCompile(doenetML); + serializedComponents = result.components; + errorsForDoenetML.push(...result.errors); + } serializedComponents = cleanIfHaveJustDocument(serializedComponents); @@ -38,7 +47,7 @@ export async function expandDoenetMLsToFullSerializedComponents({ result = correctComponentTypeCapitalization( serializedComponents, - componentInfoObjects.componentTypeLowerCaseMapping, + componentInfoObjects, ); errorsForDoenetML.push(...result.errors); warningsForDoenetML.push(...result.warnings); @@ -147,7 +156,6 @@ export async function expandDoenetMLsToFullSerializedComponents({ warnings: additionalWarnings, } = await expandDoenetMLsToFullSerializedComponents({ doenetMLs: newDoenetMLs, - cids: newCids, componentInfoObjects, nPreviousDoenetMLs: nPreviousDoenetMLs + doenetMLs.length, }); @@ -237,7 +245,6 @@ export async function expandDoenetMLsToFullSerializedComponents({ } return { - cids, fullSerializedComponents: arrayOfSerializedComponents, allDoenetMLs, errors, @@ -878,7 +885,8 @@ function cleanIfHaveJustDocument(serializedComponents) { function correctComponentTypeCapitalization( serializedComponents, - componentTypeLowerCaseMapping, + componentInfoObjects, + ignoreErrors = false, ) { let errors = []; let warnings = []; @@ -891,23 +899,30 @@ function correctComponentTypeCapitalization( } let componentTypeFixed = - componentTypeLowerCaseMapping[component.componentType.toLowerCase()]; + componentInfoObjects.componentTypeLowerCaseMapping[ + component.componentType.toLowerCase() + ]; if (componentTypeFixed) { component.componentType = componentTypeFixed; } else { let message = `Invalid component type: <${component.componentType}>.`; convertToErrorComponent(component, message); - errors.push({ - message, - doenetMLrange: component.doenetMLrange, - }); + if (!ignoreErrors) { + errors.push({ + message, + doenetMLrange: component.doenetMLrange, + }); + } } if (component.children) { + let cClass = + componentInfoObjects.allComponentClasses[component.componentType]; let res = correctComponentTypeCapitalization( component.children, - componentTypeLowerCaseMapping, + componentInfoObjects, + ignoreErrors || cClass?.ignoreErrorsFromChildren, ); errors.push(...res.errors); warnings.push(...res.warnings); @@ -1310,7 +1325,11 @@ function breakUpTargetIntoPropsAndIndices( return { errors, warnings }; } -function createAttributesFromProps(serializedComponents, componentInfoObjects) { +function createAttributesFromProps( + serializedComponents, + componentInfoObjects, + ignoreErrors = false, +) { let errors = []; let warnings = []; @@ -1319,9 +1338,10 @@ function createAttributesFromProps(serializedComponents, componentInfoObjects) { continue; } + let componentClass = + componentInfoObjects.allComponentClasses[component.componentType]; + try { - let componentClass = - componentInfoObjects.allComponentClasses[component.componentType]; let classAttributes = componentClass.createAttributesObject(); let attributeLowerCaseMapping = {}; @@ -1405,17 +1425,22 @@ function createAttributesFromProps(serializedComponents, componentInfoObjects) { component.attributes = attributes; } catch (e) { convertToErrorComponent(component, e.message); - errors.push({ - message: e.message, - doenetMLrange: component.doenetMLrange, - }); + if (!ignoreErrors) { + errors.push({ + message: e.message, + doenetMLrange: component.doenetMLrange, + }); + } } //recurse on children if (component.children !== undefined) { + let ignoreErrorsInChildren = + ignoreErrors || componentClass.ignoreErrorsFromChildren; let res = createAttributesFromProps( component.children, componentInfoObjects, + ignoreErrorsInChildren, ); errors.push(...res.errors); warnings.push(...res.warnings); @@ -2067,7 +2092,6 @@ function createAttributesFromString(componentAttributes, componentInfoObjects) { componentsForAttributes = result.components; errors.push(...result.errors); - warnings.push(...result.warnings); } catch (e) { errors.push({ message: "Error in macro", @@ -2969,6 +2993,7 @@ export function createComponentNames({ indOffset = 0, createNameContext = "", initWithoutShadowingComposite = false, + ignoreErrors = false, }) { let errors = []; let warnings = []; @@ -3465,6 +3490,9 @@ export function createComponentNames({ if (serializedComponent.children) { // recurse on child, creating new namespace if specified + let ignoreErrorsInChildren = + ignoreErrors || componentClass.ignoreErrorsFromChildren; + if (!(newNamespace || attributes.assignNewNamespaces?.primitive)) { let children = serializedComponent.children; @@ -3489,6 +3517,7 @@ export function createComponentNames({ parentName: componentName, useOriginalNames, attributesByTargetComponentName, + ignoreErrors: ignoreErrorsInChildren, }); errors.push(...res.errors); warnings.push(...res.warnings); @@ -3505,6 +3534,7 @@ export function createComponentNames({ parentName: componentName, useOriginalNames, attributesByTargetComponentName, + ignoreErrors: ignoreErrorsInChildren, }); errors.push(...res.errors); warnings.push(...res.warnings); @@ -3543,6 +3573,7 @@ export function createComponentNames({ parentName: componentName, useOriginalNames, attributesByTargetComponentName, + ignoreErrors: ignoreErrorsInChildren, }); errors.push(...res.errors); warnings.push(...res.warnings); @@ -3599,6 +3630,7 @@ export function createComponentNames({ parentName: componentName, useOriginalNames, attributesByTargetComponentName, + ignoreErrors: ignoreErrorsInChildren, }); errors.push(...res.errors); warnings.push(...res.warnings); @@ -3619,6 +3651,7 @@ export function createComponentNames({ parentName: componentName, useOriginalNames, attributesByTargetComponentName, + ignoreErrors: ignoreErrorsInChildren, }); errors.push(...res.errors); warnings.push(...res.warnings); @@ -3654,6 +3687,7 @@ export function createComponentNames({ useOriginalNames, attributesByTargetComponentName, createNameContext: attrName, + ignoreErrors, }); errors.push(...res.errors); warnings.push(...res.warnings); @@ -3670,6 +3704,7 @@ export function createComponentNames({ useOriginalNames, attributesByTargetComponentName, createNameContext: attrName, + ignoreErrors, }); errors.push(...res.errors); warnings.push(...res.warnings); @@ -3682,10 +3717,12 @@ export function createComponentNames({ if (foundError) { convertToErrorComponent(serializedComponent, errorMessage); - errors.push({ - message: errorMessage, - doenetMLrange: serializedComponent.doenetMLrange, - }); + if (!ignoreErrors) { + errors.push({ + message: errorMessage, + doenetMLrange: serializedComponent.doenetMLrange, + }); + } } } diff --git a/src/Core/utils/variants.js b/src/Core/utils/variants.js index 14ca5b9786..2a31fd6afa 100644 --- a/src/Core/utils/variants.js +++ b/src/Core/utils/variants.js @@ -13,12 +13,12 @@ export function getVariantsForDescendantsForUniqueVariants({ return { success: false }; } - let numberOfVariantsByDescendant = descendantVariantComponents.map( - (x) => x.variants.numberOfVariants, + let numVariantsByDescendant = descendantVariantComponents.map( + (x) => x.variants.numVariants, ); let indices = enumerateCombinations({ - numberOfOptionsByIndex: numberOfVariantsByDescendant, + numberOfOptionsByIndex: numVariantsByDescendant, maxNumber: variantIndex, })[variantIndex - 1]; @@ -172,10 +172,7 @@ export function gatherVariantComponents({ return variantComponents; } -export function getNumberOfVariants({ - serializedComponent, - componentInfoObjects, -}) { +export function getNumVariants({ serializedComponent, componentInfoObjects }) { // get number of variants from document (or other sectioning component) if (!serializedComponent.variants) { @@ -213,7 +210,7 @@ export function getNumberOfVariants({ ) { let sectionChild = nonBlankChildren[0]; - let results = getNumberOfVariants({ + let results = getNumVariants({ serializedComponent: sectionChild, componentInfoObjects, }); @@ -226,8 +223,8 @@ export function getNumberOfVariants({ }); serializedComponent.variants.uniqueVariants = true; - serializedComponent.variants.numberOfVariants = - sectionChild.variants.numberOfVariants; + serializedComponent.variants.numVariants = + sectionChild.variants.numVariants; serializedComponent.variants.allPossibleVariants = sectionChild.variants.allPossibleVariants; serializedComponent.variants.allVariantNames = @@ -432,11 +429,11 @@ export function determineVariantsForSection({ componentInfoObjects, }); - if (!uniqueResult.success || !(uniqueResult.numberOfVariants > 0)) { + if (!uniqueResult.success || !(uniqueResult.numVariants > 0)) { uniqueVariants = false; } else { uniqueVariants = - uniqueVariants || uniqueResult.numberOfVariants <= numVariantsSpecified; + uniqueVariants || uniqueResult.numVariants <= numVariantsSpecified; } } @@ -446,7 +443,7 @@ export function determineVariantsForSection({ if (uniqueVariants) { for (let [ind, num] of variantsToIncludeUniqueIndices.entries()) { - if (num <= uniqueResult.numberOfVariants) { + if (num <= uniqueResult.numVariants) { allPossibleVariantUniqueIndices.push(num); allPossibleVariants.push(variantsToInclude[ind]); allPossibleVariantSeeds.push(variantsToIncludeSeeds[ind]); @@ -458,15 +455,15 @@ export function determineVariantsForSection({ allPossibleVariantSeeds = variantsToIncludeSeeds; } - let numberOfVariants = allPossibleVariants.length; - if (numberOfVariants === 0) { + let numVariants = allPossibleVariants.length; + if (numVariants === 0) { throw Error( "No variants selected based on variantsToInclude, variantsToExclude, and the number of variants available", ); } serializedComponent.variants.uniqueVariants = uniqueVariants; - serializedComponent.variants.numberOfVariants = numberOfVariants; + serializedComponent.variants.numVariants = numVariants; serializedComponent.variants.allPossibleVariants = allPossibleVariants; serializedComponent.variants.allVariantNames = variantNames; serializedComponent.variants.allPossibleVariantUniqueIndices = @@ -476,7 +473,7 @@ export function determineVariantsForSection({ return { success: true, - numberOfVariants, + numVariants, }; } diff --git a/src/Parser/parser.js b/src/Parser/parser.js index 54b415ea2b..a7e55d1b84 100644 --- a/src/Parser/parser.js +++ b/src/Parser/parser.js @@ -15,7 +15,6 @@ export function parse(inText) { */ export function parseAndCompile(inText) { let errors = []; - let warnings = []; function compileElement(cursor) { if (cursor.name !== "Element") { @@ -88,6 +87,12 @@ export function parseAndCompile(inText) { adjustedTagName = "_error"; } else { attrs[attrName] = true; + attrRanges[attrName] = { + attrBegin: beginAttributeInd, + attrEnd: cursor.to, + begin: beginAttributeInd, + end: cursor.to, + }; } } else { cursor.nextSibling(); @@ -104,6 +109,8 @@ export function parseAndCompile(inText) { } else { attrs[attrName] = attrValue; attrRanges[attrName] = { + attrBegin: beginAttributeInd, + attrEnd: cursor.to, begin: cursor.from + 2, end: cursor.to - 1, }; @@ -325,7 +332,12 @@ export function parseAndCompile(inText) { //fuddling to ignore the quotes let attrValue = inText.substring(cursor.from + 1, cursor.to - 1); attrs[attrName] = attrValue; - attrRanges[attrName] = { begin: cursor.from + 1, end: cursor.to }; + attrRanges[attrName] = { + attrBegin: beginAttributeInd, + attrEnd: cursor.to, + begin: cursor.from + 2, + end: cursor.to - 1, + }; } } //move out of Attribute to maintain loop invariant @@ -417,12 +429,12 @@ export function parseAndCompile(inText) { } } if (!inText) { - return { components: [], errors, warnings }; + return { components: [], errors }; } let tc = parse(inText); let out = []; if (!tc.firstChild()) { - return { components: out, errors, warnings }; + return { components: out, errors }; } // console.log("intext",inText) // console.log("showCursor",showCursor(tc)); @@ -438,7 +450,7 @@ export function parseAndCompile(inText) { } } - return { components: out, errors, warnings }; + return { components: out, errors }; } /** diff --git a/src/Tools/_framework/Footers/MathInputKeyboard.jsx b/src/Tools/_framework/Footers/MathInputKeyboard.jsx deleted file mode 100644 index e9ea1f0e6a..0000000000 --- a/src/Tools/_framework/Footers/MathInputKeyboard.jsx +++ /dev/null @@ -1,1717 +0,0 @@ -import React, { useState, useEffect } from "react"; -import styled from "styled-components"; -import { MathJax } from "better-react-mathjax"; -import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import ToggleButton from "../../../_reactComponents/PanelHeaderComponents/ToggleButton"; -import ToggleButtonGroup from "../../../_reactComponents/PanelHeaderComponents/ToggleButtonGroup"; -import VerticalDivider from "../../../_reactComponents/PanelHeaderComponents/VerticalDivider"; - -import { faBackspace, faArrowUp } from "@fortawesome/free-solid-svg-icons"; -import { useToast, toastType } from "../Toast"; - -import { useRecoilValue, useSetRecoilState } from "recoil"; - -import { - focusedMathField, - palletRef, - focusedMathFieldReturn, -} from "./MathInputSelector"; - -// import { doenetMainBlue } from '../../../_reactComponents/PanelHeaderComponents/theme'; - -import { useRef } from "react"; - -const Panel = styled.div` - height: 240px; - // position: fixed; - bottom: 0; - left: 0; - width: 100%; - background-color: var(--canvas); - color: var(--canvas); - display: flex; - flex-direction: row; - text-align: center; - justify-content: center; -`; - -const SubSection = styled.div` - display: flex; - flex-direction: row; - flex-wrap: no-wrap; - /* flex-basis: 27%; */ - flex-grow: 1; -`; - -const ContainerSection = styled.div` - display: grid; - flex-direction: row; - flex-wrap: no-wrap; - /* flex-basis: 27%; */ - /* flex-grow: 1; */ -`; - -const ControlSection = styled.div` - display: flex; - flex-direction: column; - flex-wrap: no-wrap; - flex-basis: 19%; -`; - -const ToggleButtonSection = styled.div` - margin-left: auto; - margin-right: auto; - margin-top: 10px; -`; - -const Section = styled.div` - height: 160px; - /* min-width: 100px; */ - /* max-width: 300px; */ - margin-left: auto; - margin-right: auto; - margin-top: auto; - margin-bottom: auto; - display: flex; - flex-wrap: wrap; - justify-content: space-evenly; -`; - -const LettersSection = styled.div` - height: 150px; - max-width: 700px; - flex-basis: 90%; - margin-left: 5px; - margin-right: 5px; - margin-top: auto; - margin-bottom: auto; - display: flex; - flex-wrap: wrap; - justify-content: space-evenly; -`; - -const Button = styled.button` - flex-basis: 18%; - height: 30px; - color: var(--mainBlue); - border: 2px solid var(--mainBlue); - background: white; - border-radius: 5px; -`; - -const Button33 = styled.button` - flex-basis: ${(props) => (props.alpha ? "20%" : "28%")}; - height: 30px; - color: ${(props) => - props.alpha || props.transition ? "white" : "var(--mainBlue)"}; - border: 2px solid var(--mainBlue); - border-radius: 5px; - background: ${(props) => - props.alpha || props.transition ? "var(--mainBlue)" : "white"}; -`; - -const Button44 = styled.button` - flex-basis: 24%; - height: 30px; - color: var(--mainBlue); - border: 2px solid var(--mainBlue); - border-radius: 5px; - background: white; -`; - -const White15Button = styled.button` - flex-basis: 14%; - margin: 1px; - height: 30px; - background: ${(props) => (props.lowercase ? "white" : "var(--mainBlue)")}; - border: ${(props) => - props.lowercase ? "2px solid var(--mainBlue)" : "none"}; - color: ${(props) => (props.lowercase ? "var(--mainBlue)" : "white")}; - border-radius: 5px; -`; - -const CursorButton = styled.button` - flex-basis: 18%; - height: 30px; - background: var(--mainBlue); - border: none; - color: white; - border-radius: 5px; -`; - -const DeleteButton = styled.button` - flex-basis: 18%; - height: 30px; - background: var(--mainBlue); - border: none; - color: white; - border-radius: 5px; -`; - -const EnterButton = styled.button` - flex-basis: 18%; - height: 30px; - background: var(--mainBlue); - border: none; - color: white; - border-radius: 5px; -`; - -const SpaceButton = styled.button` - flex-basis: 49%; - margin: 1px; - height: 30px; - background: white; - border: 2px solid var(--mainBlue); - color: var(--mainBlue); - border-radius: 5px; -`; - -const White20Button = styled.button` - flex-basis: 19%; - margin: 1px; - height: 30px; - background: var(--mainBlue); - border: none; - color: white; - border-radius: 5px; -`; - -const LetterButton = styled.button` - flex-basis: 9.5%; - margin: 1px; - height: 30px; - color: ${(props) => (props.transition ? "white" : "var(--mainBlue)")}; - border: ${(props) => - props.transition ? "none" : "2px solid var(--mainBlue)"}; - background: ${(props) => (props.transition ? "var(--mainBlue)" : "white")}; - border-radius: 5px; -`; - -export default function VirtualKeyboard() { - const [toggleLetters, setToggleLetters] = useState(false); - const [toggleABCCase, setToggleABCCase] = useState(false); - const [toggleGreekCase, setToggleGreekCase] = useState(false); - const [toggleFn, setToggleFn] = useState(0); - const [toggleNumpad, setToggleNumpad] = useState(0); - const callback = useRecoilValue(focusedMathField); - const returncallback = useRecoilValue(focusedMathFieldReturn); - const setPalletRef = useSetRecoilState(palletRef); - const containerRef = useRef(null); - const addToast = useToast(); - - useEffect(() => { - setPalletRef({ ...containerRef }); - //console.log(">>> ref: ", containerRef, toggleButtonRef, functionTabRef) - setToggleFn(0); - // setToggleGreek(0); - setToggleNumpad(0); - }, [toggleLetters, setPalletRef]); - - const handleToggleLetters = () => { - setToggleLetters(!toggleLetters); - }; - - const handleToggleABCCase = () => { - setToggleABCCase(!toggleABCCase); - }; - - const handleToggleGreekCase = () => { - setToggleGreekCase(!toggleGreekCase); - }; - - const handleFnToggle = (val) => { - setToggleFn(val); - }; - - const handleNumpadToggle = (val) => { - setToggleNumpad(val); - }; - - // if (toggleLetters) { - // if (toggleCase) { - let sectionUpperABC = ( - - - callback("write Q")}>Q - callback("write W")}>W - callback("write E")}>E - callback("write R")}>R - callback("write T")}>T - callback("write Y")}>Y - callback("write U")}>U - callback("write I")}>I - callback("write O")}>O - callback("write P")}>P - callback("write A")}>A - callback("write S")}>S - callback("write D")}>D - callback("write F")}>F - callback("write G")}>G - callback("write H")}>H - callback("write J")}>J - callback("write K")}>K - callback("write L")}>L - - - - callback("write Z")}>Z - callback("write X")}>X - callback("write C")}>C - callback("write V")}>V - callback("write B")}>B - callback("write N")}>N - callback("write M")}>M - callback("keystroke Backspace")}> - - - callback("write ,")}>, - callback("write '")}>' - callback("write \\ ")}> - callback("keystroke Left")}> - \(\leftarrow\) - - callback("keystroke Right")}> - \(\rightarrow\) - - returncallback()}> - Enter - - - - ); - // } else { - let sectionLowerABC = ( - - - callback("write q")}>q - callback("write w")}>w - callback("write e")}>e - callback("write r")}>r - callback("write t")}>t - callback("write y")}>y - callback("write u")}>u - callback("write i")}>i - callback("write o")}>o - callback("write p")}>p - callback("write a")}>a - callback("write s")}>s - callback("write d")}>d - callback("write f")}>f - callback("write g")}>g - callback("write h")}>h - callback("write j")}>j - callback("write k")}>k - callback("write l")}>l - - - - callback("write z")}>z - callback("write x")}>x - callback("write c")}>c - callback("write v")}>v - callback("write b")}>b - callback("write n")}>n - callback("write m")}>m - callback("keystroke Backspace")}> - - - callback("write ,")}>, - callback("write '")}>' - callback("write \\ ")}> - callback("keystroke Left")}> - \(\leftarrow\) - - callback("keystroke Right")}> - \(\rightarrow\) - - returncallback()}> - Enter - - - - ); - // } - // } - let sectionSymbols1 = ( -
- - - - - - - - - - - - - - - - {/* - - - - */} -
- ); - - let sectionSymbols2 = ( -
- - - - - - - - - - - - - - - callback("keystroke Backspace")}> - - - - {/* */} - callback("keystroke Left")}> - \(\leftarrow\) - - callback("keystroke Right")}> - \(\rightarrow\) - - returncallback()}>Enter -
- ); - - let sectionTrig1 = ( -
- callback("type sin(")}> - \(\sin\) - - callback("type cos(")}> - \(\cos\) - - callback("type tan(")}> - \(\tan\) - - { - callback("write \\sin^{-1}"); - callback("type ("); - }} - > - {`\\(\\sin^{-1}\\)`} - - { - callback("write \\cos^{-1}"); - callback("type ("); - }} - > - {`\\(\\cos^{-1}\\)`} - - { - callback("write \\tan^{-1}"); - callback("type ("); - }} - > - {`\\(\\tan^{-1}\\)`} - - callback("type ln(")}> - \(\ln\) - - { - callback("write \\log_{}"); - callback("keystroke Left"); - }} - > - \(\log_b\) - - callback("write \\log_{10}")}> - {`\\(\\log_{10}\\)`} - - { - callback("write e^{}"); - callback("keystroke Left"); - }} - > - {`\\(e^{a}\\)`} - - { - callback("write 10^{}"); - callback("keystroke Left"); - }} - > - {`\\(10^{a}\\)`} - - { - callback("write \\sqrt[]{}"); - callback("keystroke Left"); - callback("keystroke Left"); - }} - > - {`\\(\\sqrt[b]{a}\\)`} - -
- ); - let sectionTrig2 = ( -
- callback("cmd \\csc")}> - \(\csc\) - - callback("cmd \\sec")}> - \(\sec\) - - callback("cmd \\cot")}> - \(\cot\) - - callback("write \\csc^{-1}")}> - {`\\(\\csc^{-1}\\)`} - - callback("write \\sec^{-1}")}> - {`\\(\\sec^{-1}\\)`} - - callback("write \\cot^{-1}")}> - {`\\(\\cot^{-1}\\)`} - - callback("cmd \\csch")}> - \(\csch\) - - callback("cmd \\coth")}> - \(\coth\) - - callback("cmd \\sech")}> - \(\sech\) - -
- ); - let sectionFn = ( -
- callback("write \\frac{\\partial}{\\partial{x}}")} - > - {`\\(\\frac{\\partial}{\\partial x}\\)`} - - callback("write \\int")}> - \(\int\) - - callback("write \\frac{d}{dx}")}> - {`\\(\\frac{d}{dx}\\)`} - - callback("write \\log_{}")}> - \(\log_ab\) - - callback("cmd \\ln")}> - \(\ln\) - - { - callback("write e^{}"); - callback("keystroke Left"); - }} - > - {`\\(e^{x}\\)`} - - { - callback("write 10^{}"); - callback("keystroke Left"); - }} - > - {`\\(10^{x}\\)`} - -
- ); - let sectionFx = ( -
- callback("write \\frac{d}{dx}")}> - {`\\(\\frac{d}{dx}\\)`} - - {/* callback('write \\int')}> - \(\int\) - */} - { - callback("write \\int_{}^{}"); - callback("keystroke Left"); - callback("keystroke Left"); - }} - > - {`\\(\\int_{a}^{b}\\)`} - - callback("type nPr(")}> - {`\\(\\operatorname{nPr}\\)`} - - callback("type nCr(")}> - {`\\(\\operatorname{nCr}\\)`} - - callback("write !")}>! - { - callback("write \\lfloor"); - callback("write \\rfloor"); - callback("keystroke Left"); - }} - > - {`\\(\\lfloor{a}\\rfloor\\)`} - - { - callback("write \\lceil"); - callback("write \\rceil"); - callback("keystroke Left"); - }} - > - {`\\(\\lceil{a}\\rceil\\)`} - - callback("keystroke Backspace")}> - - - callback("keystroke Left")}> - \(\leftarrow\) - - callback("keystroke Right")}> - \(\rightarrow\) - - returncallback()}> - Enter - -
- ); - - let sectionGreekNone = null; - - let sectionGreek1 = ( -
- - - - - - - - - - - - - - - - - - - -
- ); - let sectionGreek2 = ( -
- - - - - - - - - - - - - - - - - - -
- ); - - let sectionUpperGreek = ( - - - callback("write \\Phi")}> - \(\Phi\) - - callback("write \\Sigma")}> - \(\Sigma\) - - callback("write E")}>E - callback("write P")}>P - callback("write T")}>T - callback("write Y")}>Y - callback("write \\Theta")}> - \(\Theta\) - - callback("write I")}>I - callback("write O")}>O - callback("write \\Pi")}> - \(\Pi\) - - callback("write A")}>A - callback("write \\Sigma")}> - \(\Sigma\) - - callback("write \\Delta")}> - \(\Delta\) - - callback("write \\Phi")}> - \(\Phi\) - - callback("write \\Gamma")}> - \(\Gamma\) - - callback("write H")}>H - callback("write \\Xi")}> - \(\Xi\) - - callback("write K")}>K - callback("write \\Lambda")}> - \(\Lambda\) - - - - - callback("write Z")}>Z - callback("write X")}>X - callback("write \\Psi")}> - \(\Psi\) - - callback("write \\Omega")}> - \(\Delta\) - - callback("write B")}>B - callback("write N")}>N - callback("write M")}>M - callback("keystroke Backspace")}> - - - callback("write ,")}>, - callback("write '")}>' - callback("write \\ ")}> - callback("keystroke Left")}> - \(\leftarrow\) - - callback("keystroke Right")}> - \(\rightarrow\) - - returncallback()}> - Enter - - - - ); - - let sectionLowerGreek = ( - - - callback("write \\phi")}> - \(\phi\) - - callback("write \\varsigma")}> - \(\varsigma\) - - callback("write \\epsilon")}> - \(\epsilon\) - - callback("write \\rho")}> - \(\rho\) - - callback("write \\tau")}> - \(\tau\) - - callback("write \\upsilon")}> - \(\upsilon\) - - callback("write \\theta")}> - \(\theta\) - - callback("write \\iota")}> - \(\iota\) - - callback("write o")}>o - callback("write \\pi")}> - \(\pi\) - - callback("write \\alpha")}> - \(\alpha\) - - callback("write \\sigma")}> - \(\sigma\) - - callback("write \\delta")}> - \(\delta\) - - callback("write \\varphi")}> - \(\varphi\) - - callback("write \\gamma")}> - \(\gamma\) - - callback("write \\eta")}> - \(\eta\) - - callback("write \\xi")}> - \(\xi\) - - callback("write \\kappa")}> - \(\kappa\) - - callback("write \\lambda")}> - \(\lambda\) - - - - - callback("write \\zeta")}> - \(\zeta\) - - callback("write \\chi")}> - \(\chi\) - - callback("write \\psi")}> - \(\psi\) - - callback("write \\omega")}> - \(\omega\) - - callback("write \\beta")}> - \(\beta\) - - callback("write \\nu")}> - \(\nu\) - - callback("write \\mu")}> - \(\mu\) - - callback("keystroke Backspace")}> - - - callback("write ,")}>, - callback("write '")}>' - callback("write \\ ")}> - callback("keystroke Left")}> - \(\leftarrow\) - - callback("keystroke Right")}> - \(\rightarrow\) - - returncallback()}> - Enter - - - - ); - - let sectionXYZ = ( -
- - - - - - - - - - - - - - - -
- ); - - let section123 = ( -
- - - - - - - - - - - - - - - callback("keystroke Backspace")}> - - - - - callback("keystroke Left")}> - \(\leftarrow\) - - callback("keystroke Right")}> - \(\rightarrow\) - - returncallback()}>Enter -
- ); - - let sectionControl = ( -
- {/* functions */} - callback("keystroke Left")}> - \(\leftarrow\) - - callback("keystroke Right")}> - \(\rightarrow\) - - callback("keystroke Backspace")}> - - - returncallback()}>Enter - ABC -
- ); - - return ( - - {/* - - - - - - - {toggleGreek === 0 - ? sectionGreek1 - : toggleGreek === 1 - ? sectionGreek2 - : null} - */} - {/* */} - - - - - - - - - - - {toggleFn === 0 ? ( - - {sectionXYZ} - {section123} - - ) : toggleFn === 1 ? ( - - {sectionTrig1} - {sectionFx} - - ) : toggleFn === 2 ? ( - toggleABCCase ? ( - sectionUpperABC - ) : ( - sectionLowerABC - ) - ) : toggleFn === 3 ? ( - toggleGreekCase ? ( - sectionUpperGreek - ) : ( - sectionLowerGreek - ) - ) : toggleFn === 4 ? ( - - {sectionSymbols1} - {sectionSymbols2} - - ) : null} - - {/* */} - - {/* - - - - - */} - {/* {toggleNumpad === 0 - ? section123 - : toggleNumpad === 1 - ? sectionXYZ - : null} */} - - {/* */} - {/* {sectionControl} */} - - ); - - // return ( - // <> - // - // {/* - // - // */} - // {toggleFunctions ? ( - // - // - // Trig - // - // - // Sets - // - // - // Misc - // - // {selectedTab === 'Trig' ? ( - // <> - // callback('cmd \\sin')}> - // - // - // callback('write \\sin^{-1}')}> - // - // - // callback('cmd \\sinh')}> - // - // - // callback('cmd \\tan')}> - // - // - // callback('write \\tan^{-1}')}> - // - // - // callback('cmd \\tanh')}> - // - // - // callback('cmd \\cos')}> - // - // - // callback('write \\cos^{-1}')}> - // - // - // callback('cmd \\cosh')}> - // - // - // callback('cmd \\csc')}> - // - // - // callback('write \\csc^{-1}')}> - // - // - // callback('cmd \\csch')}> - // - // - // callback('cmd \\cot')}> - // - // - // callback('write \\cot^{-1}')}> - // - // - // callback('cmd \\coth')}> - // - // - // callback('cmd \\sec')}> - // - // - // callback('write \\sec^{-1}')}> - // - // - // callback('cmd \\sech')}> - // - // - // - // ) : selectedTab === 'Sets' ? ( - // <> - // callback('write \\cup')}> - // - // - // callback('write \\cap')}> - // - // - // callback('write \\subset')}> - // - // - // callback('write \\supset')}> - // - // - // callback('write \\subseteq')}> - // - // - // callback('write \\supseteq')}> - // - // - // callback('write \\nsubseteq')}> - // - // - // callback('write \\nsupseteq')}> - // - // - // callback('write \\emptyset')}> - // - // - // - // ) : ( - // <> - // - // callback('write \\frac{\\partial}{\\partial{x}}') - // } - // > - // - // - // callback('write \\int')}> - // - // - // callback('write \\frac{d}{dx}')}> - // - // - // callback('write \\log_{}')}> - // - // - // callback('cmd \\ln')}> - // - // - // { - // callback('write e^{}'); - // callback('keystroke Left'); - // }} - // > - // - // - // { - // callback('write 10^{}'); - // callback('keystroke Left'); - // }} - // > - // - // - // - // )} - // - // ) : null} - // {toggleKeyboard ? ( - // toggleLetters ? ( - // toggleCase ? ( - // - // - // callback('write Q')}> - // Q - // - // callback('write W')}> - // W - // - // callback('write E')}> - // E - // - // callback('write R')}> - // R - // - // callback('write T')}> - // T - // - // callback('write Y')}> - // Y - // - // callback('write U')}> - // U - // - // callback('write I')}> - // I - // - // callback('write O')}> - // O - // - // callback('write P')}> - // P - // - // callback('write A')}> - // A - // - // callback('write S')}> - // S - // - // callback('write D')}> - // D - // - // callback('write F')}> - // F - // - // callback('write G')}> - // G - // - // callback('write H')}> - // H - // - // callback('write J')}> - // J - // - // callback('write K')}> - // K - // - // callback('write L')}> - // L - // - // callback('write \\tau')}> - // - // - // - // - // - // callback('write Z')}> - // Z - // - // callback('write X')}> - // X - // - // callback('write C')}> - // C - // - // callback('write V')}> - // V - // - // callback('write B')}> - // B - // - // callback('write N')}> - // N - // - // callback('write M')}> - // M - // - // callback('keystroke Backspace')}> - // - // - // 123 - // callback('cmd ^')}> - // - // - // callback('write %')}> - // % - // - // callback('cmd ]')}> - // ] - // - // callback('cmd }')} - // >{`}`} - // callback('write :')}> - // : - // - // callback("write '")}> - // ' - // - // returncallback()}> - // - // - // - // - // ) : ( - // - // - // callback('write q')}> - // q - // - // callback('write w')}> - // w - // - // callback('write e')}> - // e - // - // callback('write r')}> - // r - // - // callback('write t')}> - // t - // - // callback('write y')}> - // y - // - // callback('write u')}> - // u - // - // callback('write i')}> - // i - // - // callback('write o')}> - // o - // - // callback('write p')}> - // p - // - // callback('write a')}> - // a - // - // callback('write s')}> - // s - // - // callback('write d')}> - // d - // - // callback('write f')}> - // f - // - // callback('write g')}> - // g - // - // callback('write h')}> - // h - // - // callback('write j')}> - // j - // - // callback('write k')}> - // k - // - // callback('write l')}> - // l - // - // callback('write \\theta')}> - // - // - // - // - // - // callback('write z')}> - // z - // - // callback('write x')}> - // x - // - // callback('write c')}> - // c - // - // callback('write v')}> - // v - // - // callback('write b')}> - // b - // - // callback('write n')}> - // n - // - // callback('write m')}> - // m - // - // callback('keystroke Backspace')}> - // - // - // 123 - // callback('cmd _')}> - // - // - // callback('write !')}> - // ! - // - // callback('cmd [')}> - // [ - // - // callback('cmd {')} - // >{`{`} - // callback('write ~')}> - // ~ - // - // callback('write ,')}> - // , - // - // returncallback()}> - // - // - // - // - // ) - // ) : ( - // { - // // if ( - // // functionTabRef && - // // functionTabRef.current && - // // functionTabRef.current.contains(e.relatedTarget) - // // ) { - // // console.log('>>> clicked inside the panel functional panel'); - // // } else { - // // console.log('blurred'); - // // setToggleFunctions(false); - // // } - // // }} - // > - //
- // - //
- - //
- // - // - // - // - // - // - // - // - // - // - // - // - // - // - // - // - // - // - //
- //
- // callback('write 7')}> - // - // - // callback('write 8')}> - // - // - // callback('write 9')}> - // - // - // - // callback('write 4')}> - // - // - // callback('write 5')}> - // - // - // callback('write 6')}> - // - // - // - // callback('write 1')}> - // - // - // callback('write 2')}> - // - // - // callback('write 3')}> - // - // - // - // callback('write 0')}> - // - // - // callback('write .')}> - // - // - // - // - //
- //
- // - // functions - // - // callback('keystroke Left')}> - // - // - // callback('keystroke Right')}> - // - // - // callback('keystroke Backspace')}> - // - // - // returncallback()}> - // - // - //
- //
- // ) - // ) : null} - //
- // - // ); -} diff --git a/src/Tools/_framework/Menus/CreditAchieved.jsx b/src/Tools/_framework/Menus/CreditAchieved.jsx index 8ff134b7df..38ea6c1c65 100644 --- a/src/Tools/_framework/Menus/CreditAchieved.jsx +++ b/src/Tools/_framework/Menus/CreditAchieved.jsx @@ -9,15 +9,11 @@ import { searchParamAtomFamily } from "../NewToolRoot"; import axios from "axios"; import { creditAchievedAtom, + activityStatusAtom, currentAttemptNumber, } from "../ToolPanels/AssignmentViewer"; import styled from "styled-components"; -import { itemByDoenetId } from "../../../_reactComponents/Course/CourseActions"; -import { - activityAttemptNumberSetUpAtom, - currentPageAtom, - itemWeightsAtom, -} from "../../../Viewer/ActivityViewer"; + import { useLocation, useNavigate } from "react-router"; import { effectivePermissionsByCourseId } from "../../../_reactComponents/PanelHeaderComponents/RoleDropdown"; import Button from "../../../_reactComponents/PanelHeaderComponents/Button"; @@ -124,12 +120,7 @@ export default function CreditAchieved() { const recoilDoenetId = useRecoilValue(searchParamAtomFamily("doenetId")); const recoilUserId = useRecoilValue(searchParamAtomFamily("userId")); const recoilTool = useRecoilValue(searchParamAtomFamily("tool")); - const itemObj = useRecoilValue(itemByDoenetId(recoilDoenetId)); - const itemWeights = useRecoilValue(itemWeightsAtom); - const currentPage = useRecoilValue(currentPageAtom); - const activityAttemptNumberSetUp = useRecoilValue( - activityAttemptNumberSetUpAtom, - ); + const activityStatus = useRecoilValue(activityStatusAtom); let { search } = useLocation(); let navigate = useNavigate(); @@ -188,8 +179,8 @@ export default function CreditAchieved() { // wait for the assignment attempt item tables to be set up // so that will have the rows for each item - if (activityAttemptNumberSetUp !== recoilAttemptNumber) { - lastAttemptNumber.current = activityAttemptNumberSetUp; + if (activityStatus.activityAttemptNumberSetUp !== recoilAttemptNumber) { + lastAttemptNumber.current = activityStatus.activityAttemptNumberSetUp; return null; } @@ -210,7 +201,7 @@ export default function CreditAchieved() { let creditByItemsJSX = creditByItem.map((x, i) => { let scoreDisplay; - if (itemWeights[i] === 0) { + if (activityStatus.itemWeights[i] === 0) { scoreDisplay = x === 0 ? "Not started" : x === 1 ? "Complete" : "In progress"; } else { @@ -219,7 +210,7 @@ export default function CreditAchieved() { return ( navigate(search + `#page${i + 1}`)} isLink={true} > diff --git a/src/Tools/_framework/NewToolRoot.jsx b/src/Tools/_framework/NewToolRoot.jsx index d37cec32c4..810884e23c 100644 --- a/src/Tools/_framework/NewToolRoot.jsx +++ b/src/Tools/_framework/NewToolRoot.jsx @@ -16,7 +16,6 @@ import axios from "axios"; import MainPanel from "./Panels/NewMainPanel"; import SupportPanel from "./Panels/NewSupportPanel"; import MenuPanel from "./Panels/NewMenuPanel"; -import FooterPanel from "./Panels/FooterPanel"; import { animated } from "@react-spring/web"; import { useNavigate, useLocation } from "react-router"; @@ -93,7 +92,6 @@ export default function ToolRoot() { hasNoHeaderPanel: false, headerControls: [], displaySettings: true, - footer: null, }); let mainPanel = null; let supportPanel = null; @@ -176,10 +174,6 @@ export default function ToolRoot() { ), }).current; - const LazyFooterObj = useRef({ - MathInputKeyboard: lazy(() => import("./Footers/MathInputKeyboard")), - }).current; - let MainPanelKey = `${toolRootMenusAndPanels.pageName}-${toolRootMenusAndPanels.currentMainPanel}`; mainPanel = ( @@ -334,35 +328,6 @@ export default function ToolRoot() { openMenuButton = false; } - let footer = null; - - if (toolRootMenusAndPanels.footer) { - let footerKey = `footer`; - footer = ( - - loading...} - > - {React.createElement( - LazyFooterObj[toolRootMenusAndPanels.footer.component], - { - key: { footerKey }, - }, - )} - - - ); - } - - //

insert keyboard here

- return ( <> @@ -382,7 +347,6 @@ export default function ToolRoot() { hasNoHeaderPanel={toolRootMenusAndPanels.hasNoHeaderPanel} support={supportPanel} /> - {footer} @@ -406,7 +370,6 @@ export default function ToolRoot() { // hasNoMenuPanel: true, // headerControls:["BackButton"], // waitForMenuSuppression:true, -// footer: {height,open,component} // initialProportion: 1, // /umn/1271qual @@ -438,7 +401,6 @@ let navigationObj = { hasNoMenuPanel: true, hasNoHeaderPanel: true, waitForMenuSuppression: true, - footer: { height: 250, open: false, component: "MathInputKeyboard" }, }, endExam: { pageName: "endExam", @@ -482,7 +444,6 @@ let navigationObj = { supportPanelIndex: 0, headerControls: ["PortfolioBreadCrumb", "ViewerUpdateButton"], // onLeave: 'EditorLeave', - footer: { height: 250, open: false, component: "MathInputKeyboard" }, waitForMenuSuppression: true, }, }, @@ -505,7 +466,6 @@ let navigationObj = { headerControls: [], displaySettings: false, waitForMenuSuppression: true, - footer: { height: 250, open: false, component: "MathInputKeyboard" }, }, endExam: { pageName: "endExam", @@ -531,7 +491,6 @@ let navigationObj = { "ActivityNavigationButtons", ], waitForMenuSuppression: true, - footer: { height: 250, open: false, component: "MathInputKeyboard" }, }, courseChooser: { //allCourses @@ -563,7 +522,6 @@ let navigationObj = { menusTitles: ["Activity Variant"], menusInitOpen: [], headerControls: ["AssignmentBreadCrumb"], - footer: { height: 250, open: false, component: "MathInputKeyboard" }, }, endExam: { pageName: "endExam", @@ -668,7 +626,6 @@ let navigationObj = { supportPanelIndex: 0, headerControls: ["EditorBreadCrumb", "ViewerUpdateButton"], // onLeave: 'EditorLeave', - footer: { height: 250, open: false, component: "MathInputKeyboard" }, waitForMenuSuppression: true, }, people: { @@ -742,7 +699,6 @@ let navigationObj = { supportPanelTitles: ["DoenetML Editor"], supportPanelIndex: 0, headerControls: ["PublicNavigation", "ViewerUpdateButton"], - footer: { height: 250, open: false, component: "MathInputKeyboard" }, }, }, settings: { diff --git a/src/Tools/_framework/Panels/FooterPanel.jsx b/src/Tools/_framework/Panels/FooterPanel.jsx deleted file mode 100644 index 751c182e22..0000000000 --- a/src/Tools/_framework/Panels/FooterPanel.jsx +++ /dev/null @@ -1,20 +0,0 @@ -import React, { useState } from "react"; -import styled from "styled-components"; -import DragPanel, { handleDirection } from "./Panel"; - -export default function NavPanel({ children, id, isInitOpen, height = 120 }) { - const [visible, setVisible] = useState(isInitOpen); - - return ( - - {children} - - ); -} diff --git a/src/Tools/_framework/Paths/Home.jsx b/src/Tools/_framework/Paths/Home.jsx index 6eedb21499..94f0c3a009 100644 --- a/src/Tools/_framework/Paths/Home.jsx +++ b/src/Tools/_framework/Paths/Home.jsx @@ -1,6 +1,6 @@ import React, { lazy, Suspense, useEffect } from "react"; -import { redirect, useLoaderData, useOutletContext } from "react-router"; -import PageViewer from "../../../Viewer/PageViewer"; +import { useLoaderData, useOutletContext } from "react-router"; +import { DoenetML } from "../../../Viewer/DoenetML"; import { pageVariantInfoAtom, pageVariantPanelAtom, @@ -247,26 +247,29 @@ export function Home() {
- - - Explore - - - Interact with our existing content - - -
+ + + Explore + + + Interact with{" "} + + our existing content + + + +
- - - Learn - - - Designed for the In-Person Classroom - - -
+ + + Learn + + + Designed for the In-Person Classroom + + + - diff --git a/src/Tools/_framework/Paths/PortfolioActivityEditor.jsx b/src/Tools/_framework/Paths/PortfolioActivityEditor.jsx index 4c1aa5cb5d..94898491d8 100644 --- a/src/Tools/_framework/Paths/PortfolioActivityEditor.jsx +++ b/src/Tools/_framework/Paths/PortfolioActivityEditor.jsx @@ -2,7 +2,7 @@ import React, { useCallback, useEffect, useRef, useState } from "react"; import { redirect, useLoaderData } from "react-router"; import CodeMirror from "../CodeMirror"; -import PageViewer from "../../../Viewer/PageViewer"; +import { DoenetML } from "../../../Viewer/DoenetML"; import Papa from "papaparse"; import { useSetRecoilState } from "recoil"; @@ -72,9 +72,9 @@ import { textEditorDoenetMLAtom } from "../../../_sharedRecoil/EditorViewerRecoi import { HiOutlineX, HiPlus } from "react-icons/hi"; // import Select from "react-select"; import { useCourse } from "../../../_reactComponents/Course/CourseActions"; -import VirtualKeyboard from "../Footers/VirtualKeyboard"; import VariantSelect from "../ChakraBasedComponents/VariantSelect"; import ErrorWarningPopovers from "../ChakraBasedComponents/ErrorWarningPopovers"; +import { useLocation, useNavigate } from "react-router"; export async function action({ params, request }) { const formData = await request.formData(); @@ -1317,6 +1317,9 @@ export function PortfolioActivityEditor() { let inTheMiddleOfSaving = useRef(false); let postponedSaving = useRef(false); + let navigate = useNavigate(); + let location = useLocation(); + const { saveDraft } = useSaveDraft(); const handleSaveDraft = useCallback(async () => { @@ -1354,7 +1357,7 @@ export function PortfolioActivityEditor() { }, [pageId, courseId, saveDraft]); useEffect(() => { - const handleKeyDown = (event) => { + const handleEditorKeyDown = (event) => { if ( (platform == "Mac" && event.metaKey && event.code === "KeyS") || (platform != "Mac" && event.ctrlKey && event.code === "KeyS") @@ -1366,6 +1369,9 @@ export function PortfolioActivityEditor() { clearTimeout(timeout.current); handleSaveDraft(); } + }; + + const handleDocumentKeyDown = (event) => { if ( (platform == "Mac" && event.metaKey && event.code === "KeyU") || (platform != "Mac" && event.ctrlKey && event.code === "KeyU") @@ -1375,11 +1381,14 @@ export function PortfolioActivityEditor() { controlsOnOpen(); } }; + let codeEditorContainer = document.getElementById("codeEditorContainer"); - document.addEventListener("keydown", handleKeyDown); + document.addEventListener("keydown", handleDocumentKeyDown); + codeEditorContainer.addEventListener("keydown", handleEditorKeyDown); return () => { - document.removeEventListener("keydown", handleKeyDown); + document.removeEventListener("keydown", handleDocumentKeyDown); + codeEditorContainer.removeEventListener("keydown", handleEditorKeyDown); }; }, [textEditorDoenetML, controlsOnOpen, platform, handleSaveDraft]); @@ -1399,26 +1408,12 @@ export function PortfolioActivityEditor() { const [variants, setVariants] = useState({ index: 1, + numVariants: 1, allPossibleVariants: ["a"], }); - let variantOptions = []; - variants.allPossibleVariants.forEach((variant) => { - variantOptions.push({ value: variant, label: variant }); - }); // console.log("variants", variants); - function variantCallback(generatedVariantInfo, allPossibleVariants) { - // console.log(">>>variantCallback",generatedVariantInfo,allPossibleVariants) - const cleanGeneratedVariant = JSON.parse( - JSON.stringify(generatedVariantInfo), - ); - setVariants({ - index: cleanGeneratedVariant.index, - allPossibleVariants, - }); - } - return ( <> - - {variants.allPossibleVariants.length > 1 && ( + {variants.numVariants > 1 && ( <> - - @@ -1700,6 +1705,7 @@ export function PortfolioActivityEditor() { borderBottom="solid 1px" borderColor="doenet.mediumGray" w="100%" + id="codeEditorContainer" > - {variants.allPossibleVariants.length > 1 && ( + {variants.numVariants > 1 && ( 1 + variants.numVariants > 1 ? "calc(100vh - 132px)" : "calc(100vh - 100px)" } @@ -1813,9 +1819,10 @@ export function PortfolioActivityEditor() { flexGrow={1} overflow="scroll" w="100%" + id="viewer-container" > <> - - diff --git a/src/Tools/_framework/Paths/PortfolioActivityViewer.jsx b/src/Tools/_framework/Paths/PortfolioActivityViewer.jsx index e113c88ba7..336fac9dd6 100644 --- a/src/Tools/_framework/Paths/PortfolioActivityViewer.jsx +++ b/src/Tools/_framework/Paths/PortfolioActivityViewer.jsx @@ -1,7 +1,12 @@ import React, { useEffect, useRef, useState } from "react"; -import { redirect, useLoaderData, useNavigate } from "react-router"; +import { + redirect, + useLoaderData, + useNavigate, + useLocation, +} from "react-router"; import styled from "styled-components"; -import PageViewer from "../../../Viewer/PageViewer"; +import { DoenetML } from "../../../Viewer/DoenetML"; import { useRecoilState } from "recoil"; import { checkIfUserClearedOut } from "../../../_utils/applicationUtils"; @@ -17,7 +22,6 @@ import { } from "@chakra-ui/react"; import { pageToolViewAtom } from "../NewToolRoot"; import axios from "axios"; -import VirtualKeyboard from "../Footers/VirtualKeyboard"; import VariantSelect from "../ChakraBasedComponents/VariantSelect"; import findFirstPageIdInContent from "../../../_utils/findFirstPage"; import ContributorsMenu from "../ChakraBasedComponents/ContributorsMenu"; @@ -95,6 +99,7 @@ export function PortfolioActivityViewer() { } const navigate = useNavigate(); + const location = useLocation(); const [recoilPageToolView, setRecoilPageToolView] = useRecoilState(pageToolViewAtom); @@ -113,28 +118,12 @@ export function PortfolioActivityViewer() { const [variants, setVariants] = useState({ index: 1, + numVariants: 1, allPossibleVariants: ["a"], }); - let variantOptions = []; - variants.allPossibleVariants.forEach((variant) => { - variantOptions.push({ value: variant, label: variant }); - }); - - function variantCallback(generatedVariantInfo, allPossibleVariants) { - // console.log(">>>variantCallback",generatedVariantInfo,allPossibleVariants) - const cleanGeneratedVariant = JSON.parse( - JSON.stringify(generatedVariantInfo), - ); - setVariants({ - index: cleanGeneratedVariant.index, - allPossibleVariants, - }); - } - return ( <> - - {variants.allPossibleVariants.length > 1 && ( + {variants.numVariants > 1 && ( 1 + variants.numVariants > 1 ? "calc(100vh - 192px)" : "calc(100vh - 160px)" } @@ -300,7 +289,7 @@ export function PortfolioActivityViewer() { width="100%" overflow="scroll" > - diff --git a/src/Tools/_framework/Paths/PublicEditor.jsx b/src/Tools/_framework/Paths/PublicEditor.jsx index 8f102dec1e..e630107c0e 100644 --- a/src/Tools/_framework/Paths/PublicEditor.jsx +++ b/src/Tools/_framework/Paths/PublicEditor.jsx @@ -3,11 +3,12 @@ import { redirect, useLoaderData, useNavigate, + useLocation, useOutletContext, } from "react-router"; import CodeMirror from "../CodeMirror"; -import PageViewer from "../../../Viewer/PageViewer"; +import { DoenetML } from "../../../Viewer/DoenetML"; import { Box, @@ -32,7 +33,6 @@ import { BsGripVertical, BsPlayBtnFill } from "react-icons/bs"; import { RxUpdate } from "react-icons/rx"; import axios from "axios"; import { cidFromText } from "../../../Core/utils/cid"; -import VirtualKeyboard from "../Footers/VirtualKeyboard"; import { pageToolViewAtom } from "../NewToolRoot"; import { useRecoilState } from "recoil"; import VariantSelect from "../ChakraBasedComponents/VariantSelect"; @@ -133,6 +133,7 @@ export function PublicEditor() { const { signedIn } = useOutletContext(); const navigate = useNavigate(); + const location = useLocation(); const [recoilPageToolView, setRecoilPageToolView] = useRecoilState(pageToolViewAtom); @@ -177,28 +178,12 @@ export function PublicEditor() { const [variants, setVariants] = useState({ index: 1, + numVariants: 1, allPossibleVariants: ["a"], }); - let variantOptions = []; - variants.allPossibleVariants.forEach((variant) => { - variantOptions.push({ value: variant, label: variant }); - }); - - function variantCallback(generatedVariantInfo, allPossibleVariants) { - const cleanGeneratedVariant = JSON.parse( - JSON.stringify(generatedVariantInfo), - ); - setVariants({ - index: cleanGeneratedVariant.index, - allPossibleVariants, - }); - } - return ( <> - - - {variants.allPossibleVariants.length > 1 && ( + {variants.numVariants > 1 && ( - diff --git a/src/Tools/_framework/ToolPanels/AssignmentViewer.jsx b/src/Tools/_framework/ToolPanels/AssignmentViewer.jsx index 276454dbae..cc47568818 100644 --- a/src/Tools/_framework/ToolPanels/AssignmentViewer.jsx +++ b/src/Tools/_framework/ToolPanels/AssignmentViewer.jsx @@ -1,7 +1,5 @@ import React, { useEffect, useRef, useState } from "react"; -import ActivityViewer, { - saveStateToDBTimerIdAtom, -} from "../../../Viewer/ActivityViewer"; +import { DoenetML } from "../../../Viewer/DoenetML"; import { useRecoilValue, atom, @@ -54,6 +52,15 @@ export const creditAchievedAtom = atom({ }, }); +export const activityStatusAtom = atom({ + key: "activityStatusAtom", + default: { + currentPage: 0, + activityAttemptNumberSetUp: 0, + itemWeights: [], + }, +}); + export const numberOfAttemptsAllowedAdjustmentAtom = atom({ key: "numberOfAttemptsAllowedAdjustment", default: 0, @@ -161,18 +168,12 @@ export default function AssignmentViewer() { const [cidChangedMessageOpen, setCidChangedMessageOpen] = useState(false); + const pageToolView = useRecoilValue(pageToolViewAtom); + let allPossibleVariants = useRef([]); let userId = useRef(null); let individualize = useRef(null); - const getValueOfTimeoutWithoutARefresh = useRecoilCallback( - ({ snapshot }) => - async () => { - return await snapshot.getPromise(saveStateToDBTimerIdAtom); - }, - [saveStateToDBTimerIdAtom], - ); - useSetCourseIdFromDoenetId(recoilDoenetId); useInitCourseItems(courseId); @@ -183,7 +184,8 @@ export default function AssignmentViewer() { let [itemObj, setItemObj] = useRecoilState(itemByDoenetId(recoilDoenetId)); let label = itemObj.label; - let { search, hash } = useLocation(); + let location = useLocation(); + let { search, hash } = location; let navigate = useNavigate(); useEffect(() => { @@ -400,9 +402,9 @@ export default function AssignmentViewer() { return; } - allPossibleVariants.current = [ - ...Array(result.numberOfVariants).keys(), - ].map((x) => x + 1); + allPossibleVariants.current = [...Array(result.numVariants).keys()].map( + (x) => x + 1, + ); if (needNewVariant) { // determine if should individualize @@ -446,8 +448,6 @@ export default function AssignmentViewer() { let requestedVariantIndex = usersVariantAttempts[usersVariantAttempts.length - 1]; - console.log(`requestedVariantIndex: ${requestedVariantIndex}`); - setLoad({ requestedVariantIndex, attemptNumber, @@ -489,13 +489,6 @@ export default function AssignmentViewer() { navigate(search, { replace: true }); } - // don't attempt to save data from old attempt number - // (which would triggered a reset and error message); - let oldTimeoutId = await getValueOfTimeoutWithoutARefresh(); - if (oldTimeoutId !== null) { - clearTimeout(oldTimeoutId); - } - //Check if cid has changed let cid = null; @@ -601,8 +594,19 @@ export default function AssignmentViewer() { }, ); + const updateActivityStatus = useRecoilCallback( + ({ set }) => + async ({ itemWeights, currentPage, activityAttemptNumberSetUp }) => { + set(activityStatusAtom, { + itemWeights, + currentPage, + activityAttemptNumberSetUp, + }); + }, + ); + function pageChanged(pageNumber) { - console.log(`page changed to ${pageNumber}`); + // console.log(`page changed to ${pageNumber}`); } async function incrementAttemptNumberAndAttemptsAllowed() { @@ -628,8 +632,6 @@ export default function AssignmentViewer() { return null; } - console.log("stage", stage); - // console.log(`>>>>stage -${stage}-`) // console.log(`>>>>recoilAttemptNumber -${recoilAttemptNumber}-`) // console.log(`>>>>attemptNumber -${attemptNumber}-`) @@ -771,13 +773,27 @@ export default function AssignmentViewer() { const allowLoadAndSave = effectivePermissions.canViewUnassignedContent === "0"; + const apiURLs = { + loadActivityState: "/api/loadActivityState.php", + saveActivityState: "/api/saveActivityState.php", + initAssignmentAttempt: "/api/initAssignmentAttempt.php", + recordEvent: "/api/recordEvent.php", + saveCompleted: "/api/saveCompleted.php", + loadPageState: "/api/loadPageState.php", + savePageState: "/api/savePageState.php", + saveCreditForItem: "/api/saveCreditForItem.php", + reportSolutionViewed: "/api/reportSolutionViewed.php", + }; + + const scrollableContainer = document.getElementById("mainPanel"); + return ( <> {cidChangedAlert} - setCidChanged(true)} setActivityAsCompleted={setActivityAsCompleted} + idsIncludeActivityId={false} // generatedVariantCallback={variantCallback} + linkSettings={{ + viewURL: + pageToolView.page === "placementexam" + ? "/course?tool=exam" + : "/course?tool=assignment", + editURL: "/public?tool=editor", + useQueryParameters: true, + }} + apiURLs={apiURLs} + scrollableContainer={scrollableContainer} /> ); @@ -815,10 +845,11 @@ async function returnNumberOfActivityVariants(cid) { return { success: false, message: "Could not retrieve file" }; } - let result = parseActivityDefinition(activityDefinitionDoenetML); + let result = await parseActivityDefinition(activityDefinitionDoenetML, cid); - if (!result.success) { - return result; + // TODO: handle communication of errors better + if (result.errors.length > 0) { + return { success: false, message: result.errors[0].message }; } try { @@ -827,5 +858,5 @@ async function returnNumberOfActivityVariants(cid) { return { success: false, message: e.message }; } - return { success: true, numberOfVariants: result.numberOfVariants }; + return { success: true, numVariants: result.numVariants }; } diff --git a/src/Tools/_framework/ToolPanels/CollectionEditor.jsx b/src/Tools/_framework/ToolPanels/CollectionEditor.jsx index 57906f6062..15f0c6396c 100644 --- a/src/Tools/_framework/ToolPanels/CollectionEditor.jsx +++ b/src/Tools/_framework/ToolPanels/CollectionEditor.jsx @@ -54,9 +54,11 @@ export default function CollectionEditor() { if (typeof response === "object") { response = response.data; } + + // TODO: returnAllPossibleVariants no longer has a callback argument returnAllPossibleVariants({ doenetML: response, - callback: ({ allPossibleVariants }) => { + callback: (allPossibleVariants) => { set(possibleVariantsByDoenetId(doenetId), allPossibleVariants); }, }); diff --git a/src/Tools/_framework/ToolPanels/DraftAssignmentViewer.jsx b/src/Tools/_framework/ToolPanels/DraftAssignmentViewer.jsx index 5b839ef6b0..957a1ea4f1 100644 --- a/src/Tools/_framework/ToolPanels/DraftAssignmentViewer.jsx +++ b/src/Tools/_framework/ToolPanels/DraftAssignmentViewer.jsx @@ -1,5 +1,5 @@ import React, { useEffect, useRef, useState } from "react"; -import ActivityViewer from "../../../Viewer/ActivityViewer"; +import { DoenetML } from "../../../Viewer/DoenetML"; import { useRecoilValue, atom, @@ -19,6 +19,7 @@ import { useSetCourseIdFromDoenetId, } from "../../../_reactComponents/Course/CourseActions"; import { activityVariantPanelAtom } from "../../../_sharedRecoil/PageViewerRecoil"; +import { useLocation, useNavigate } from "react-router"; export default function DraftAssignmentViewer() { // console.log(">>>===DraftAssignmentViewer") @@ -55,6 +56,9 @@ export default function DraftAssignmentViewer() { useSetCourseIdFromDoenetId(recoilDoenetId); useInitCourseItems(courseId); + let navigate = useNavigate(); + let location = useLocation(); + let itemObj = useRecoilValue(itemByDoenetId(recoilDoenetId)); let label = itemObj.label; @@ -77,11 +81,11 @@ export default function DraftAssignmentViewer() { // const loadProfile = useRecoilValueLoadable(profileAtom); // userId.current = loadProfile.contents.userId; - function variantCallback(variantIndex, numberOfVariants) { - // console.log(">>>variantCallback",variantIndex,numberOfVariants) + function variantCallback(variantIndex, numVariants) { + // console.log(">>>variantCallback",variantIndex,numVariants) setVariantPanel({ index: variantIndex, - numberOfVariants, + numVariants, }); } @@ -133,15 +137,16 @@ export default function DraftAssignmentViewer() { let result = await returnNumberOfActivityVariantsForCid(cid); - if (!result.success) { + // TODO: better display of errors + if (result.errors.length > 0) { setStage("Problem"); - setMessage(result.message); + setMessage(result.errors[0].message); return; } - allPossibleVariants.current = [ - ...Array(result.numberOfVariants).keys(), - ].map((x) => x + 1); + allPossibleVariants.current = [...Array(result.numVariants).keys()].map( + (x) => x + 1, + ); setLoad({ showCorrectness, @@ -177,12 +182,14 @@ export default function DraftAssignmentViewer() { return

{message}

; } + const scrollableContainer = document.getElementById("mainPanel"); + return ( <> - ); diff --git a/src/Tools/_framework/ToolPanels/EditorViewer.jsx b/src/Tools/_framework/ToolPanels/EditorViewer.jsx index dd0a7a506b..19e7df106d 100644 --- a/src/Tools/_framework/ToolPanels/EditorViewer.jsx +++ b/src/Tools/_framework/ToolPanels/EditorViewer.jsx @@ -1,7 +1,8 @@ import React, { useEffect, useRef } from "react"; -import PageViewer, { - scrollableContainerAtom, -} from "../../../Viewer/PageViewer"; +import { + DoenetML, + // scrollableContainerAtom, +} from "../../../Viewer/DoenetML"; import useEventListener from "../../../_utils/hooks/useEventListener"; import { useRecoilValue, @@ -30,7 +31,7 @@ import { viewerDoenetMLAtom, } from "../../../_sharedRecoil/EditorViewerRecoil"; import axios from "axios"; -import { useLoaderData, useLocation } from "react-router"; +import { useLoaderData, useLocation, useNavigate } from "react-router"; import { pageVariantInfoAtom, pageVariantPanelAtom, @@ -88,8 +89,9 @@ export default function EditorViewer() { const { canUpload } = useRecoilValue(profileAtom); const updateViewer = useUpdateViewer(); - const setScrollableContainer = useSetRecoilState(scrollableContainerAtom); + // const setScrollableContainer = useSetRecoilState(scrollableContainerAtom); + let navigate = useNavigate(); let location = useLocation(); const previousLocations = useRef({}); @@ -230,11 +232,6 @@ export default function EditorViewer() { } }, [location]); - useEffect(() => { - const mainPanel = document.getElementById("mainPanel"); - setScrollableContainer(mainPanel); - }, []); - if (courseId === "__not_found__") { return

Content not found or no permission to view content

; } else if (effectivePageId !== initializedPageId) { @@ -245,18 +242,9 @@ export default function EditorViewer() { let attemptNumber = 1; let solutionDisplayMode = "button"; - function variantCallback(generatedVariantInfo, allPossibleVariants) { - // console.log(">>>variantCallback",generatedVariantInfo,allPossibleVariants) - const cleanGeneratedVariant = JSON.parse( - JSON.stringify(generatedVariantInfo), - ); - setVariantPanel({ - index: cleanGeneratedVariant.index, - allPossibleVariants, - }); - setVariantInfo({ - index: cleanGeneratedVariant.index, - }); + function variantCallback(activityVariants) { + setVariantPanel(activityVariants); + setVariantInfo(activityVariants); } // console.log(`>>>>Show PageViewer with value -${viewerDoenetML}- -${refreshNumber}-`) @@ -266,7 +254,7 @@ export default function EditorViewer() { // console.log('>>>>variantInfo.index',variantInfo.index) return ( - ); } diff --git a/src/Tools/_framework/ToolPanels/GradebookStudentAssignment.jsx b/src/Tools/_framework/ToolPanels/GradebookStudentAssignment.jsx index ea3f61deca..700240d412 100644 --- a/src/Tools/_framework/ToolPanels/GradebookStudentAssignment.jsx +++ b/src/Tools/_framework/ToolPanels/GradebookStudentAssignment.jsx @@ -12,6 +12,7 @@ import { useSetRecoilState, useRecoilValue, useRecoilValueLoadable, + useRecoilCallback, } from "recoil"; import { @@ -21,11 +22,14 @@ import { } from "../NewToolRoot"; import { serializedComponentsReviver } from "../../../Core/utils/serializedStateProcessing"; import axios from "axios"; -import { currentAttemptNumber } from "../ToolPanels/AssignmentViewer"; -import PageViewer from "../../../Viewer/PageViewer"; +import { + activityStatusAtom, + currentAttemptNumber, +} from "../ToolPanels/AssignmentViewer"; import { effectivePermissionsByCourseId } from "../../../_reactComponents/PanelHeaderComponents/RoleDropdown"; -import ActivityViewer from "../../../Viewer/ActivityViewer"; +import { DoenetML } from "../../../Viewer/DoenetML"; import { coursePermissionsAndSettingsByCourseId } from "../../../_reactComponents/Course/CourseActions"; +import { useLocation, useNavigate } from "react-router"; // import { BreadcrumbProvider } from '../../../_reactComponents/Breadcrumb'; // import { DropTargetsProvider } from '../../../_reactComponents/DropTarget'; @@ -59,6 +63,9 @@ export default function GradebookStudentAssignmentView() { const setSuppressMenus = useSetRecoilState(suppressMenusAtom); let overview = useRecoilValueLoadable(overviewData); + let navigate = useNavigate(); + let location = useLocation(); + const totalPointsOrPercent = Number( assignments.contents[doenetId]?.totalPointsOrPercent, ); @@ -100,6 +107,17 @@ export default function GradebookStudentAssignmentView() { } }, [canViewAndModifyGrades, setSuppressMenus]); + const updateActivityStatus = useRecoilCallback( + ({ set }) => + async ({ itemWeights, currentPage, activityAttemptNumberSetUp }) => { + set(activityStatusAtom, { + itemWeights, + currentPage, + activityAttemptNumberSetUp, + }); + }, + ); + let course = useRecoilValue(coursePermissionsAndSettingsByCourseId(courseId)); if (course?.canViewCourse == "0") { @@ -236,7 +254,7 @@ export default function GradebookStudentAssignmentView() { } let credit = overview.contents?.[userId]?.assignments?.[doenetId]; - let score = totalPointsOrPercent * credit; + let score = Math.round(totalPointsOrPercent * credit * 100) / 100; let percentage = Math.round(credit * 1000) / 10 + "%"; scoreRow["total"] = score; @@ -296,14 +314,27 @@ export default function GradebookStudentAssignmentView() { // let doenetML = attemptsInfo[attemptNumber].doenetML; let solutionDisplayMode = attemptsInfo[attemptNumber].solutionDisplayMode; let paginate = attemptsInfo[attemptNumber].paginate; + + const apiURLs = { + loadActivityState: "/api/loadActivityState.php", + saveActivityState: "/api/saveActivityState.php", + initAssignmentAttempt: "/api/initAssignmentAttempt.php", + recordEvent: "/api/recordEvent.php", + saveCompleted: "/api/saveCompleted.php", + loadPageState: "/api/loadPageState.php", + savePageState: "/api/savePageState.php", + saveCreditForItem: "/api/saveCreditForItem.php", + reportSolutionViewed: "/api/reportSolutionViewed.php", + }; + dViewer = ( - ); diff --git a/src/Tools/_framework/ToolPanels/GuestEditorViewer.jsx b/src/Tools/_framework/ToolPanels/GuestEditorViewer.jsx index f642acc26a..aae26d5b26 100644 --- a/src/Tools/_framework/ToolPanels/GuestEditorViewer.jsx +++ b/src/Tools/_framework/ToolPanels/GuestEditorViewer.jsx @@ -1,7 +1,8 @@ import React, { useEffect, useRef, useState } from "react"; -import PageViewer, { - scrollableContainerAtom, -} from "../../../Viewer/PageViewer"; +import { + DoenetML, + // scrollableContainerAtom, +} from "../../../Viewer/DoenetML"; import useEventListener from "../../../_utils/hooks/useEventListener"; import { useRecoilValue, @@ -22,7 +23,7 @@ import { updateTextEditorDoenetMLAtom, viewerDoenetMLAtom, } from "../../../_sharedRecoil/EditorViewerRecoil"; -import { useLocation } from "react-router"; +import { useLocation, useNavigate } from "react-router"; import { pageVariantInfoAtom, pageVariantPanelAtom, @@ -44,8 +45,7 @@ export default function EditorViewer() { const [errMsg, setErrMsg] = useState(null); - const setScrollableContainer = useSetRecoilState(scrollableContainerAtom); - + let navigate = useNavigate(); let location = useLocation(); const previousLocations = useRef({}); @@ -92,15 +92,21 @@ export default function EditorViewer() { return; } - let parseResult = parseActivityDefinition(activityDefinition); - if (!parseResult.success) { - setErrMsg(`Invalid activity definition: ${parseResult.message}`); + let parseResult = await parseActivityDefinition( + activityDefinition, + activityCid, + ); + // TODO: handle diplsay of errors better + if (parseResult.errors.length > 0) { + setErrMsg( + `Invalid activity definition: ${parseResult.errors[0].message}`, + ); return; } let activityJSON = parseResult.activityJSON; - setPageCid(findFirstPageCidFromCompiledActivity(activityJSON.order)); + setPageCid(findFirstPageCidFromCompiledActivity(activityJSON.children)); if (errMsg) { setErrMsg(null); @@ -151,11 +157,6 @@ export default function EditorViewer() { } }, [location]); - useEffect(() => { - const mainPanel = document.getElementById("mainPanel"); - setScrollableContainer(mainPanel); - }, []); - let initDoenetML = useRecoilCallback( ({ snapshot, set }) => async (pageCid) => { @@ -219,7 +220,7 @@ export default function EditorViewer() { } return ( - ); } -function findFirstPageCidFromCompiledActivity(orderObj) { - if (!orderObj?.content) { +function findFirstPageCidFromCompiledActivity(children) { + if (!children) { return null; } //No pages or orders in order so return null - if (orderObj.content.length == 0) { + if (children.length == 0) { return null; } - for (let item of orderObj.content) { + for (let item of children) { if (item.type === "page") { return item.cid; } else { - //First item of content is another order - let nextOrderResponse = findFirstPageOfActivity(item.content); + //First item is another order + let nextOrderResponse = findFirstPageOfActivity(item.children); if (nextOrderResponse) { return nextOrderResponse; } diff --git a/src/Tools/_framework/ToolPanels/PublicActivityViewer.jsx b/src/Tools/_framework/ToolPanels/PublicActivityViewer.jsx index 8bb8917cf5..9adc849d24 100644 --- a/src/Tools/_framework/ToolPanels/PublicActivityViewer.jsx +++ b/src/Tools/_framework/ToolPanels/PublicActivityViewer.jsx @@ -1,7 +1,8 @@ -import React, { useEffect, useRef, useState } from "react"; -import ActivityViewer from "../../../Viewer/ActivityViewer"; +import React, { useEffect, useState } from "react"; +import { DoenetML } from "../../../Viewer/DoenetML"; import { useRecoilValue } from "recoil"; import { searchParamAtomFamily } from "../NewToolRoot"; +import { useLocation, useNavigate } from "react-router"; import axios from "axios"; @@ -12,6 +13,9 @@ export default function Public(props) { const [errMsg, setErrMsg] = useState(null); + let navigate = useNavigate(); + let location = useLocation(); + useEffect(() => { const prevTitle = document.title; @@ -52,10 +56,10 @@ export default function Public(props) { return ( <> - ); diff --git a/src/Tools/cypressTest/CypressTest.jsx b/src/Tools/cypressTest/CypressTest.jsx index 54605e3bb4..7d49c26a3a 100644 --- a/src/Tools/cypressTest/CypressTest.jsx +++ b/src/Tools/cypressTest/CypressTest.jsx @@ -1,9 +1,8 @@ import React, { useState, useEffect, useRef } from "react"; -import PageViewer from "../../Viewer/PageViewer.jsx"; -import ActivityViewer from "../../Viewer/ActivityViewer.jsx"; +import { DoenetML } from "../../Viewer/DoenetML.jsx"; import { useRecoilState } from "recoil"; -import { darkModeAtom } from "../_framework/DarkmodeController.jsx"; // import testCodeDoenetML from './testCode.doenet?raw'; +import { useLocation, useNavigate } from "react-router"; function Test() { // console.log("===Test") @@ -23,6 +22,7 @@ function Test() { allowSaveEvents: false, autoSubmit: false, paginate: true, + darkMode: "light", }; let testSettings = JSON.parse(localStorage.getItem("test settings")); if (!testSettings) { @@ -30,12 +30,10 @@ function Test() { localStorage.setItem("test settings", JSON.stringify(defaultTestSettings)); } - const [{ doenetML, activityDefinition, attemptNumber }, setBaseState] = - useState({ - doenetML: null, - activityDefinition: null, - attemptNumber: testSettings.attemptNumber, - }); + const [{ doenetMLstring, attemptNumber }, setBaseState] = useState({ + doenetMLstring: null, + attemptNumber: testSettings.attemptNumber, + }); const [updateNumber, setUpdateNumber] = useState(testSettings.updateNumber); const [controlsVisible, setControlsVisible] = useState( @@ -48,7 +46,7 @@ function Test() { const [showFeedback, setShowFeedback] = useState(testSettings.showFeedback); const [showHints, setShowHints] = useState(testSettings.showHints); - const [darkModeToggle, setDarkModeToggle] = useRecoilState(darkModeAtom); + const [darkMode, setDarkMode] = useState(testSettings.darkMode); const [allowLoadState, setAllowLoadState] = useState( testSettings.allowLoadState, @@ -70,6 +68,9 @@ function Test() { const [_, setRefresh] = useState(0); const solutionDisplayMode = "button"; + let navigate = useNavigate(); + let location = useLocation(); + // requestedVariantIndex is undefined by default so that viewer // will use attemptNumber for variant // unless get a message (from cypress) to select a particular variant @@ -77,14 +78,11 @@ function Test() { //For Cypress Test Use window.onmessage = (e) => { - let newDoenetML = null, - newActivityDefinition = null, + let newDoenetMLstring = null, newAttemptNumber = attemptNumber; if (e.data.doenetML !== undefined) { - newDoenetML = e.data.doenetML; - } else if (e.data.activityDefinition !== undefined) { - newActivityDefinition = e.data.activityDefinition; + newDoenetMLstring = e.data.doenetML; } if (e.data.requestedVariantIndex !== undefined) { @@ -97,14 +95,9 @@ function Test() { } // don't do anything if receive a message from another source (like the youtube player) - if ( - newDoenetML || - newActivityDefinition || - newAttemptNumber !== attemptNumber - ) { + if (newDoenetMLstring || newAttemptNumber !== attemptNumber) { setBaseState({ - doenetML: newDoenetML, - activityDefinition: newActivityDefinition, + doenetMLstring: newDoenetMLstring, attemptNumber: newAttemptNumber, }); } @@ -399,9 +392,9 @@ function Test() { { - setDarkModeToggle(darkModeToggle === "dark" ? "light" : "dark"); + setDarkMode(darkMode === "dark" ? "light" : "dark"); }} /> Dark Mode @@ -412,42 +405,13 @@ function Test() { } let viewer = null; - if (doenetML !== null) { - viewer = ( - - ); - } else if (activityDefinition !== null) { + + if (doenetMLstring) { viewer = ( - ); } @@ -476,6 +445,7 @@ function Test() { return (

@@ -494,14 +464,4 @@ function Test() { ); } -// if (import.meta.hot) { -// import.meta.hot.accept(); -// // import.meta.hot.accept(({module}) => { -// // Test = module.default; -// // console.log(">>>ACCEPT CALLED in test!!!!!!!!!",module.default) -// // console.log(">>>module",module) -// // } -// // ); -// } - export default Test; diff --git a/src/Tools/cypressTest/index.jsx b/src/Tools/cypressTest/index.jsx index 7ba98dd4d1..599e3b63a9 100644 --- a/src/Tools/cypressTest/index.jsx +++ b/src/Tools/cypressTest/index.jsx @@ -6,7 +6,6 @@ import { RecoilRoot } from "recoil"; import { MathJaxContext } from "better-react-mathjax"; import { mathjaxConfig } from "../../Core/utils/math.js"; import { BrowserRouter as Router, Routes, Route } from "react-router-dom"; -import DarkmodeController from "../_framework/DarkmodeController.jsx"; // function CypressTest(props){ @@ -23,15 +22,13 @@ root.render( - (mathJax.Hub.processSectionDelay = 0)} - > - - - + (mathJax.Hub.processSectionDelay = 0)} + > + + } /> diff --git a/src/Tools/test/DoenetTest.jsx b/src/Tools/test/DoenetTest.jsx index 73901560ca..eadff468fa 100644 --- a/src/Tools/test/DoenetTest.jsx +++ b/src/Tools/test/DoenetTest.jsx @@ -1,26 +1,23 @@ import React, { useState, useEffect, useRef } from "react"; -import ActivityViewer from "../../Viewer/ActivityViewer.jsx"; -import PageViewer from "../../Viewer/PageViewer.jsx"; -import testActivityDefinition from "./testActivityDefinition.doenet?raw"; +import { DoenetML } from "../../Viewer/DoenetML"; import testCodeDoenetML from "./testCode.doenet?raw"; import { MathJaxContext } from "better-react-mathjax"; import { mathjaxConfig } from "../../Core/utils/math.js"; import { useRecoilState } from "recoil"; -import { darkModeAtom } from "../_framework/DarkmodeController.jsx"; +import { useLocation, useNavigate } from "react-router"; function Test() { // console.log("===Test") const [doenetML, setDoenetML] = useState(null); - const [activityDefinition, setActivityDefinition] = useState(null); + + let navigate = useNavigate(); + let location = useLocation(); //New ActivityViewer when code changes useEffect(() => { setDoenetML(testCodeDoenetML); }, [testCodeDoenetML]); - useEffect(() => { - setActivityDefinition(testActivityDefinition); - }, [testActivityDefinition]); const defaultTestSettings = { updateNumber: 0, @@ -36,8 +33,8 @@ function Test() { allowSaveSubmissions: false, allowSaveEvents: false, autoSubmit: false, - useTestCode: false, paginate: true, + darkMode: "light", }; let testSettings = JSON.parse(localStorage.getItem("test settings")); if (!testSettings) { @@ -59,7 +56,7 @@ function Test() { const [showFeedback, setShowFeedback] = useState(testSettings.showFeedback); const [showHints, setShowHints] = useState(testSettings.showHints); - const [darkModeToggle, setDarkModeToggle] = useRecoilState(darkModeAtom); + const [darkMode, setDarkMode] = useState(testSettings.darkMode); const [allowLoadState, setAllowLoadState] = useState( testSettings.allowLoadState, @@ -77,18 +74,16 @@ function Test() { testSettings.allowSaveEvents, ); const [autoSubmit, setAutoSubmit] = useState(testSettings.autoSubmit); - const [useTestCode, setUseTestCode] = useState(testSettings.useTestCode); const [paginate, setPaginate] = useState(testSettings.paginate); - const [_, setRefresh] = useState(0); const solutionDisplayMode = "button"; // TODO: currently, requestedVariantIndex cannot be changed from undefined - // so variant is always determiend by attemptNumber + // so variant is always determined by attemptNumber // Do we add the ability to specify requestedVariantIndex directly in test mode? let requestedVariantIndex = useRef(undefined); //Don't construct core until we have the doenetML defined - if (doenetML === null && activityDefinition === null) { + if (doenetML === null) { return null; } @@ -334,26 +329,6 @@ function Test() {


-
- -
-
{checkWorkButton} diff --git a/src/Viewer/renderers/mathList.jsx b/src/Viewer/renderers/mathList.jsx index 11f7f943d5..59616dba28 100644 --- a/src/Viewer/renderers/mathList.jsx +++ b/src/Viewer/renderers/mathList.jsx @@ -1,9 +1,9 @@ import { MathJax } from "better-react-mathjax"; import React from "react"; -import useDoenetRender from "../useDoenetRenderer"; +import useDoenetRenderer from "../useDoenetRenderer"; export default React.memo(function MathList(props) { - let { name, id, SVs, children } = useDoenetRender(props); + let { name, id, SVs, children } = useDoenetRenderer(props); if (SVs.hidden) { return null; diff --git a/src/Viewer/renderers/matrixInput.jsx b/src/Viewer/renderers/matrixInput.jsx index a8f0ce6c0a..a53f94ac5b 100644 --- a/src/Viewer/renderers/matrixInput.jsx +++ b/src/Viewer/renderers/matrixInput.jsx @@ -1,5 +1,5 @@ import React, { useRef } from "react"; -import useDoenetRender from "../useDoenetRenderer"; +import useDoenetRenderer from "../useDoenetRenderer"; // import me from 'math-expressions'; import ActionButton from "../../_reactComponents/PanelHeaderComponents/ActionButton"; import ActionButtonGroup from "../../_reactComponents/PanelHeaderComponents/ActionButtonGroup"; @@ -68,7 +68,8 @@ const Button = styled.button` `; export default React.memo(function MatrixInput(props) { - let { name, id, SVs, actions, children, callAction } = useDoenetRender(props); + let { name, id, SVs, actions, children, callAction } = + useDoenetRenderer(props); let validationState = useRef(null); diff --git a/src/Viewer/renderers/mdash.jsx b/src/Viewer/renderers/mdash.jsx index fbba438077..f482c61725 100644 --- a/src/Viewer/renderers/mdash.jsx +++ b/src/Viewer/renderers/mdash.jsx @@ -1,8 +1,8 @@ import React from "react"; -import useDoenetRender from "../useDoenetRenderer"; +import useDoenetRenderer from "../useDoenetRenderer"; export default React.memo(function Ndash(props) { - let { SVs } = useDoenetRender(props, false); + let { SVs } = useDoenetRenderer(props, false); if (SVs.hidden) { return null; diff --git a/src/Viewer/renderers/nbsp.jsx b/src/Viewer/renderers/nbsp.jsx index 4dc19bcd61..d04618d2c4 100644 --- a/src/Viewer/renderers/nbsp.jsx +++ b/src/Viewer/renderers/nbsp.jsx @@ -1,8 +1,8 @@ import React from "react"; -import useDoenetRender from "../useDoenetRenderer"; +import useDoenetRenderer from "../useDoenetRenderer"; export default React.memo(function Nbsp(props) { - let { SVs } = useDoenetRender(props, false); + let { SVs } = useDoenetRenderer(props, false); if (SVs.hidden) { return null; diff --git a/src/Viewer/renderers/ndash.jsx b/src/Viewer/renderers/ndash.jsx index 52a91c18de..18f4a42e8c 100644 --- a/src/Viewer/renderers/ndash.jsx +++ b/src/Viewer/renderers/ndash.jsx @@ -1,8 +1,8 @@ import React from "react"; -import useDoenetRender from "../useDoenetRenderer"; +import useDoenetRenderer from "../useDoenetRenderer"; export default React.memo(function Ndash(props) { - let { SVs } = useDoenetRender(props); + let { SVs } = useDoenetRenderer(props); if (SVs.hidden) { return null; diff --git a/src/Viewer/renderers/number.jsx b/src/Viewer/renderers/number.jsx index 71b0c8e863..a9d94f6b5c 100644 --- a/src/Viewer/renderers/number.jsx +++ b/src/Viewer/renderers/number.jsx @@ -2,16 +2,15 @@ import { MathJax } from "better-react-mathjax"; import React, { useContext, useEffect, useRef } from "react"; import { BoardContext, TEXT_LAYER_OFFSET } from "./graph"; -import useDoenetRender from "../useDoenetRenderer"; +import useDoenetRenderer from "../useDoenetRenderer"; import me from "math-expressions"; -import { useRecoilValue } from "recoil"; -import { darkModeAtom } from "../../Tools/_framework/DarkmodeController"; import { textRendererStyle } from "../../Core/utils/style"; import { getPositionFromAnchorByCoordinate } from "../../Core/utils/graphical"; +import { PageContext } from "../PageViewer"; export default React.memo(function NumberComponent(props) { let { name, id, SVs, actions, sourceOfUpdate, callAction } = - useDoenetRender(props); + useDoenetRenderer(props); NumberComponent.ignoreActionsWithoutCore = () => true; @@ -39,7 +38,7 @@ export default React.memo(function NumberComponent(props) { fixed.current = SVs.fixed; fixLocation.current = !SVs.draggable || SVs.fixLocation || SVs.fixed; - const darkMode = useRecoilValue(darkModeAtom); + const { darkMode } = useContext(PageContext) || {}; useEffect(() => { //On unmount diff --git a/src/Viewer/renderers/numberList.jsx b/src/Viewer/renderers/numberList.jsx index efbc4238aa..b1b55da7d3 100644 --- a/src/Viewer/renderers/numberList.jsx +++ b/src/Viewer/renderers/numberList.jsx @@ -1,9 +1,9 @@ import { MathJax } from "better-react-mathjax"; import React from "react"; -import useDoenetRender from "../useDoenetRenderer"; +import useDoenetRenderer from "../useDoenetRenderer"; export default React.memo(function MathList(props) { - let { name, id, SVs, children } = useDoenetRender(props); + let { name, id, SVs, children } = useDoenetRenderer(props); if (SVs.hidden) { return null; diff --git a/src/Viewer/renderers/paginatorControls.jsx b/src/Viewer/renderers/paginatorControls.jsx index bb4254da35..3fde607258 100644 --- a/src/Viewer/renderers/paginatorControls.jsx +++ b/src/Viewer/renderers/paginatorControls.jsx @@ -1,9 +1,9 @@ import React from "react"; -import useDoenetRender from "../useDoenetRenderer"; +import useDoenetRenderer from "../useDoenetRenderer"; import Button from "../../_reactComponents/PanelHeaderComponents/Button"; export default React.memo(function PaginatorControls(props) { - let { name, id, SVs, actions, callAction } = useDoenetRender(props, false); + let { name, id, SVs, actions, callAction } = useDoenetRenderer(props, false); if (SVs.hidden) { return null; diff --git a/src/Viewer/renderers/pegboard.jsx b/src/Viewer/renderers/pegboard.jsx index 4de60630d2..d101318ae6 100644 --- a/src/Viewer/renderers/pegboard.jsx +++ b/src/Viewer/renderers/pegboard.jsx @@ -1,11 +1,11 @@ import React, { useContext, useEffect, useState, useRef } from "react"; -import useDoenetRender from "../useDoenetRenderer"; +import useDoenetRenderer from "../useDoenetRenderer"; import { BASE_LAYER_OFFSET, BoardContext } from "./graph"; import me from "math-expressions"; export default React.memo(function Pegboard(props) { let { name, id, SVs, actions, sourceOfUpdate, callAction } = - useDoenetRender(props); + useDoenetRenderer(props); Pegboard.ignoreActionsWithoutCore = () => true; diff --git a/src/Viewer/renderers/point.jsx b/src/Viewer/renderers/point.jsx index 6e000e0f3a..7747e7c6a4 100644 --- a/src/Viewer/renderers/point.jsx +++ b/src/Viewer/renderers/point.jsx @@ -1,9 +1,7 @@ import React, { useContext, useEffect, useState, useRef } from "react"; -import useDoenetRender from "../useDoenetRenderer"; +import useDoenetRenderer from "../useDoenetRenderer"; import { BoardContext, POINT_LAYER_OFFSET } from "./graph"; import { MathJax } from "better-react-mathjax"; -import { darkModeAtom } from "../../Tools/_framework/DarkmodeController"; -import { useRecoilValue } from "recoil"; import { textRendererStyle } from "../../Core/utils/style"; import { characterizeOffGraphPoint } from "./utils/offGraphIndicators"; import { @@ -12,10 +10,11 @@ import { normalizePointSize, normalizePointStyle, } from "./utils/graph"; +import { PageContext } from "../PageViewer"; export default React.memo(function Point(props) { let { name, id, SVs, actions, sourceOfUpdate, callAction } = - useDoenetRender(props); + useDoenetRenderer(props); Point.ignoreActionsWithoutCore = () => true; @@ -52,7 +51,7 @@ export default React.memo(function Point(props) { fixLocation.current = !SVs.draggable || SVs.fixLocation || SVs.fixed; switchable.current = SVs.switchable && !SVs.fixed; - const darkMode = useRecoilValue(darkModeAtom); + const { darkMode } = useContext(PageContext) || {}; const useOpenSymbol = SVs.open || ["cross", "plus"].includes(SVs.selectedStyle.markerStyle); // Cross and plus should always be treated as "open" to remain visible on graph diff --git a/src/Viewer/renderers/polygon.jsx b/src/Viewer/renderers/polygon.jsx index c1a687931e..6b307f9581 100644 --- a/src/Viewer/renderers/polygon.jsx +++ b/src/Viewer/renderers/polygon.jsx @@ -1,12 +1,11 @@ import React, { useContext, useEffect, useRef } from "react"; -import useDoenetRender from "../useDoenetRenderer"; +import useDoenetRenderer from "../useDoenetRenderer"; import { BoardContext, LINE_LAYER_OFFSET, VERTEX_LAYER_OFFSET } from "./graph"; -import { useRecoilValue } from "recoil"; -import { darkModeAtom } from "../../Tools/_framework/DarkmodeController"; +import { PageContext } from "../PageViewer"; export default React.memo(function Polygon(props) { let { name, id, SVs, actions, sourceOfUpdate, callAction } = - useDoenetRender(props); + useDoenetRenderer(props); Polygon.ignoreActionsWithoutCore = () => true; @@ -35,7 +34,7 @@ export default React.memo(function Polygon(props) { verticesFixed.current = !SVs.verticesDraggable || SVs.fixed || SVs.fixLocation; - const darkMode = useRecoilValue(darkModeAtom); + const { darkMode } = useContext(PageContext) || {}; useEffect(() => { //On unmount diff --git a/src/Viewer/renderers/polyline.jsx b/src/Viewer/renderers/polyline.jsx index 6ecde6a5a0..51590e7866 100644 --- a/src/Viewer/renderers/polyline.jsx +++ b/src/Viewer/renderers/polyline.jsx @@ -1,12 +1,11 @@ import React, { useContext, useEffect, useRef } from "react"; -import useDoenetRender from "../useDoenetRenderer"; +import useDoenetRenderer from "../useDoenetRenderer"; import { BoardContext, LINE_LAYER_OFFSET, VERTEX_LAYER_OFFSET } from "./graph"; -import { useRecoilValue } from "recoil"; -import { darkModeAtom } from "../../Tools/_framework/DarkmodeController"; +import { PageContext } from "../PageViewer"; export default React.memo(function Polyline(props) { let { name, id, SVs, actions, sourceOfUpdate, callAction } = - useDoenetRender(props); + useDoenetRenderer(props); Polyline.ignoreActionsWithoutCore = () => true; @@ -36,7 +35,7 @@ export default React.memo(function Polyline(props) { verticesFixed.current = !SVs.verticesDraggable || SVs.fixed || SVs.fixLocation; - const darkMode = useRecoilValue(darkModeAtom); + const { darkMode } = useContext(PageContext) || {}; useEffect(() => { //On unmount diff --git a/src/Viewer/renderers/q.jsx b/src/Viewer/renderers/q.jsx index b634fb02d1..d195eaabf0 100644 --- a/src/Viewer/renderers/q.jsx +++ b/src/Viewer/renderers/q.jsx @@ -1,8 +1,8 @@ import React from "react"; -import useDoenetRender from "../useDoenetRenderer"; +import useDoenetRenderer from "../useDoenetRenderer"; export default React.memo(function Q(props) { - let { name, id, SVs, children } = useDoenetRender(props); + let { name, id, SVs, children } = useDoenetRenderer(props); if (SVs.hidden) { return null; diff --git a/src/Viewer/renderers/ray.jsx b/src/Viewer/renderers/ray.jsx index 332e459f7a..9150d69d18 100644 --- a/src/Viewer/renderers/ray.jsx +++ b/src/Viewer/renderers/ray.jsx @@ -1,13 +1,11 @@ import React, { useContext, useEffect, useState, useRef } from "react"; -import useDoenetRender from "../useDoenetRenderer"; +import useDoenetRenderer from "../useDoenetRenderer"; import { BoardContext, LINE_LAYER_OFFSET } from "./graph"; -import { useRecoilValue } from "recoil"; -import { darkModeAtom } from "../../Tools/_framework/DarkmodeController"; -// import me from 'math-expressions'; +import { PageContext } from "../PageViewer"; export default React.memo(function Ray(props) { let { name, id, SVs, actions, sourceOfUpdate, callAction } = - useDoenetRender(props); + useDoenetRenderer(props); Ray.ignoreActionsWithoutCore = () => true; @@ -34,7 +32,7 @@ export default React.memo(function Ray(props) { fixed.current = SVs.fixed; fixLocation.current = !SVs.draggable || SVs.fixLocation || SVs.fixed; - const darkMode = useRecoilValue(darkModeAtom); + const { darkMode } = useContext(PageContext) || {}; useEffect(() => { //On unmount diff --git a/src/Viewer/renderers/ref.jsx b/src/Viewer/renderers/ref.jsx index 7a62618b14..df6f4fd2ea 100644 --- a/src/Viewer/renderers/ref.jsx +++ b/src/Viewer/renderers/ref.jsx @@ -1,11 +1,7 @@ -import React from "react"; -import { useLocation, useNavigate } from "react-router"; +import React, { useContext } from "react"; import { Link } from "react-router-dom"; -import { useRecoilValue } from "recoil"; -import { pageToolViewAtom } from "../../Tools/_framework/NewToolRoot"; -import { itemByDoenetId } from "../../_reactComponents/Course/CourseActions"; -import { getURLFromRef, scrollableContainerAtom } from "../PageViewer"; -import useDoenetRender from "../useDoenetRenderer"; +import { PageContext, getURLFromRef } from "../PageViewer"; +import useDoenetRenderer from "../useDoenetRenderer"; import styled from "styled-components"; // const LinkStyling = styled.a` @@ -46,14 +42,16 @@ const RefButton = styled.button` `; export default React.memo(function Ref(props) { - let { name, id, SVs, children } = useDoenetRender(props); + let { name, id, SVs, children } = useDoenetRenderer(props); - const pageToolView = useRecoilValue(pageToolViewAtom); - const itemInCourse = useRecoilValue(itemByDoenetId(SVs.doenetId)); - const scrollableContainer = useRecoilValue(scrollableContainerAtom); + let { + location = {}, + navigate, + linkSettings, + scrollableContainer, + } = useContext(PageContext) || {}; - let { search, pathname } = useLocation(); - let navigate = useNavigate(); + let search = location.search || ""; if (SVs.hidden) { return null; @@ -66,16 +64,14 @@ export default React.memo(function Ref(props) { let { targetForATag, url, haveValidTarget, externalUri } = getURLFromRef({ cid: SVs.cid, - doenetId: SVs.doenetId, + activityId: SVs.activityId, variantIndex: SVs.variantIndex, edit: SVs.edit, hash: SVs.hash, page: SVs.page, givenUri: SVs.uri, targetName: SVs.targetName, - pageToolView, - inCourse: Object.keys(itemInCourse).length > 0, - pathname, + linkSettings, search, id, }); diff --git a/src/Viewer/renderers/regionBetweenCurveXAxis.jsx b/src/Viewer/renderers/regionBetweenCurveXAxis.jsx index 87fd1b867d..1d1d0bd791 100644 --- a/src/Viewer/renderers/regionBetweenCurveXAxis.jsx +++ b/src/Viewer/renderers/regionBetweenCurveXAxis.jsx @@ -1,12 +1,11 @@ import React, { useContext, useEffect, useState, useRef } from "react"; -import useDoenetRender from "../useDoenetRenderer"; +import useDoenetRenderer from "../useDoenetRenderer"; import { BoardContext, LINE_LAYER_OFFSET } from "./graph"; import { createFunctionFromDefinition } from "../../Core/utils/function"; -import { useRecoilValue } from "recoil"; -import { darkModeAtom } from "../../Tools/_framework/DarkmodeController"; +import { PageContext } from "../PageViewer"; export default React.memo(function RegionBetweenCurveXAxis(props) { - let { name, id, SVs } = useDoenetRender(props); + let { name, id, SVs } = useDoenetRenderer(props); RegionBetweenCurveXAxis.ignoreActionsWithoutCore = () => true; @@ -15,7 +14,7 @@ export default React.memo(function RegionBetweenCurveXAxis(props) { let curveJXG = useRef(null); let integralJXG = useRef(null); - const darkMode = useRecoilValue(darkModeAtom); + const { darkMode } = useContext(PageContext) || {}; useEffect(() => { //On unmount diff --git a/src/Viewer/renderers/renderDoenetML.jsx b/src/Viewer/renderers/renderDoenetML.jsx deleted file mode 100644 index 0ba5f3c8cd..0000000000 --- a/src/Viewer/renderers/renderDoenetML.jsx +++ /dev/null @@ -1,56 +0,0 @@ -import React, { useEffect } from "react"; -import useDoenetRenderer from "../useDoenetRenderer"; -import VisibilitySensor from "react-visibility-sensor-v2"; -import PageViewer from "../PageViewer"; - -export default React.memo(function Container(props) { - let { name, id, SVs, children, actions, callAction } = - useDoenetRenderer(props); - - let onChangeVisibility = (isVisible) => { - if (actions.recordVisibilityChange) { - callAction({ - action: actions.recordVisibilityChange, - args: { isVisible }, - }); - } - }; - - useEffect(() => { - return () => { - if (actions.recordVisibilityChange) { - callAction({ - action: actions.recordVisibilityChange, - args: { isVisible: false }, - }); - } - }; - }, []); - - if (SVs.hidden) { - return null; - } - - return ( - - - - ); -}); diff --git a/src/Viewer/renderers/row.jsx b/src/Viewer/renderers/row.jsx index 2aa8675e2b..6b3e5d088a 100644 --- a/src/Viewer/renderers/row.jsx +++ b/src/Viewer/renderers/row.jsx @@ -1,8 +1,8 @@ import React from "react"; -import useDoenetRender from "../useDoenetRenderer"; +import useDoenetRenderer from "../useDoenetRenderer"; export default React.memo(function Row(props) { - let { name, id, SVs, children } = useDoenetRender(props); + let { name, id, SVs, children } = useDoenetRenderer(props); if (SVs.hidden) { return null; diff --git a/src/Viewer/renderers/rq.jsx b/src/Viewer/renderers/rq.jsx index 2c9e60ca40..d1d899125c 100644 --- a/src/Viewer/renderers/rq.jsx +++ b/src/Viewer/renderers/rq.jsx @@ -1,8 +1,8 @@ import React from "react"; -import useDoenetRender from "../useDoenetRenderer"; +import useDoenetRenderer from "../useDoenetRenderer"; export default React.memo(function Rq(props) { - let { SVs } = useDoenetRender(props, false); + let { SVs } = useDoenetRenderer(props, false); if (SVs.hidden) { return null; diff --git a/src/Viewer/renderers/rsq.jsx b/src/Viewer/renderers/rsq.jsx index f2084ba550..181b34fa03 100644 --- a/src/Viewer/renderers/rsq.jsx +++ b/src/Viewer/renderers/rsq.jsx @@ -1,8 +1,8 @@ import React from "react"; -import useDoenetRender from "../useDoenetRenderer"; +import useDoenetRenderer from "../useDoenetRenderer"; export default React.memo(function Rsq(props) { - let { SVs } = useDoenetRender(props, false); + let { SVs } = useDoenetRenderer(props, false); if (SVs.hidden) { return null; diff --git a/src/Viewer/renderers/section.jsx b/src/Viewer/renderers/section.jsx index 1e10a1b17a..20449a195d 100644 --- a/src/Viewer/renderers/section.jsx +++ b/src/Viewer/renderers/section.jsx @@ -10,11 +10,12 @@ import { import { faCaretRight as twirlIsClosed } from "@fortawesome/free-solid-svg-icons"; import { faCaretDown as twirlIsOpen } from "@fortawesome/free-solid-svg-icons"; -import useDoenetRender from "../useDoenetRenderer"; +import useDoenetRenderer from "../useDoenetRenderer"; import VisibilitySensor from "react-visibility-sensor-v2"; export default React.memo(function Section(props) { - let { name, id, SVs, children, actions, callAction } = useDoenetRender(props); + let { name, id, SVs, children, actions, callAction } = + useDoenetRenderer(props); // console.log("name: ", name, " SVs: ", SVs," Children",children); let onChangeVisibility = (isVisible) => { diff --git a/src/Viewer/renderers/sideBySide.jsx b/src/Viewer/renderers/sideBySide.jsx index cf7f27a497..bd711700bb 100644 --- a/src/Viewer/renderers/sideBySide.jsx +++ b/src/Viewer/renderers/sideBySide.jsx @@ -1,11 +1,12 @@ import React, { useRef, useState, useEffect } from "react"; // import styled from "styled-components"; -import useDoenetRender from "../useDoenetRenderer"; +import useDoenetRenderer from "../useDoenetRenderer"; import VisibilitySensor from "react-visibility-sensor-v2"; export default React.memo(function sideBySide(props) { - let { name, id, SVs, children, actions, callAction } = useDoenetRender(props); + let { name, id, SVs, children, actions, callAction } = + useDoenetRenderer(props); // console.log(">>>name: ", name, " value: ", SVs); // console.log(">>>children",children) diff --git a/src/Viewer/renderers/slider.jsx b/src/Viewer/renderers/slider.jsx index 302527f14d..f44d8f0d0e 100644 --- a/src/Viewer/renderers/slider.jsx +++ b/src/Viewer/renderers/slider.jsx @@ -2,7 +2,7 @@ import React, { useRef, useState, useEffect } from "react"; import me from "math-expressions"; import styled from "styled-components"; // import { Spring } from '@react-spring/web'; -import useDoenetRender from "../useDoenetRenderer"; +import useDoenetRenderer from "../useDoenetRenderer"; import ActionButton from "../../_reactComponents/PanelHeaderComponents/ActionButton"; import ActionButtonGroup from "../../_reactComponents/PanelHeaderComponents/ActionButtonGroup"; import { useSetRecoilState } from "recoil"; @@ -342,7 +342,7 @@ function nearestValue(refval, points, SVs) { export default React.memo(function Slider(props) { let { name, id, SVs, actions, ignoreUpdate, rendererName, callAction } = - useDoenetRender(props); + useDoenetRenderer(props); // console.log("name: ", name, " value: ", SVs.value, " index: ", SVs.index, "ignoreUpdate", ignoreUpdate); // console.log(SVs) diff --git a/src/Viewer/renderers/solution.jsx b/src/Viewer/renderers/solution.jsx index d136d3f878..17394f22b8 100644 --- a/src/Viewer/renderers/solution.jsx +++ b/src/Viewer/renderers/solution.jsx @@ -1,5 +1,5 @@ import React, { useEffect } from "react"; -import useDoenetRender from "../useDoenetRenderer"; +import useDoenetRenderer from "../useDoenetRenderer"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { faPuzzlePiece as puzzle } from "@fortawesome/free-solid-svg-icons"; import VisibilitySensor from "react-visibility-sensor-v2"; @@ -23,7 +23,8 @@ const SpanStyling = styled.span` `; export default React.memo(function Solution(props) { - let { name, id, SVs, children, actions, callAction } = useDoenetRender(props); + let { name, id, SVs, children, actions, callAction } = + useDoenetRenderer(props); let onChangeVisibility = (isVisible) => { callAction({ diff --git a/src/Viewer/renderers/spreadsheet.jsx b/src/Viewer/renderers/spreadsheet.jsx index f3cce58d02..0f589dfaa7 100644 --- a/src/Viewer/renderers/spreadsheet.jsx +++ b/src/Viewer/renderers/spreadsheet.jsx @@ -1,5 +1,5 @@ import React, { useEffect } from "react"; -import useDoenetRender from "../useDoenetRenderer"; +import useDoenetRenderer from "../useDoenetRenderer"; import { HotTable } from "@handsontable/react"; import { HyperFormula } from "hyperformula"; import "handsontable/dist/handsontable.full.css"; @@ -10,7 +10,7 @@ import VisibilitySensor from "react-visibility-sensor-v2"; registerAllModules(); export default React.memo(function SpreadsheetRenderer(props) { - let { name, id, SVs, actions, callAction } = useDoenetRender(props); + let { name, id, SVs, actions, callAction } = useDoenetRenderer(props); let onChangeVisibility = (isVisible) => { callAction({ diff --git a/src/Viewer/renderers/sq.jsx b/src/Viewer/renderers/sq.jsx index e4a5eaa6de..fe666ca92f 100644 --- a/src/Viewer/renderers/sq.jsx +++ b/src/Viewer/renderers/sq.jsx @@ -1,8 +1,8 @@ import React from "react"; -import useDoenetRender from "../useDoenetRenderer"; +import useDoenetRenderer from "../useDoenetRenderer"; export default React.memo(function Sq(props) { - let { name, id, SVs, children } = useDoenetRender(props); + let { name, id, SVs, children } = useDoenetRenderer(props); if (SVs.hidden) { return null; diff --git a/src/Viewer/renderers/subsetOfRealsInput.jsx b/src/Viewer/renderers/subsetOfRealsInput.jsx index 37b3c3fd10..b5d916c80e 100644 --- a/src/Viewer/renderers/subsetOfRealsInput.jsx +++ b/src/Viewer/renderers/subsetOfRealsInput.jsx @@ -1,6 +1,6 @@ import React, { useRef, useState, useEffect } from "react"; import styled from "styled-components"; -import useDoenetRender from "../useDoenetRenderer"; +import useDoenetRenderer from "../useDoenetRenderer"; import ActionButton from "../../_reactComponents/PanelHeaderComponents/ActionButton"; import ActionButtonGroup from "../../_reactComponents/PanelHeaderComponents/ActionButtonGroup"; import ToggleButton from "../../_reactComponents/PanelHeaderComponents/ToggleButton"; @@ -31,7 +31,7 @@ const TextNoSelect = styled.text` // `; export default React.memo(function subsetOfReals(props) { - let { name, id, SVs, actions, callAction } = useDoenetRender(props, false); + let { name, id, SVs, actions, callAction } = useDoenetRenderer(props, false); let [mode, setMode] = useState("add remove points"); let bounds = useRef(null); let pointGrabbed = useRef(null); diff --git a/src/Viewer/renderers/summaryStatistics.jsx b/src/Viewer/renderers/summaryStatistics.jsx index 4d3adc7b09..f239499c3a 100644 --- a/src/Viewer/renderers/summaryStatistics.jsx +++ b/src/Viewer/renderers/summaryStatistics.jsx @@ -1,10 +1,11 @@ import React, { useEffect } from "react"; -import useDoenetRender from "../useDoenetRenderer"; +import useDoenetRenderer from "../useDoenetRenderer"; import { sizeToCSS } from "./utils/css"; import VisibilitySensor from "react-visibility-sensor-v2"; export default React.memo(function Tabular(props) { - let { name, id, SVs, children, actions, callAction } = useDoenetRender(props); + let { name, id, SVs, children, actions, callAction } = + useDoenetRenderer(props); let onChangeVisibility = (isVisible) => { callAction({ diff --git a/src/Viewer/renderers/table.jsx b/src/Viewer/renderers/table.jsx index ce0740df2a..0743898099 100644 --- a/src/Viewer/renderers/table.jsx +++ b/src/Viewer/renderers/table.jsx @@ -1,9 +1,10 @@ import React, { useEffect } from "react"; -import useDoenetRender from "../useDoenetRenderer"; +import useDoenetRenderer from "../useDoenetRenderer"; import VisibilitySensor from "react-visibility-sensor-v2"; export default React.memo(function Table(props) { - let { name, id, SVs, children, actions, callAction } = useDoenetRender(props); + let { name, id, SVs, children, actions, callAction } = + useDoenetRenderer(props); let onChangeVisibility = (isVisible) => { callAction({ diff --git a/src/Viewer/renderers/tabular.jsx b/src/Viewer/renderers/tabular.jsx index 0394a9f84e..2c0a9d39b3 100644 --- a/src/Viewer/renderers/tabular.jsx +++ b/src/Viewer/renderers/tabular.jsx @@ -1,10 +1,11 @@ import React, { useEffect } from "react"; -import useDoenetRender from "../useDoenetRenderer"; +import useDoenetRenderer from "../useDoenetRenderer"; import { sizeToCSS } from "./utils/css"; import VisibilitySensor from "react-visibility-sensor-v2"; export default React.memo(function Tabular(props) { - let { name, id, SVs, children, actions, callAction } = useDoenetRender(props); + let { name, id, SVs, children, actions, callAction } = + useDoenetRenderer(props); let onChangeVisibility = (isVisible) => { callAction({ diff --git a/src/Viewer/renderers/tag.jsx b/src/Viewer/renderers/tag.jsx index c7bf4dad5d..8c8eba14ff 100644 --- a/src/Viewer/renderers/tag.jsx +++ b/src/Viewer/renderers/tag.jsx @@ -1,8 +1,8 @@ import React from "react"; -import useDoenetRender from "../useDoenetRenderer"; +import useDoenetRenderer from "../useDoenetRenderer"; export default React.memo(function Tag(props) { - let { name, id, SVs, children } = useDoenetRender(props); + let { name, id, SVs, children } = useDoenetRenderer(props); if (SVs.hidden) { return null; diff --git a/src/Viewer/renderers/text.jsx b/src/Viewer/renderers/text.jsx index b7c052372e..b7ed813f2d 100644 --- a/src/Viewer/renderers/text.jsx +++ b/src/Viewer/renderers/text.jsx @@ -1,15 +1,14 @@ import React, { useContext, useEffect, useRef } from "react"; import { BoardContext, TEXT_LAYER_OFFSET } from "./graph"; -import useDoenetRender from "../useDoenetRenderer"; +import useDoenetRenderer from "../useDoenetRenderer"; import me from "math-expressions"; -import { useRecoilValue } from "recoil"; -import { darkModeAtom } from "../../Tools/_framework/DarkmodeController"; import { textRendererStyle } from "../../Core/utils/style"; import { getPositionFromAnchorByCoordinate } from "../../Core/utils/graphical"; +import { PageContext } from "../PageViewer"; export default React.memo(function Text(props) { let { name, id, SVs, actions, sourceOfUpdate, callAction } = - useDoenetRender(props); + useDoenetRenderer(props); Text.ignoreActionsWithoutCore = () => true; @@ -37,7 +36,7 @@ export default React.memo(function Text(props) { fixed.current = SVs.fixed; fixLocation.current = !SVs.draggable || SVs.fixLocation || SVs.fixed; - const darkMode = useRecoilValue(darkModeAtom); + const { darkMode } = useContext(PageContext) || {}; useEffect(() => { //On unmount diff --git a/src/Viewer/renderers/textInput.jsx b/src/Viewer/renderers/textInput.jsx index 0137e9e30d..22668c0039 100644 --- a/src/Viewer/renderers/textInput.jsx +++ b/src/Viewer/renderers/textInput.jsx @@ -1,5 +1,5 @@ import React, { useContext, useEffect, useRef, useState } from "react"; -import useDoenetRender from "../useDoenetRenderer"; +import useDoenetRenderer from "../useDoenetRenderer"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { faCheck, @@ -76,7 +76,7 @@ export default function TextInput(props) { ignoreUpdate, rendererName, callAction, - } = useDoenetRender(props); + } = useDoenetRenderer(props); let width = sizeToCSS(SVs.width); let height = sizeToCSS(SVs.height); // only for TextArea diff --git a/src/Viewer/renderers/textList.jsx b/src/Viewer/renderers/textList.jsx index 60c0ce59d5..cf3d5faadf 100644 --- a/src/Viewer/renderers/textList.jsx +++ b/src/Viewer/renderers/textList.jsx @@ -1,8 +1,8 @@ import React from "react"; -import useDoenetRender from "../useDoenetRenderer"; +import useDoenetRenderer from "../useDoenetRenderer"; export default React.memo(function TextList(props) { - let { name, id, SVs, children } = useDoenetRender(props); + let { name, id, SVs, children } = useDoenetRenderer(props); if (SVs.hidden) { return null; diff --git a/src/Viewer/renderers/vector.jsx b/src/Viewer/renderers/vector.jsx index f299344a3a..93710d6529 100644 --- a/src/Viewer/renderers/vector.jsx +++ b/src/Viewer/renderers/vector.jsx @@ -1,15 +1,13 @@ import React, { useContext, useEffect, useState, useRef } from "react"; -import useDoenetRender from "../useDoenetRenderer"; +import useDoenetRenderer from "../useDoenetRenderer"; import { BoardContext, LINE_LAYER_OFFSET, VERTEX_LAYER_OFFSET } from "./graph"; -import me from "math-expressions"; import { MathJax } from "better-react-mathjax"; -import { useRecoilValue } from "recoil"; -import { darkModeAtom } from "../../Tools/_framework/DarkmodeController"; import { textRendererStyle } from "../../Core/utils/style"; +import { PageContext } from "../PageViewer"; export default React.memo(function Vector(props) { let { name, id, SVs, actions, sourceOfUpdate, callAction } = - useDoenetRender(props); + useDoenetRenderer(props); Vector.ignoreActionsWithoutCore = () => true; @@ -43,7 +41,7 @@ export default React.memo(function Vector(props) { tailDraggable.current = SVs.tailDraggable && !SVs.fixed && !SVs.fixLocation; headDraggable.current = SVs.headDraggable && !SVs.fixed && !SVs.fixLocation; - const darkMode = useRecoilValue(darkModeAtom); + const { darkMode } = useContext(PageContext) || {}; useEffect(() => { //On unmount diff --git a/src/Viewer/renderers/video.jsx b/src/Viewer/renderers/video.jsx index de695c9d81..91c7491826 100644 --- a/src/Viewer/renderers/video.jsx +++ b/src/Viewer/renderers/video.jsx @@ -3,7 +3,7 @@ // https://github.com/XimeraProject/server/blob/master/public/javascripts/youtube.js import React, { useRef, useEffect } from "react"; -import useDoenetRender from "../useDoenetRenderer"; +import useDoenetRenderer from "../useDoenetRenderer"; import { sizeToCSS } from "./utils/css"; import VisibilitySensor from "react-visibility-sensor-v2"; import styled from "styled-components"; @@ -15,7 +15,7 @@ const VideoStyling = styled.div` `; export default React.memo(function Video(props) { - let { name, id, SVs, actions, callAction } = useDoenetRender(props); + let { name, id, SVs, actions, callAction } = useDoenetRenderer(props); let player = useRef(null); let postSkipTime = useRef(null); diff --git a/src/Viewer/useDoenetRenderer.jsx b/src/Viewer/useDoenetRenderer.jsx index bdeddf46d8..588769cf72 100644 --- a/src/Viewer/useDoenetRenderer.jsx +++ b/src/Viewer/useDoenetRenderer.jsx @@ -68,7 +68,6 @@ export default function useDoenetRenderer( key: props.coreId + childInstructions.componentName, componentInstructions: childInstructions, rendererClasses: props.rendererClasses, - flags: props.flags, coreId: props.coreId, callAction: props.callAction, }; @@ -120,5 +119,9 @@ export default function useDoenetRenderer( rendererName, initializeChildren: () => {}, callAction, + navigate: props.navigate, + location: props.location, + linkSettings: props.linkSettings, + scrollableContainer: props.scrollableContainer, }; } diff --git a/src/_reactComponents/Course/CourseActions.jsx b/src/_reactComponents/Course/CourseActions.jsx index a85026981e..6e76682129 100644 --- a/src/_reactComponents/Course/CourseActions.jsx +++ b/src/_reactComponents/Course/CourseActions.jsx @@ -228,17 +228,17 @@ function buildDoenetIdToParentDoenetIdObj(contentArray, parentDoenetId = null) { return returnObj; } -export function findFirstPageOfActivity(content = []) { +export function findFirstPageOfActivity(children = []) { let response = null; - for (let item of content) { + for (let item of children) { if (typeof item === "string" || item instanceof String) { - //First content is a string so return the doenetId + //First item is a string so return the doenetId response = item; break; } else { - //First item of content is another order - let nextOrderResponse = findFirstPageOfActivity(item.content); + //First item of children is another order + let nextOrderResponse = findFirstPageOfActivity(item.children); if ( typeof nextOrderResponse === "string" || @@ -2196,8 +2196,8 @@ export const useCourse = (courseId) => { attributeString += ` shuffleItemWeights="true"`; } - if (activity.numberOfVariants !== undefined) { - attributeString += ` numberOfVariants="${activity.numberOfVariants}"`; + if (activity.numVariants !== undefined) { + attributeString += ` numVariants="${activity.numVariants}"`; } if (activity.isSinglePage) { diff --git a/src/_sharedRecoil/PageViewerRecoil.jsx b/src/_sharedRecoil/PageViewerRecoil.jsx index fe8c4b0e52..f02d0bf176 100644 --- a/src/_sharedRecoil/PageViewerRecoil.jsx +++ b/src/_sharedRecoil/PageViewerRecoil.jsx @@ -12,5 +12,5 @@ export const pageVariantPanelAtom = atom({ export const activityVariantPanelAtom = atom({ key: "activityVariantPanelAtom", - default: { index: 1, numberOfVariants: 0 }, + default: { index: 1, numVariants: 0 }, }); diff --git a/src/_utils/activityUtils.js b/src/_utils/activityUtils.js index 18b3f8fbc1..12d714cfe0 100644 --- a/src/_utils/activityUtils.js +++ b/src/_utils/activityUtils.js @@ -9,39 +9,74 @@ import { countComponentTypes, expandDoenetMLsToFullSerializedComponents, } from "../Core/utils/serializedStateProcessing"; +import { deepClone } from "../Core/utils/deepFunctions"; +import { cidFromText } from "../Core/utils/cid"; let rngClass = prng_alea; -export function parseActivityDefinition(activityDefDoenetML) { - let serializedDefinition = parseAndCompile( - activityDefDoenetML, - ).components.filter((x) => typeof x !== "string" || /\S/.test(x)); +export async function parseActivityDefinition(activityDoenetML, activityCid) { + // parse the activity DoenetML, validate and normalize the form so that the activityJSON + // is a single object of type "activity" with: + // - itemWeights (optional): array of numbers for the weights of credit for resulting pages + // - shuffleItemWeights: currently not used, but included for the future feature + // where an author could decide, in the case where the outer order is a shuffle, + // whether or no the item weights should be shuffled along with the pages. + // The current behavior is that the item weights are not shuffled and refer to the weights + // of the pages after shuffling is finished. + // - isSinglePage: true if the activity was designated as a single page activity. + // This attribute is used in the app to determine if the activity + // should be displayed to authors as a multi-page activity. + // TODO: determine if this attribute is used downstream from this function or if can omit it here. + // - numVariants (optional): number of variants of the activity + // - xmlns (optional, at least for now): xml namespace determining version of the activity + // - children: array of orders and pages + // Returns: + // - activityJSON: the new activityJSON object + // - errors: array of any errors found + + let result = parseAndCompile(activityDoenetML); + + let errors = result.errors; + let serializedComponents = result.components; + + serializedComponents = removeOuterBlankStrings(serializedComponents); if ( - serializedDefinition.length !== 1 || - serializedDefinition[0].componentType !== "document" + serializedComponents.length !== 1 || + serializedComponents[0].componentType !== "document" ) { - return { success: false, message: `Invalid activity definition` }; + serializedComponents = [ + { + componentType: "document", + children: serializedComponents, + }, + ]; } - serializedDefinition = serializedDefinition[0]; + const serializedDocument = serializedComponents[0]; // make document props lowercase let documentProps = {}; - for (let prop in serializedDefinition.props) { + for (let prop in serializedDocument.props) { let lowerProp = prop.toLowerCase(); if (lowerProp in documentProps) { + errors.push({ + message: `Invalid activity definition: duplicate attribute ${lowerProp}`, + doenetMLrange: convertDoenetMLAttrRange( + serializedDocument.attributeRanges[prop], + ), + displayInActivity: true, + }); return { success: false, - message: `Invalid activity definition: duplicate attribute ${lowerProp}`, }; } - documentProps[prop.toLowerCase()] = serializedDefinition.props[prop]; + documentProps[prop.toLowerCase()] = serializedDocument.props[prop]; } let xmlns; - if (documentProps.type.toLowerCase() === "activity") { + if (documentProps.type?.toLowerCase() === "activity") { let jsonDefinition = { type: "activity", }; @@ -49,9 +84,15 @@ export function parseActivityDefinition(activityDefDoenetML) { if (documentProps.itemweights) { if (typeof documentProps.itemweights !== "string") { + errors.push({ + message: `Invalid activity definition: invalid itemWeights`, + doenetMLrange: convertDoenetMLAttrRange( + serializedDocument.attributeRanges.itemweights, + ), + displayInActivity: true, + }); return { success: false, - message: `Invalid activity definition: invalid itemWeights`, }; } jsonDefinition.itemWeights = documentProps.itemweights @@ -85,56 +126,70 @@ export function parseActivityDefinition(activityDefDoenetML) { xmlns = documentProps.xmlns; delete documentProps.xmlns; } else { + errors.push({ + message: `Invalid activity definition: unrecognized xmlns`, + doenetMLrange: convertDoenetMLAttrRange( + serializedDocument.attributeRanges.xmlns, + ), + displayInActivity: true, + }); + return { success: false, - message: `Invalid activity definition: unrecognized xmlns`, }; } } else { console.warn("no xmlns of activity!"); } - if (documentProps.numberofvariants) { - jsonDefinition.numberOfVariants = Number(documentProps.numberofvariants); - delete documentProps.numberofvariants; + if (documentProps.numvariants) { + jsonDefinition.numVariants = Number(documentProps.numvariants); + delete documentProps.numvariants; } if (Object.keys(documentProps).length > 0) { - return { - success: false, + let begin = Infinity; + let end = -Infinity; + for (let prop in documentProps) { + let attrRange = convertDoenetMLAttrRange( + serializedDocument.attributeRanges[prop], + ); + begin = Math.min(begin, attrRange.begin); + end = Math.max(end, attrRange.end); + } + + errors.push({ message: `Invalid activity definition: invalid document attributes: ${Object.keys( documentProps, ).join(", ")}`, - }; + doenetMLrange: { begin, end }, + displayInActivity: true, + }); } - let fakeOrder = { + let temporaryOrder = { type: "order", behavior: "sequence", - children: serializedDefinition.children, + children: serializedDocument.children, }; - let result = validateOrder(fakeOrder); + let result = await validateOrder(temporaryOrder, activityDoenetML); - if (!result.success) { - return { - success: false, - message: `Invalid activity definition: ${result.message}`, - }; - } + errors.push(...result.errors); - jsonDefinition.order = result.order; + jsonDefinition.children = result.order.children; - return { success: true, activityJSON: jsonDefinition }; - } else if (documentProps.type.toLowerCase() === "page") { - let page = { type: "page", doenetML: activityDefDoenetML }; + return { activityJSON: jsonDefinition, errors }; + } else { + let page = { + type: "page", + doenetML: activityDoenetML, + children: serializedComponents, + cid: activityCid, + }; let jsonDefinition = { type: "activity", - order: { - type: "order", - behavior: "sequence", - content: [page], - }, + children: [page], }; if (documentProps.xmlns) { @@ -145,36 +200,45 @@ export function parseActivityDefinition(activityDefDoenetML) { jsonDefinition.version = documentProps.xmlns.slice(34); delete documentProps.xmlns; } else { - return { - success: false, + errors.push({ message: `Invalid activity definition: unrecognized xmlns`, - }; + doenetMLrange: convertDoenetMLAttrRange( + serializedDocument.attributeRanges.xmlns, + ), + displayInActivity: true, + }); } } else { console.warn("no xmlns of activity!"); } - // TODO: what to do about variant control? - // It'd be great to have a way to map activity variants - // directly to page variants - // That way, we could preserve the exact variants specified in the page definition - - return { success: true, activityJSON: jsonDefinition }; - } else { - return { success: false, message: `Invalid activity definition` }; + return { activityJSON: jsonDefinition, errors }; } - function validateOrder(order) { + async function validateOrder(order, activityDoenetML) { + // Process the order object from parsing the activity definition, + // creating a new order object with + // - behavior (if in original order) + // - numberToSelect (if in original order) + // - withReplacement (if in original order) + // - children: array of pages or orders + // Returns: + // - order: the new order object + // - errors: array of any errors found + + let errors = []; + let newOrder = { type: "order" }; let orderProps = {}; for (let prop in order.props) { let lowerProp = prop.toLowerCase(); if (lowerProp in orderProps) { - return { - success: false, - message: `duplicate attribute of order ${lowerProp}`, - }; + errors.push({ + message: `duplicate attribute of : ${lowerProp}`, + doenetMLrange: convertDoenetMLAttrRange(order.attributeRanges[prop]), + displayInActivity: true, + }); } orderProps[prop.toLowerCase()] = order.props[prop]; } @@ -190,10 +254,11 @@ export function parseActivityDefinition(activityDefDoenetML) { (orderProps.withreplacement === true || orderProps.withreplacement.toLowerCase() === "true"); } else { - return { - success: false, - message: `invalid order attribute: ${prop}`, - }; + errors.push({ + message: `invalid attribute: ${prop}`, + doenetMLrange: convertDoenetMLAttrRange(order.attributeRanges[prop]), + displayInActivity: true, + }); } } @@ -202,127 +267,249 @@ export function parseActivityDefinition(activityDefDoenetML) { (x) => typeof x !== "string" || /\S/.test(x), ); - let content = []; + let children = []; for (let child of orderChildren) { - if (child.componentType.toLowerCase() === "order") { - let result = validateOrder(child); - if (result.success) { - content.push(result.order); - } else { - return result; - } + if (typeof child === "string") { + errors.push({ + message: `invalid child of , found string`, + doenetMLrange: order.doenetMLrange, // just use order for now, since don't have range for string + displayInActivity: true, + }); + } else if (child.componentType.toLowerCase() === "order") { + let result = await validateOrder(child, activityDoenetML); + children.push(result.order); + errors.push(...result.errors); } else if (child.componentType.toLowerCase() == "page") { - let result = validatePage(child); - if (result.success) { - content.push(result.page); - } else { - return result; - } + let result = await validatePage(child, activityDoenetML); + children.push(result.page); + errors.push(...result.errors); } else { - return { - success: false, - message: `invalid child of order, found type: ${child.componentType}`, - }; + errors.push({ + message: `invalid child of , found type: <${child.componentType}>`, + doenetMLrange: child.doenetMLrange, + displayInActivity: true, + }); } } - newOrder.content = content; + newOrder.children = children; return { - success: true, order: newOrder, + errors, }; } - function validatePage(page) { + async function validatePage(page, activityDoenetML) { + // Process the page object from parsing the activity definition, + // creating a new page object with + // - cid + // - doenetML: the doenetML of the page, or if children is present, + // it could be the doenetML of the entire activity, which is OK the doenetML won't be parsed in this case. + // The doenetML string needs to correspond to the doenetMLrange of children, if present, + // as snippets may be extracted (e.g,. for error messages or doenetML state variables). + // The line breaks in doenetML will also be used to calculate line numbers for user messages. + // - children (optional): the parsed children of the page. + // If not present, Core will parse the doenetML string to create the children. + // If the contents of the page were provided in the activity definition, + // we've already parsed it intot these children, + // so we send it to Core to avoid the need to parse it again. + // More importantly, we want to use the children parsed with the activity definition, + // as the line/character numbers will correspond to what the user composed. + // Returns: + // - page: the new page object + // - errors: array of any errors found + + let errors = []; + let newPage = { type: "page" }; let pageProps = {}; for (let prop in page.props) { let lowerProp = prop.toLowerCase(); if (lowerProp in pageProps) { - return { - success: false, - message: `duplicate attribute of page ${lowerProp}`, - }; + errors.push({ + message: `duplicate attribute of : ${lowerProp}`, + doenetMLrange: convertDoenetMLAttrRange(page.attributeRanges[prop]), + displayInActivity: true, + }); } pageProps[prop.toLowerCase()] = page.props[prop]; } + let foundCid = false; + for (let prop in pageProps) { if (prop === "cid") { newPage.cid = pageProps.cid; delete pageProps.cid; + foundCid = true; } else if (prop == "label") { // we ignore label, at least for now } else { - return { - success: false, - message: `invalid page attribute: ${prop}`, - }; + errors.push({ + message: `invalid attribute: ${prop}`, + doenetMLrange: convertDoenetMLAttrRange(page.attributeRanges[prop]), + displayInActivity: true, + }); } } - if (page.children.length > 0) { - let pageDoenetML = activityDefDoenetML.slice( - page.doenetMLrange.openEnd, - page.doenetMLrange.closeBegin, - ); + let pageChildren = removeOuterBlankStrings(page.children); + + if (foundCid) { + if (pageChildren.length > 0) { + errors.push({ + message: `Invalid page: cannot have both cid attribute and children.`, + doenetMLrange: page.doenetMLrange, + displayInActivity: true, + }); + } + try { + newPage.doenetML = await retrieveTextFileForCid(newPage.cid, "doenet"); + } catch (e) { + errors.push({ + message: `DoenetML for page with cid "${newPage.cid}" not found.`, + doenetMLrange: page.doenetMLrange, + displayInActivity: true, + }); + newPage.doenetML = ""; + } + } else if (pageChildren.length > 0) { + // the page had content inside the tags + + if ( + pageChildren.length > 1 || + pageChildren[0].componentType?.toLowerCase() !== "document" + ) { + // add document around page components + pageChildren = [ + { + componentType: "document", + children: pageChildren, + }, + ]; - if (page.children[0].componentType?.toLowerCase() !== "document") { - // add around page - let xmlnsprop = ""; if (xmlns) { - xmlnsprop = ` xmlns="${xmlns}"`; + pageChildren[0].props = { xmlns }; } - pageDoenetML = `${pageDoenetML}`; } - newPage.doenetML = pageDoenetML; + newPage.children = pageChildren; + + // Note: activityDoenetML is the doenetML of the entire activity, not the just this page. + // However, the doenetMLrange from the parser is in terms of the activityDoenetML, + // so this is the correct doenetML to use. + // Since we pass in serializedComponents, the doenetML will not be parsed or used for the page content. + newPage.doenetML = activityDoenetML; + + // To calculate the cid, however, we use doenetML of just the page + let pageDoenetML = activityDoenetML.slice( + page.doenetMLrange.openEnd, + page.doenetMLrange.closeBegin - 1, + ); + newPage.cid = await cidFromText(pageDoenetML); + } else { + // No cid and no content specified. Create a blank page. + newPage.serializedComponents = [ + { + componentType: "document", + }, + ]; + + if (xmlns) { + newPage.serializedComponents[0].props = { xmlns }; + } + + newPage.doenetML = ""; + newPage.cid = await cidFromText(newPage.doenetML); } return { - success: true, page: newPage, + errors, }; } } +function removeOuterBlankStrings(serializedComponents) { + let firstNonBlankInd, lastNonBlankInd; + + // find any beginning or ending blank strings; + for (let ind = 0; ind < serializedComponents.length; ind++) { + let comp = serializedComponents[ind]; + if (typeof comp !== "string" || /\S/.test(comp)) { + if (firstNonBlankInd === undefined) { + firstNonBlankInd = ind; + } + lastNonBlankInd = ind; + } + } + + serializedComponents = serializedComponents.slice( + firstNonBlankInd, + lastNonBlankInd + 1, + ); + + return serializedComponents; +} + export async function calculateOrderAndVariants({ activityDefinition, requestedVariantIndex, }) { + let errors = []; + let activityVariantResult = await determineNumberOfActivityVariants( activityDefinition, ); let variantIndex = - ((requestedVariantIndex - 1) % activityVariantResult.numberOfVariants) + 1; + ((requestedVariantIndex - 1) % activityVariantResult.numVariants) + 1; if (variantIndex < 1) { - variantIndex += activityVariantResult.numberOfVariants; + variantIndex += activityVariantResult.numVariants; } if (!Number.isFinite(variantIndex)) { - return { success: false, message: "Invalid requested variant index" }; + errors.push({ + message: "Invalid requested variant index", + displayInActivity: true, + }); + variantIndex = 1; } let rng = new rngClass(variantIndex.toString()); - let orderResult = determineOrder(activityDefinition.order, rng); + let activityPages = []; - if (!orderResult.success) { - return orderResult; + for (let item of activityDefinition.children) { + let type = item.type.toLowerCase(); + if (type === "page") { + activityPages.push(item); + } else if (type === "order") { + let orderResult = determineOrder(item, rng); + errors.push(...orderResult.errors); + activityPages.push(...orderResult.pages); + } else { + errors.push({ + message: `Unrecognized item <${item.type}> in activity.`, + doenetMLrange: item.doenetMLrange, + displayInActivity: true, + }); + } } - let originalOrder = orderResult.pages; - let itemWeights = activityDefinition.itemWeights || []; if (!Array.isArray(itemWeights) || !itemWeights.every((x) => x >= 0)) { - return { success: false, message: "Invalid itemWeights" }; + errors.push({ + message: "Invalid itemWeights", + displayInActivity: true, + }); + itemWeights = []; } - let nPages = originalOrder.length; + let nPages = activityPages.length; itemWeights = itemWeights.slice(0, nPages); @@ -338,15 +525,15 @@ export async function calculateOrderAndVariants({ let pageVariantsResult; - if (activityVariantResult.pageVariants) { - pageVariantsResult = [activityVariantResult.pageVariants]; + if (activityVariantResult.pageVariantsResult) { + pageVariantsResult = activityVariantResult.pageVariantsResult; } else { let promises = []; - for (let page of originalOrder) { + for (let page of activityPages) { promises.push( returnAllPossibleVariants({ - cid: page.cid, doenetML: page.doenetML, + serializedComponents: page.children, }), ); } @@ -354,143 +541,117 @@ export async function calculateOrderAndVariants({ try { pageVariantsResult = await Promise.all(promises); } catch (e) { - return { - success: false, + errors.push({ message: `Error retrieving content for activity. ${e.message}`, - }; + displayInActivity: true, + }); + + pageVariantsResult = []; + for (let page of activityPages) { + pageVariantsResult.push("error"); + } } } - let variantForEachPage; + let variantsByPage; let allPossiblePerPage = []; - let numberOfVariantsPerPage = []; + let numVariantsPerPage = []; - for (let pageResult of pageVariantsResult) { - allPossiblePerPage.push(pageResult.allPossibleVariants); - numberOfVariantsPerPage.push(pageResult.allPossibleVariants.length); + for (let possibleVariants of pageVariantsResult) { + allPossiblePerPage.push(possibleVariants); + numVariantsPerPage.push(possibleVariants.length); } - let numberOfPageVariantCombinations = numberOfVariantsPerPage.reduce( + let numberOfPageVariantCombinations = numVariantsPerPage.reduce( (a, c) => a * c, 1, ); - if ( - numberOfPageVariantCombinations <= activityVariantResult.numberOfVariants - ) { + if (numberOfPageVariantCombinations <= activityVariantResult.numVariants) { let pageVariantCombinationIndex = ((variantIndex - 1) % numberOfPageVariantCombinations) + 1; - variantForEachPage = enumerateCombinations({ - numberOfOptionsByIndex: numberOfVariantsPerPage, + variantsByPage = enumerateCombinations({ + numberOfOptionsByIndex: numVariantsPerPage, maxNumber: pageVariantCombinationIndex, })[pageVariantCombinationIndex - 1].map((x) => x + 1); } else { - variantForEachPage = [...Array(nPages).keys()].map( - (i) => Math.floor(rng() * numberOfVariantsPerPage[i]) + 1, + variantsByPage = [...Array(nPages).keys()].map( + (i) => Math.floor(rng() * numVariantsPerPage[i]) + 1, ); } - let variantsByPage = []; - - let newOrder = []; - for (let [ind, possibleVariants] of pageVariantsResult.entries()) { - let pageVariantIndex = variantForEachPage[ind]; - - variantsByPage.push(pageVariantIndex); - - // if looked up doenetML to determine possible variants - // record that doenetML in the order so don't have to load it again - // Also, add cid if it isn't there - let page = originalOrder[ind]; - if (page.doenetML === undefined) { - page = { ...page }; - page.doenetML = possibleVariants.doenetML; - } else if (!page.cid) { - page = { ...page }; - page.cid = possibleVariants.cid; - } - newOrder.push(page); - } - - let orderWithCids = [...originalOrder]; - newOrder.forEach((v, i) => (orderWithCids[i].cid = v.cid)); - let previousComponentTypeCounts = await initializeComponentTypeCounts( - newOrder, + activityPages, ); let activityInfo = { - orderWithCids, + orderWithCids: activityPages, variantsByPage, itemWeights, - numberOfVariants: activityVariantResult.numberOfVariants, + numVariants: activityVariantResult.numVariants, previousComponentTypeCounts, }; return { - success: true, - order: newOrder, + errors, + order: activityPages, itemWeights, variantsByPage, variantIndex, activityInfo, previousComponentTypeCounts, + pageVariantsResult, }; } export async function determineNumberOfActivityVariants(activityDefinition) { - let numberOfVariants = 1000; + let numVariants = 1000; let pageVariantsResult = null; - if (activityDefinition.numberOfVariants !== undefined) { - numberOfVariants = activityDefinition.numberOfVariants; - if (!(Number.isInteger(numberOfVariants) && numberOfVariants >= 1)) { - numberOfVariants = 1000; + if (activityDefinition.numVariants !== undefined) { + numVariants = activityDefinition.numVariants; + if (!(Number.isInteger(numVariants) && numVariants >= 1)) { + numVariants = 1000; } - } else if ( - (activityDefinition.order.behavior === undefined || - activityDefinition.order.behavior === "sequence") && - activityDefinition.order.content.every((x) => x.type === "page") - ) { + } else if (activityDefinition.children.every((x) => x.type === "page")) { // determine number of variants from the pages let promises = []; - for (let page of activityDefinition.order.content) { + for (let page of activityDefinition.children) { promises.push( returnAllPossibleVariants({ - cid: page.cid, doenetML: page.doenetML, + serializedComponents: page.children, }), ); } pageVariantsResult = await Promise.all(promises); - numberOfVariants = pageVariantsResult.reduce( - (a, c) => a * c.allPossibleVariants.length, - 1, - ); + numVariants = pageVariantsResult.reduce((a, c) => a * c.length, 1); - numberOfVariants = Math.min(1000, numberOfVariants); + numVariants = Math.min(1000, numVariants); } - return { numberOfVariants, pageVariantsResult }; + return { numVariants, pageVariantsResult }; } export async function returnNumberOfActivityVariantsForCid(cid) { let activityDefinitionDoenetML = await retrieveTextFileForCid(cid); - let result = parseActivityDefinition(activityDefinitionDoenetML); + let result = await parseActivityDefinition(activityDefinitionDoenetML); - if (!result.success) { - return result; - } + let errors = result.errors; result = await determineNumberOfActivityVariants(result.activityJSON); - return { success: true, numberOfVariants: result.numberOfVariants }; + return { + numVariants: result.numVariants, + pageVariantsResult: result.pageVariantsResult, + errors, + }; } function determineOrder(order, rng) { @@ -521,29 +682,32 @@ function determineOrder(order, rng) { function processSequenceOrder(order, rng) { let pages = []; + let errors = []; - for (let item of order.content) { + for (let item of order.children) { let type = item.type.toLowerCase(); if (type === "page") { pages.push(item); } else if (type === "order") { let orderResult = determineOrder(item, rng); - if (orderResult.success) { - pages.push(...orderResult.pages); - } else { - return orderResult; - } + errors.push(...orderResult.errors); + pages.push(...orderResult.pages); } else { - return { success: false, message: "Unrecognized item in order." }; + errors.push({ + message: `Unrecognized item <${item.type}> in order.`, + doenetMLrange: item.doenetMLrange, + displayInActivity: true, + }); } } - return { success: true, pages }; + return { pages, errors }; } function processSelectOrder(order, rng) { let numberToSelect = order.numberToSelect; - let nItems = order.content.length; + let nItems = order.children.length; + let errors = []; if (!(Number.isInteger(numberToSelect) && numberToSelect > 0)) { numberToSelect = 1; @@ -555,20 +719,29 @@ function processSelectOrder(order, rng) { } if (numberUniqueRequired > nItems) { - return { - success: false, + errors.push({ message: "Cannot select " + numberUniqueRequired + " components from only " + nItems, + doenetMLrange: order.doenetMLrange, + displayInActivity: true, + }); + return { + pages: [], + errors, }; } let selectWeights = order.selectWeights || []; if (!Array.isArray(selectWeights) || !selectWeights.every((x) => x >= 0)) { - return { success: false, message: "Invalid selectWeights" }; + errors.push({ + message: "Invalid selectWeights", + doenetMLrange: order.doenetMLrange, + displayInActivity: true, + }); } selectWeights = selectWeights.slice(0, nItems); @@ -607,7 +780,7 @@ function processSelectOrder(order, rng) { } } - selectedItems.push(order.content[indsRemaining[end]]); + selectedItems.push(order.children[indsRemaining[end]]); if (!order.withReplacement && ind < numberToSelect - 1) { // remove selected index and renormalize weights @@ -630,25 +803,27 @@ function processSelectOrder(order, rng) { pages.push(item); } else if (type === "order") { let orderResult = determineOrder(item); - if (orderResult.success) { - pages.push(...orderResult.pages); - } else { - return orderResult; - } + pages.push(...orderResult.pages); + errors.push(...orderResult.errors); } else { - return { success: false, message: "Unrecognized item in order." }; + errors.push({ + message: `Unrecognized item ${item.type} in order`, + doenetMLrange: item.doenetMLrange, + displayInActivity: true, + }); } } - return { success: true, pages }; + return { pages, errors }; } function processShuffleOrder(order, rng) { - let newOrder = [...order.content]; + let newOrder = [...order.children]; + let errors = []; // shuffle the order // https://stackoverflow.com/a/12646864 - for (let i = order.content.length - 1; i > 0; i--) { + for (let i = order.children.length - 1; i > 0; i--) { const rand = rng(); const j = Math.floor(rand * (i + 1)); [newOrder[i], newOrder[j]] = [newOrder[j], newOrder[i]]; @@ -662,29 +837,30 @@ function processShuffleOrder(order, rng) { pages.push(item); } else if (type === "order") { let orderResult = determineOrder(item, rng); - if (orderResult.success) { - pages.push(...orderResult.pages); - } else { - return orderResult; - } + pages.push(...orderResult.pages); + errors.push(...orderResult.errors); } else { - return { success: false, message: "Unrecognized item in order." }; + errors.push({ + message: `Unrecognized item ${item.type} in order`, + doenetMLrange: item.doenetMLrange, + displayInActivity: true, + }); } } - return { success: true, pages }; + return { pages, errors }; } -async function initializeComponentTypeCounts(order) { +async function initializeComponentTypeCounts(pages) { let previousComponentTypeCountsByPage = [{}]; let componentInfoObjects = createComponentInfoObjects(); - for (let [ind, page] of order.slice(0, order.length - 1).entries()) { + for (let [ind, page] of pages.slice(0, pages.length - 1).entries()) { let { fullSerializedComponents } = await expandDoenetMLsToFullSerializedComponents({ - contentIds: [page.cid], doenetMLs: [page.doenetML], + preliminarySerializedComponents: [page.children], componentInfoObjects, }); @@ -710,3 +886,14 @@ async function initializeComponentTypeCounts(order) { return previousComponentTypeCountsByPage; } + +function convertDoenetMLAttrRange(doenetMLrange) { + if (doenetMLrange?.attrBegin) { + return { + begin: doenetMLrange.attrBegin, + end: doenetMLrange.attrEnd, + }; + } else { + return doenetMLrange; + } +} diff --git a/src/_utils/prerenderWorker.js b/src/_utils/prerenderWorker.js index 4b68e7d460..b858bc9a4e 100644 --- a/src/_utils/prerenderWorker.js +++ b/src/_utils/prerenderWorker.js @@ -23,16 +23,20 @@ async function prerenderActivity({ cid, doenetId, flags = {} }) { return; } - let parseResult = parseActivityDefinition(activityDefDoenetML); + let parseResult = await parseActivityDefinition(activityDefDoenetML, cid); - if (!parseResult.success) { - postMessage({ messageType: "error", message: parseResult.message }); + // TODO: handle error display better + if (parseResult.errors.length > 0) { + postMessage({ + messageType: "error", + message: parseResult.errors[0].message, + }); return; } let activityDefinition = parseResult.activityJSON; - let { numberOfVariants } = await determineNumberOfActivityVariants( + let { numVariants } = await determineNumberOfActivityVariants( activityDefinition, ); @@ -43,15 +47,15 @@ async function prerenderActivity({ cid, doenetId, flags = {} }) { const variantsNeededByPage = {}; const doenetMLByPage = {}; - for (let variantIndex = 1; variantIndex <= numberOfVariants; variantIndex++) { - console.log(`Gathering ${variantIndex} of ${numberOfVariants}`); + for (let variantIndex = 1; variantIndex <= numVariants; variantIndex++) { + console.log(`Gathering ${variantIndex} of ${numVariants}`); let result = await calculateOrderAndVariants({ activityDefinition, requestedVariantIndex: variantIndex, }); - if (!result.success) { + if (result.errors.length > 0) { console.error(`Couldn't save initial renderer state: ${result.message}`); continue; } @@ -77,7 +81,7 @@ async function prerenderActivity({ cid, doenetId, flags = {} }) { postMessage({ messageType: "status", stage: "Gathering", - complete: variantIndex / numberOfVariants, + complete: variantIndex / numVariants, }); } }