Skip to content

Commit

Permalink
🔨Upgraded to react 18 (#201)
Browse files Browse the repository at this point in the history
* Upgraded to react 18

* Fixed progress not animating

* Fixed React-Modal: Cannot register modal instance that's already open

* Updated cypress to react 18

* Upgraded framer-motion + removed d3-ease

* Fixed first question being fetched twice

* refactored gap-text and gap-text dropdown
  • Loading branch information
Rllyyy authored Apr 27, 2023
1 parent 4a97f2a commit 5208839
Show file tree
Hide file tree
Showing 30 changed files with 298 additions and 292 deletions.
2 changes: 1 addition & 1 deletion cypress/e2e/addModule.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ describe("Test importing a new module", () => {

//Should close modal
cy.contains("h1", "Create or import a Module").should("not.exist");
cy.get(".ReactModalPortal").should("not.exist");
cy.get(".ReactModal__Overlay--after-open").should("not.exist");
});

//Test if the toast for successful imports works
Expand Down
18 changes: 18 additions & 0 deletions cypress/e2e/addQuestions.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,10 @@ describe("Adding a question of type gap-text", () => {

cy.visit("/module/empty-questions/question/test-id?mode=practice&order=chronological");
cy.get("section.question-user-response").find("input").type("test");

// Click the dom to trigger lose of focus on input and rerender of component
cy.get("body").click();

cy.get("button[type='submit']").click();
cy.contains("Yes, that's correct!").should("exist");
});
Expand All @@ -261,6 +265,10 @@ describe("Adding a question of type gap-text", () => {
cy.get("section.question-user-response").find("input").first().type("This");
cy.get("section.question-user-response").find("input").eq(1).type("complex");
cy.get("section.question-user-response").find("input").last().type("test");

// Click the dom to trigger lose of focus on input and rerender of component
cy.get("body").click();

cy.get("button[type='submit']").click();
cy.contains("Yes, that's correct!").should("exist");
});
Expand All @@ -271,12 +279,19 @@ describe("Adding a question of type gap-text", () => {

cy.visit("/module/empty-questions/question/test-id?mode=practice&order=chronological");
cy.get("section.question-user-response").find("input").type("more than one");
// Click the dom to trigger lose of focus on input and rerender of component
cy.get("body").click();

cy.get("button[type='submit']").click();
cy.contains("Yes, that's correct!").should("exist");
cy.contains("This text supports multiple; more than one correct values").should("exist");

cy.get("button[aria-label='Retry Question']").click();
cy.get("section.question-user-response").find("input").type("multiple");

// Click the dom to trigger lose of focus on input and rerender of component
cy.get("body").click();

cy.get("button[type='submit']").click();
cy.contains("Yes, that's correct!").should("exist");
});
Expand Down Expand Up @@ -341,6 +356,9 @@ describe("Adding a question of type gap-text", () => {
cy.get("section.question-user-response").find("input").first().type("This");
cy.get("section.question-user-response").find("input").last().type("gap");

// Click the dom to trigger lose of focus on input and rerender of component
cy.get("body").click();

cy.get("button[type='submit']").click();
cy.contains("Yes, that's correct!").should("exist");
cy.get("span.correct-gap-value").first().should("have.text", "This");
Expand Down
2 changes: 1 addition & 1 deletion cypress/e2e/bookmarkedQuestions.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -280,7 +280,7 @@ describe("Test import of bookmarked Questions", () => {
});

it("should add items to the localStorage if there are no old items in the localStorage before", () => {
cy.clearLocalStorage();
cy.clearLocalStorage("repeatio-marked-cypress_1");

cy.get("article[data-cy='Bookmarked Questions']").find("button.popover-button").click();

Expand Down
1 change: 0 additions & 1 deletion cypress/e2e/deleteModule.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ describe("Test deletion of module", () => {
});
});

//TODO delete this
it("should delete module that is located in localStorage", () => {
//Add item to localStorage and check existence
cy.fixtureToLocalStorage("repeatio-module-cypress_1.json");
Expand Down
1 change: 1 addition & 0 deletions cypress/e2e/publicModule.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ describe("Test the module that is provided by the public folder", () => {
cy.get("#input-0").type("gaps");
cy.get("#input-1").type("correct");
cy.get("#input-2").type("not");
cy.get("body").click();
cy.get("button[aria-label='Check Question']").click();
cy.contains("Yes, that's correct!");
cy.get("button[aria-label='Next Question']").click();
Expand Down
1 change: 1 addition & 0 deletions cypress/e2e/updateQuestion.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,7 @@ describe("Updating a question of type gap-text", () => {
});

cy.get("section.question-user-response").find("input").type("updated");
cy.get("body").click();
cy.get("button[type='submit']").click();
cy.contains("p", "Yes, that's correct!").should("exist");
cy.contains("This is the updated question").should("exist");
Expand Down
4 changes: 2 additions & 2 deletions cypress/support/commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@
//To enable tabbing in cypress: https://github.com/dmtrKovalenko/cypress-real-events
import "cypress-real-events";

// React 16, 17
import { mount } from "cypress/react";
// React 18
import { mount } from "cypress/react18";

type Fixtures =
| "repeatio-module-gap_text.json"
Expand Down
2 changes: 1 addition & 1 deletion cypress/support/component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import "./commands";
// Alternatively you can use CommonJS syntax:
// require('./commands')

import { mount } from "cypress/react";
import { mount } from "cypress/react18";

// Augment the Cypress namespace to include type definitions for
// your custom command.
Expand Down
14 changes: 6 additions & 8 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,37 +12,35 @@
"@mui/material": "^5.11.15",
"@react-hook/resize-observer": "^1.2.6",
"@testing-library/jest-dom": "^5.16.5",
"@testing-library/react": "^12.1.5",
"@testing-library/react": "^14.0.0",
"@testing-library/user-event": "^12.8.3",
"@types/dompurify": "^3.0.0",
"@types/file-saver": "^2.0.5",
"@types/jest": "^29.5.0",
"@types/node": "^18.15.11",
"@types/react": "^18.0.31",
"@types/react-dom": "^18.0.11",
"@types/react": "^18.2.0",
"@types/react-dom": "^18.2.0",
"@types/react-router-dom": "^5.3.3",
"concurrently": "^7.6.0",
"cross-env": "^7.0.3",
"cypress": "^12.9.0",
"cypress-real-events": "^1.7.6",
"d3-ease": "^3.0.1",
"dompurify": "^3.0.1",
"electron-devtools-installer": "^3.2.0",
"electron-is-dev": "^2.0.0",
"file-saver": "^2.0.5",
"framer-motion": "6.2.4",
"framer-motion": "10.12.4",
"is-electron": "^2.2.2",
"lodash": "^4.17.21",
"react": "^17.0.2",
"react": "^18.2.0",
"react-circular-progressbar": "^2.1.0",
"react-device-detect": "^2.2.3",
"react-dom": "^17.0.2",
"react-dom": "^18.2.0",
"react-dropzone": "^14.2.3",
"react-hook-form": "^7.43.9",
"react-icons": "^4.8.0",
"react-markdown": "^8.0.6",
"react-modal": "^3.16.1",
"react-move": "^6.5.0",
"react-router-dom": "^6.10.0",
"react-textarea-autosize": "^8.4.1",
"react-toastify": "^9.1.2",
Expand Down
33 changes: 17 additions & 16 deletions src/components/Card/Card.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Link, LinkProps } from "react-router-dom";
import { memo, ReactNode } from "react";
import { memo, PropsWithChildren, ReactNode, FC } from "react";
import { motion, MotionProps } from "framer-motion";

//css
Expand All @@ -15,24 +15,25 @@ interface ICard extends MotionProps {
title: string;
description?: string;
icon: JSX.Element;
children: ReactNode;
}

//Card Component
export const Card = memo(({ type, disabled, title, description, icon, children, ...props }: ICard) => {
return (
<motion.article className={`card ${disabled ? "disabled " : "active"}`} {...props}>
<div className='card-title-info-wrapper' style={type === "module" ? { paddingRight: "10px" } : {}}>
<h2 className='card-title'>{title}</h2>
<p className='card-description'>{description}</p>
</div>
<div className='svg-wrapper' style={type === "module-card" ? { padding: "12px", marginTop: "-12px" } : {}}>
{icon}
</div>
<div className='card-bottom'>{children}</div>
</motion.article>
);
});
export const Card: FC<PropsWithChildren<ICard>> = memo(
({ type, disabled, title, description, icon, children, ...props }) => {
return (
<motion.article className={`card ${disabled ? "disabled " : "active"}`} {...props}>
<div className='card-title-info-wrapper' style={type === "module" ? { paddingRight: "10px" } : {}}>
<h2 className='card-title'>{title}</h2>
<p className='card-description'>{description}</p>
</div>
<div className='svg-wrapper' style={type === "module-card" ? { padding: "12px", marginTop: "-12px" } : {}}>
{icon}
</div>
<div className='card-bottom'>{children}</div>
</motion.article>
);
}
);

/* ----------------------------------------- LINK -------------------------------------------- */
//!This extend might be false
Expand Down
5 changes: 3 additions & 2 deletions src/components/Card/Popover.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
*/

/* ----------------------------------- Imports -------------------------------------------- */
import { FC, PropsWithChildren } from "react";

//MaterialUI
import { styled } from "@mui/material/styles";
import Menu, { MenuProps } from "@mui/material/Menu";
Expand Down Expand Up @@ -88,11 +90,10 @@ const StyledMenu = styled((props: MenuProps) => (
interface IPopoverMenu {
anchorEl: HTMLButtonElement | null;
handlePopoverClose: () => void;
children: React.ReactNode;
}

//PopoverMenu that is returned
export const PopoverMenu = ({ anchorEl, handlePopoverClose, children }: IPopoverMenu) => {
export const PopoverMenu: FC<PropsWithChildren<IPopoverMenu>> = ({ anchorEl, handlePopoverClose, children }) => {
return (
<StyledMenu
MenuListProps={{
Expand Down
63 changes: 14 additions & 49 deletions src/components/Card/ProgressPie.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
import { useState, useEffect } from "react";
import { Animate } from "react-move";
import { CircularProgressbar, buildStyles } from "react-circular-progressbar";
import { easeQuadInOut } from "d3-ease";

//CSS
import "react-circular-progressbar/dist/styles.css";
Expand All @@ -10,7 +8,7 @@ import "react-circular-progressbar/dist/styles.css";
export const ProgressPie = ({ progress }: { progress: number }) => {
//https://codesandbox.io/s/vymm4oln6y?file=/index.js:3609-3739
return (
<AnimatedProgressProvider valueStart={0} valueEnd={progress} duration={0.7} easingFunction={easeQuadInOut}>
<ProgressProvider valueStart={0} valueEnd={progress}>
{(value: number) => {
const roundedValue = Math.round(value);
return (
Expand All @@ -23,66 +21,33 @@ export const ProgressPie = ({ progress }: { progress: number }) => {
trailColor: "rgb(230, 230, 230)",
textSize: "1.2rem",
textColor: "rgb(75, 75, 75)",
pathTransition: "none",
pathTransitionDuration: 0.7,
pathTransition: "ease-in-out",
})}
/>
);
}}
</AnimatedProgressProvider>
</ProgressProvider>
);
};

type TAnimatedProgressProvider = {
// The animation was previously handled by react-move but there is no support for react 18
// https://github.com/sghall/react-move/issues/88
type TProgressProvider = {
valueStart: number;
valueEnd: number;
duration: number;
easingFunction: (normalizedTime: number) => number;
children: (value: number) => React.ReactElement;
};

//Animate the progress bar
//Pre 16.8 react code can be found here: https://codesandbox.io/s/vymm4oln6y?file=/index.js:3609-3739
const AnimatedProgressProvider: React.FC<TAnimatedProgressProvider> = ({
valueStart,
valueEnd,
duration,
easingFunction,
children,
}) => {
// State to track whether the animation is currently running
const [isAnimated, setIsAnimated] = useState(false);

// Trigger the animation to start on component mount
const ProgressProvider: React.FC<TProgressProvider> = ({ valueStart, valueEnd, children }) => {
const [value, setValue] = useState(valueStart);
useEffect(() => {
setIsAnimated(true);
// Clean up the animation state when the component unmounts
setValue(valueEnd);

return () => {
setIsAnimated(false);
setValue(valueStart);
};
}, []);
}, [valueEnd]);

// JSX for the component
return (
// Use the Animate component to control the animation
<Animate
// Set the starting value for the animation
start={() => ({
value: valueStart,
})}
// Update the animation value and timing
update={() => ({
// Use the current value of `isAnimated` to determine the end value for the animation
value: [isAnimated ? valueEnd : valueStart],
timing: {
// Convert the duration from seconds to milliseconds
duration: duration * 1000,
// Use the provided easing function
ease: easingFunction,
},
})}
>
{/* Render the children with the current value of the animation */}
{({ value }) => children(value)}
</Animate>
);
return children(value);
};
16 changes: 11 additions & 5 deletions src/components/CustomModal/CustomModal.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useState, useEffect } from "react";
import { useState, useEffect, PropsWithChildren } from "react";
import Modal from "react-modal";

import { IoClose } from "react-icons/io5";
Expand Down Expand Up @@ -28,16 +28,22 @@ type Height =

interface ICustomModal {
handleModalClose: () => void;
showModal: boolean;
title: string;
//Pass any valid css height
desktopModalHeight: Height;
children: JSX.Element;
}

export const CustomModal = ({ handleModalClose, title, desktopModalHeight, children }: ICustomModal) => {
export const CustomModal: React.FC<PropsWithChildren<ICustomModal>> = ({
handleModalClose,
showModal,
title,
desktopModalHeight,
children,
}) => {
const [mobileLayout, setMobileLayout] = useState(false);

Modal.setAppElement("main");
Modal.setAppElement(document.getElementsByTagName("main")[0]);

//TODO replace this in the future with css container query
useEffect(() => {
Expand All @@ -58,7 +64,7 @@ export const CustomModal = ({ handleModalClose, title, desktopModalHeight, child

return (
<Modal
isOpen={true}
isOpen={showModal}
parentSelector={() => document.getElementsByTagName("main")[0]}
onRequestClose={handleModalClose}
className={desktopModalHeight === "fit-content" ? "fit-content" : "max-modal"}
Expand Down
3 changes: 2 additions & 1 deletion src/components/GridCards/GridCards.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import "./GridCards.css";
import { motion } from "framer-motion";
import { PropsWithChildren } from "react";

export const GridCards = ({ children }: { children: React.ReactNode }) => {
export const GridCards: React.FC<PropsWithChildren> = ({ children }) => {
return (
<motion.div layout className='grid-cards'>
{children}
Expand Down
17 changes: 8 additions & 9 deletions src/components/Home/AddModule.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,15 +26,14 @@ export const AddModule = () => {
<button className='add-module-btn' onClick={() => setShowModal(true)} type='button'>
<p>Add Module</p>
</button>
{showModal && (
<CustomModal
handleModalClose={handleModalClose}
title='Create or import a Module'
desktopModalHeight='fit-content'
>
<ComponentChooser handleModalClose={handleModalClose} />
</CustomModal>
)}
<CustomModal
handleModalClose={handleModalClose}
showModal={showModal}
title='Create or import a Module'
desktopModalHeight='fit-content'
>
<ComponentChooser handleModalClose={handleModalClose} />
</CustomModal>
</>
);
};
Expand Down
Loading

0 comments on commit 5208839

Please sign in to comment.