From fa7d951d6ae0beeb1ad05673a186276d31288494 Mon Sep 17 00:00:00 2001
From: Claire Peng
Date: Sun, 26 Oct 2025 22:41:05 +0000
Subject: [PATCH 01/52] client/modules/User/pages/EmailVerificationView: update
to ts, no-verify
---
.../{EmailVerificationView.jsx => EmailVerificationView.tsx} | 0
1 file changed, 0 insertions(+), 0 deletions(-)
rename client/modules/User/pages/{EmailVerificationView.jsx => EmailVerificationView.tsx} (100%)
diff --git a/client/modules/User/pages/EmailVerificationView.jsx b/client/modules/User/pages/EmailVerificationView.tsx
similarity index 100%
rename from client/modules/User/pages/EmailVerificationView.jsx
rename to client/modules/User/pages/EmailVerificationView.tsx
From c7f73e3d655d3674504aa2b574461002a32e76f5 Mon Sep 17 00:00:00 2001
From: Claire Peng
Date: Sun, 26 Oct 2025 22:43:45 +0000
Subject: [PATCH 02/52] client/modules/User/pages/EmailVerificationView: add
types for emailVerificationTokenState
---
client/modules/User/pages/EmailVerificationView.tsx | 6 +++---
client/modules/User/reducers.ts | 2 +-
2 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/client/modules/User/pages/EmailVerificationView.tsx b/client/modules/User/pages/EmailVerificationView.tsx
index d21d2446b9..7ce98d12fa 100644
--- a/client/modules/User/pages/EmailVerificationView.tsx
+++ b/client/modules/User/pages/EmailVerificationView.tsx
@@ -6,14 +6,15 @@ import { useTranslation } from 'react-i18next';
import { verifyEmailConfirmation } from '../actions';
import { RootPage } from '../../../components/RootPage';
import Nav from '../../IDE/components/Header/Nav';
+import type { RootState } from '../../../reducers';
-const EmailVerificationView = () => {
+export const EmailVerificationView = () => {
const { t } = useTranslation();
const location = useLocation();
const dispatch = useDispatch();
const browserHistory = useHistory();
const emailVerificationTokenState = useSelector(
- (state) => state.user.emailVerificationTokenState
+ (state: RootState) => state.user.emailVerificationTokenState
);
const verificationToken = useMemo(() => {
const searchParams = new URLSearchParams(location.search);
@@ -53,4 +54,3 @@ const EmailVerificationView = () => {
);
};
-export default EmailVerificationView;
diff --git a/client/modules/User/reducers.ts b/client/modules/User/reducers.ts
index 08c07b94bf..584ef24adb 100644
--- a/client/modules/User/reducers.ts
+++ b/client/modules/User/reducers.ts
@@ -16,7 +16,7 @@ export const user = (
resetPasswordInitiate?: boolean;
resetPasswordInvalid?: boolean;
emailVerificationInitiate?: boolean;
- emailVerificationTokenState?: boolean;
+ emailVerificationTokenState?: 'checking' | 'verified' | 'invalid';
} = {
authenticated: false
},
From 05998d921b275ab90a7d40dfb1f415b99490f26b Mon Sep 17 00:00:00 2001
From: Claire Peng
Date: Sun, 26 Oct 2025 22:44:47 +0000
Subject: [PATCH 03/52] client/modules/User/pages/EmailVerificationView: update
to named export
---
client/routes.jsx | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/client/routes.jsx b/client/routes.jsx
index 8926a95bdd..42215ae611 100644
--- a/client/routes.jsx
+++ b/client/routes.jsx
@@ -13,7 +13,7 @@ import TermsOfUse from './modules/Legal/pages/TermsOfUse';
import LoginView from './modules/User/pages/LoginView';
import SignupView from './modules/User/pages/SignupView';
import ResetPasswordView from './modules/User/pages/ResetPasswordView';
-import EmailVerificationView from './modules/User/pages/EmailVerificationView';
+import { EmailVerificationView } from './modules/User/pages/EmailVerificationView';
import NewPasswordView from './modules/User/pages/NewPasswordView';
import AccountView from './modules/User/pages/AccountView';
import CollectionView from './modules/User/pages/CollectionView';
From b56adb06b4b8633d96e3ac395b4c4e13ec4875e9 Mon Sep 17 00:00:00 2001
From: Claire Peng
Date: Sun, 26 Oct 2025 22:48:44 +0000
Subject: [PATCH 04/52] client/modules/User/pages/CollectionView: update to ts,
no-verify
---
.../modules/User/pages/{CollectionView.jsx => CollectionView.tsx} | 0
1 file changed, 0 insertions(+), 0 deletions(-)
rename client/modules/User/pages/{CollectionView.jsx => CollectionView.tsx} (100%)
diff --git a/client/modules/User/pages/CollectionView.jsx b/client/modules/User/pages/CollectionView.tsx
similarity index 100%
rename from client/modules/User/pages/CollectionView.jsx
rename to client/modules/User/pages/CollectionView.tsx
From c57ae93dbe7f6ae3194e76e452d7bf42587061db Mon Sep 17 00:00:00 2001
From: Claire Peng
Date: Sun, 26 Oct 2025 22:49:11 +0000
Subject: [PATCH 05/52] client/modules/User/pages/CollectionView: add tests and
update to named export
---
client/modules/User/pages/CollectionView.tsx | 7 +++----
client/routes.jsx | 2 +-
2 files changed, 4 insertions(+), 5 deletions(-)
diff --git a/client/modules/User/pages/CollectionView.tsx b/client/modules/User/pages/CollectionView.tsx
index 8207388086..958b84737c 100644
--- a/client/modules/User/pages/CollectionView.tsx
+++ b/client/modules/User/pages/CollectionView.tsx
@@ -4,8 +4,9 @@ import Nav from '../../IDE/components/Header/Nav';
import { RootPage } from '../../../components/RootPage';
import Collection from '../components/Collection';
-const CollectionView = () => {
- const params = useParams();
+export const CollectionView = () => {
+ // eslint-disable-next-line camelcase
+ const params = useParams<{ collection_id: string; username: string }>();
return (
@@ -17,5 +18,3 @@ const CollectionView = () => {
);
};
-
-export default CollectionView;
diff --git a/client/routes.jsx b/client/routes.jsx
index 42215ae611..d8b56f80f1 100644
--- a/client/routes.jsx
+++ b/client/routes.jsx
@@ -16,7 +16,7 @@ import ResetPasswordView from './modules/User/pages/ResetPasswordView';
import { EmailVerificationView } from './modules/User/pages/EmailVerificationView';
import NewPasswordView from './modules/User/pages/NewPasswordView';
import AccountView from './modules/User/pages/AccountView';
-import CollectionView from './modules/User/pages/CollectionView';
+import { CollectionView } from './modules/User/pages/CollectionView';
import DashboardView from './modules/User/pages/DashboardView';
import { getUser } from './modules/User/actions';
import ProtectedSketchRoute from './protected-route';
From 2fe9aa92e7e06a2e9cf8cf64e147ada6ed160eb9 Mon Sep 17 00:00:00 2001
From: Claire Peng
Date: Sun, 26 Oct 2025 22:54:38 +0000
Subject: [PATCH 06/52] client/modules/User/pages/LoginView: update to ts,
no-verify
---
client/modules/User/pages/{LoginView.jsx => LoginView.tsx} | 0
1 file changed, 0 insertions(+), 0 deletions(-)
rename client/modules/User/pages/{LoginView.jsx => LoginView.tsx} (100%)
diff --git a/client/modules/User/pages/LoginView.jsx b/client/modules/User/pages/LoginView.tsx
similarity index 100%
rename from client/modules/User/pages/LoginView.jsx
rename to client/modules/User/pages/LoginView.tsx
From 5db6d3a90698bd17f7c3e5a14af6cb188654177d Mon Sep 17 00:00:00 2001
From: Claire Peng
Date: Sun, 26 Oct 2025 22:56:12 +0000
Subject: [PATCH 07/52] resolve merge conflict with LoginView
---
client/modules/User/pages/LoginView.tsx | 4 +---
client/routes.jsx | 10 +++++-----
2 files changed, 6 insertions(+), 8 deletions(-)
diff --git a/client/modules/User/pages/LoginView.tsx b/client/modules/User/pages/LoginView.tsx
index b931c50ea8..70acbf8b31 100644
--- a/client/modules/User/pages/LoginView.tsx
+++ b/client/modules/User/pages/LoginView.tsx
@@ -7,7 +7,7 @@ import SocialAuthButton from '../components/SocialAuthButton';
import Nav from '../../IDE/components/Header/Nav';
import { RootPage } from '../../../components/RootPage';
-function LoginView() {
+export function LoginView() {
const { t } = useTranslation();
return (
@@ -42,5 +42,3 @@ function LoginView() {
);
}
-
-export default LoginView;
diff --git a/client/routes.jsx b/client/routes.jsx
index d8b56f80f1..59f5fda169 100644
--- a/client/routes.jsx
+++ b/client/routes.jsx
@@ -6,11 +6,11 @@ import { Route as RouterRoute, Switch } from 'react-router-dom';
import App from './modules/App/App';
import IDEView from './modules/IDE/pages/IDEView';
import FullView from './modules/IDE/pages/FullView';
-import About from './modules/About/pages/About';
-import CodeOfConduct from './modules/Legal/pages/CodeOfConduct';
-import PrivacyPolicy from './modules/Legal/pages/PrivacyPolicy';
-import TermsOfUse from './modules/Legal/pages/TermsOfUse';
-import LoginView from './modules/User/pages/LoginView';
+import { About } from './modules/About/pages/About';
+import { CodeOfConduct } from './modules/Legal/pages/CodeOfConduct';
+import { PrivacyPolicy } from './modules/Legal/pages/PrivacyPolicy';
+import { TermsOfUse } from './modules/Legal/pages/TermsOfUse';
+import { LoginView } from './modules/User/pages/LoginView';
import SignupView from './modules/User/pages/SignupView';
import ResetPasswordView from './modules/User/pages/ResetPasswordView';
import { EmailVerificationView } from './modules/User/pages/EmailVerificationView';
From a2a5a00492e1560b533a4bfdbdce56c116f7a2c7 Mon Sep 17 00:00:00 2001
From: Claire Peng
Date: Sun, 26 Oct 2025 22:57:46 +0000
Subject: [PATCH 08/52] client/modules/User/components/SocialAuthButton: update
to ts, no-verify
---
.../components/{SocialAuthButton.jsx => SocialAuthButton.tsx} | 0
1 file changed, 0 insertions(+), 0 deletions(-)
rename client/modules/User/components/{SocialAuthButton.jsx => SocialAuthButton.tsx} (100%)
diff --git a/client/modules/User/components/SocialAuthButton.jsx b/client/modules/User/components/SocialAuthButton.tsx
similarity index 100%
rename from client/modules/User/components/SocialAuthButton.jsx
rename to client/modules/User/components/SocialAuthButton.tsx
From 10490e2b08b6ebdd07faf3ec07243b6b2dc8733b Mon Sep 17 00:00:00 2001
From: Claire Peng
Date: Sun, 26 Oct 2025 22:59:04 +0000
Subject: [PATCH 09/52] client/modules/User/components/SocialAuthButton: update
to named export, no-verify
---
client/modules/User/components/SocialAuthButton.stories.jsx | 2 +-
client/modules/User/components/SocialAuthButton.tsx | 4 +---
client/modules/User/pages/AccountView.jsx | 2 +-
client/modules/User/pages/LoginView.tsx | 2 +-
client/modules/User/pages/SignupView.jsx | 2 +-
5 files changed, 5 insertions(+), 7 deletions(-)
diff --git a/client/modules/User/components/SocialAuthButton.stories.jsx b/client/modules/User/components/SocialAuthButton.stories.jsx
index 770dd70162..a629dccc59 100644
--- a/client/modules/User/components/SocialAuthButton.stories.jsx
+++ b/client/modules/User/components/SocialAuthButton.stories.jsx
@@ -1,6 +1,6 @@
import React from 'react';
-import SocialAuthButton from './SocialAuthButton';
+import { SocialAuthButton } from './SocialAuthButton';
export default {
title: 'User/components/SocialAuthButton',
diff --git a/client/modules/User/components/SocialAuthButton.tsx b/client/modules/User/components/SocialAuthButton.tsx
index e48c660b67..6319413d43 100644
--- a/client/modules/User/components/SocialAuthButton.tsx
+++ b/client/modules/User/components/SocialAuthButton.tsx
@@ -34,7 +34,7 @@ const StyledButton = styled(Button)`
width: ${remSize(300)};
`;
-function SocialAuthButton({ service, linkStyle, isConnected }) {
+export function SocialAuthButton({ service, linkStyle, isConnected }) {
const { t } = useTranslation();
const ServiceIcon = icons[service];
@@ -94,5 +94,3 @@ SocialAuthButton.defaultProps = {
linkStyle: false,
isConnected: false
};
-
-export default SocialAuthButton;
diff --git a/client/modules/User/pages/AccountView.jsx b/client/modules/User/pages/AccountView.jsx
index e9b3da7c9a..5bbb1ecc4a 100644
--- a/client/modules/User/pages/AccountView.jsx
+++ b/client/modules/User/pages/AccountView.jsx
@@ -6,7 +6,7 @@ import { useTranslation } from 'react-i18next';
import { useHistory, useLocation } from 'react-router-dom';
import { parse } from 'query-string';
import AccountForm from '../components/AccountForm';
-import SocialAuthButton from '../components/SocialAuthButton';
+import { SocialAuthButton } from '../components/SocialAuthButton';
import APIKeyForm from '../components/APIKeyForm';
import Nav from '../../IDE/components/Header/Nav';
import ErrorModal from '../../IDE/components/ErrorModal';
diff --git a/client/modules/User/pages/LoginView.tsx b/client/modules/User/pages/LoginView.tsx
index 70acbf8b31..8b4776470f 100644
--- a/client/modules/User/pages/LoginView.tsx
+++ b/client/modules/User/pages/LoginView.tsx
@@ -3,7 +3,7 @@ import { Link } from 'react-router-dom';
import { Helmet } from 'react-helmet';
import { useTranslation } from 'react-i18next';
import LoginForm from '../components/LoginForm';
-import SocialAuthButton from '../components/SocialAuthButton';
+import { SocialAuthButton } from '../components/SocialAuthButton';
import Nav from '../../IDE/components/Header/Nav';
import { RootPage } from '../../../components/RootPage';
diff --git a/client/modules/User/pages/SignupView.jsx b/client/modules/User/pages/SignupView.jsx
index 16e038c6e0..ecd11503ef 100644
--- a/client/modules/User/pages/SignupView.jsx
+++ b/client/modules/User/pages/SignupView.jsx
@@ -3,7 +3,7 @@ import { Link } from 'react-router-dom';
import { Helmet } from 'react-helmet';
import { useTranslation, Trans } from 'react-i18next';
import SignupForm from '../components/SignupForm';
-import SocialAuthButton from '../components/SocialAuthButton';
+import { SocialAuthButton } from '../components/SocialAuthButton';
import Nav from '../../IDE/components/Header/Nav';
import { RootPage } from '../../../components/RootPage';
From 01d99c7c7cb4b7ee3cc896c26ce0586f38c114e8 Mon Sep 17 00:00:00 2001
From: Claire Peng
Date: Sun, 26 Oct 2025 23:06:09 +0000
Subject: [PATCH 10/52] client/modules/User/components/SocialAuthButton: add
types & add SocialAuthServices enum
---
.../components/SocialAuthButton.stories.jsx | 6 ++--
.../User/components/SocialAuthButton.tsx | 32 ++++++++-----------
client/modules/User/pages/AccountView.jsx | 9 ++++--
client/modules/User/pages/LoginView.tsx | 9 ++++--
client/modules/User/pages/SignupView.jsx | 9 ++++--
5 files changed, 35 insertions(+), 30 deletions(-)
diff --git a/client/modules/User/components/SocialAuthButton.stories.jsx b/client/modules/User/components/SocialAuthButton.stories.jsx
index a629dccc59..5bd25dc16a 100644
--- a/client/modules/User/components/SocialAuthButton.stories.jsx
+++ b/client/modules/User/components/SocialAuthButton.stories.jsx
@@ -1,6 +1,6 @@
import React from 'react';
-import { SocialAuthButton } from './SocialAuthButton';
+import { SocialAuthButton, SocialAuthServices } from './SocialAuthButton';
export default {
title: 'User/components/SocialAuthButton',
@@ -8,13 +8,13 @@ export default {
};
export const Github = () => (
-
+
Log in with Github
);
export const Google = () => (
-
+
Sign up with Google
);
diff --git a/client/modules/User/components/SocialAuthButton.tsx b/client/modules/User/components/SocialAuthButton.tsx
index 6319413d43..56ddc9ae5d 100644
--- a/client/modules/User/components/SocialAuthButton.tsx
+++ b/client/modules/User/components/SocialAuthButton.tsx
@@ -20,10 +20,10 @@ const icons = {
google: GoogleIcon
};
-const services = {
- github: 'github',
- google: 'google'
-};
+export enum SocialAuthServices {
+ github = 'github',
+ google = 'google'
+}
const servicesLabels = {
github: 'GitHub',
@@ -34,7 +34,16 @@ const StyledButton = styled(Button)`
width: ${remSize(300)};
`;
-export function SocialAuthButton({ service, linkStyle, isConnected }) {
+export interface SocialAuthButtonProps {
+ service: SocialAuthServices;
+ linkStyle?: boolean;
+ isConnected?: boolean;
+}
+export function SocialAuthButton({
+ service,
+ linkStyle = false,
+ isConnected = false
+}: SocialAuthButtonProps) {
const { t } = useTranslation();
const ServiceIcon = icons[service];
@@ -81,16 +90,3 @@ export function SocialAuthButton({ service, linkStyle, isConnected }) {
);
}
-
-SocialAuthButton.services = services;
-
-SocialAuthButton.propTypes = {
- service: PropTypes.oneOf(['github', 'google']).isRequired,
- linkStyle: PropTypes.bool,
- isConnected: PropTypes.bool
-};
-
-SocialAuthButton.defaultProps = {
- linkStyle: false,
- isConnected: false
-};
diff --git a/client/modules/User/pages/AccountView.jsx b/client/modules/User/pages/AccountView.jsx
index 5bbb1ecc4a..508b65816d 100644
--- a/client/modules/User/pages/AccountView.jsx
+++ b/client/modules/User/pages/AccountView.jsx
@@ -6,7 +6,10 @@ import { useTranslation } from 'react-i18next';
import { useHistory, useLocation } from 'react-router-dom';
import { parse } from 'query-string';
import AccountForm from '../components/AccountForm';
-import { SocialAuthButton } from '../components/SocialAuthButton';
+import {
+ SocialAuthButton,
+ SocialAuthServices
+} from '../components/SocialAuthButton';
import APIKeyForm from '../components/APIKeyForm';
import Nav from '../../IDE/components/Header/Nav';
import ErrorModal from '../../IDE/components/ErrorModal';
@@ -28,12 +31,12 @@ function SocialLoginPanel() {
diff --git a/client/modules/User/pages/LoginView.tsx b/client/modules/User/pages/LoginView.tsx
index 8b4776470f..5df7b53951 100644
--- a/client/modules/User/pages/LoginView.tsx
+++ b/client/modules/User/pages/LoginView.tsx
@@ -3,7 +3,10 @@ import { Link } from 'react-router-dom';
import { Helmet } from 'react-helmet';
import { useTranslation } from 'react-i18next';
import LoginForm from '../components/LoginForm';
-import { SocialAuthButton } from '../components/SocialAuthButton';
+import {
+ SocialAuthButton,
+ SocialAuthServices
+} from '../components/SocialAuthButton';
import Nav from '../../IDE/components/Header/Nav';
import { RootPage } from '../../../components/RootPage';
@@ -21,8 +24,8 @@ export function LoginView() {
{t('LoginView.LoginOr')}
-
-
+
+
{t('LoginView.DontHaveAccount')}
diff --git a/client/modules/User/pages/SignupView.jsx b/client/modules/User/pages/SignupView.jsx
index ecd11503ef..3248a7bc61 100644
--- a/client/modules/User/pages/SignupView.jsx
+++ b/client/modules/User/pages/SignupView.jsx
@@ -3,7 +3,10 @@ import { Link } from 'react-router-dom';
import { Helmet } from 'react-helmet';
import { useTranslation, Trans } from 'react-i18next';
import SignupForm from '../components/SignupForm';
-import { SocialAuthButton } from '../components/SocialAuthButton';
+import {
+ SocialAuthButton,
+ SocialAuthServices
+} from '../components/SocialAuthButton';
import Nav from '../../IDE/components/Header/Nav';
import { RootPage } from '../../../components/RootPage';
@@ -23,8 +26,8 @@ function SignupView() {
{t('SignupView.Or')}
-
-
+
+
Date: Sun, 26 Oct 2025 23:09:30 +0000
Subject: [PATCH 11/52] client/modules/User/components/LoginForm: update to ts,
no-verfy
---
client/modules/User/components/{LoginForm.jsx => LoginForm.tsx} | 0
1 file changed, 0 insertions(+), 0 deletions(-)
rename client/modules/User/components/{LoginForm.jsx => LoginForm.tsx} (100%)
diff --git a/client/modules/User/components/LoginForm.jsx b/client/modules/User/components/LoginForm.tsx
similarity index 100%
rename from client/modules/User/components/LoginForm.jsx
rename to client/modules/User/components/LoginForm.tsx
From 62cee39f61cbb0d8f59d545393b0d24be8e098e6 Mon Sep 17 00:00:00 2001
From: Claire Peng
Date: Sun, 26 Oct 2025 23:10:00 +0000
Subject: [PATCH 12/52] client/modules/User/components/LoginForm: update to
named export, no-verfy
---
client/modules/User/components/LoginForm.tsx | 4 +---
client/modules/User/components/LoginForm.unit.test.jsx | 2 +-
client/modules/User/pages/LoginView.tsx | 2 +-
3 files changed, 3 insertions(+), 5 deletions(-)
diff --git a/client/modules/User/components/LoginForm.tsx b/client/modules/User/components/LoginForm.tsx
index 5bac902ecf..998c1090b4 100644
--- a/client/modules/User/components/LoginForm.tsx
+++ b/client/modules/User/components/LoginForm.tsx
@@ -8,7 +8,7 @@ import { validateLogin } from '../../../utils/reduxFormUtils';
import { validateAndLoginUser } from '../actions';
import { useSyncFormTranslations } from '../../../common/useSyncFormTranslations';
-function LoginForm() {
+export function LoginForm() {
const { t, i18n } = useTranslation();
const dispatch = useDispatch();
@@ -114,5 +114,3 @@ function LoginForm() {
);
}
-
-export default LoginForm;
diff --git a/client/modules/User/components/LoginForm.unit.test.jsx b/client/modules/User/components/LoginForm.unit.test.jsx
index 2f4262c16b..1b6b982733 100644
--- a/client/modules/User/components/LoginForm.unit.test.jsx
+++ b/client/modules/User/components/LoginForm.unit.test.jsx
@@ -1,7 +1,7 @@
import React from 'react';
import thunk from 'redux-thunk';
import configureStore from 'redux-mock-store';
-import LoginForm from './LoginForm';
+import { LoginForm } from './LoginForm';
import * as actions from '../actions';
import { initialTestState } from '../../../testData/testReduxStore';
import { reduxRender, screen, fireEvent, act } from '../../../test-utils';
diff --git a/client/modules/User/pages/LoginView.tsx b/client/modules/User/pages/LoginView.tsx
index 5df7b53951..4dff34b650 100644
--- a/client/modules/User/pages/LoginView.tsx
+++ b/client/modules/User/pages/LoginView.tsx
@@ -2,7 +2,7 @@ import React from 'react';
import { Link } from 'react-router-dom';
import { Helmet } from 'react-helmet';
import { useTranslation } from 'react-i18next';
-import LoginForm from '../components/LoginForm';
+import { LoginForm } from '../components/LoginForm';
import {
SocialAuthButton,
SocialAuthServices
From e8750ed4def9f9925a09ea2e471ba475fe745713 Mon Sep 17 00:00:00 2001
From: Claire Peng
Date: Sun, 26 Oct 2025 23:14:02 +0000
Subject: [PATCH 13/52] client/modules/User/components/LoginForm: update with
types & update useSyncFormTranslations to handle null formRef
---
client/common/useSyncFormTranslations.ts | 4 ++--
client/modules/User/components/LoginForm.tsx | 10 +++++++---
2 files changed, 9 insertions(+), 5 deletions(-)
diff --git a/client/common/useSyncFormTranslations.ts b/client/common/useSyncFormTranslations.ts
index 4a90362750..aed766aa43 100644
--- a/client/common/useSyncFormTranslations.ts
+++ b/client/common/useSyncFormTranslations.ts
@@ -12,11 +12,11 @@ export interface FormLike {
* @param language
*/
export const useSyncFormTranslations = (
- formRef: MutableRefObject,
+ formRef: MutableRefObject,
language: string
) => {
useEffect(() => {
- const form = formRef.current;
+ const form = formRef?.current;
if (!form) return;
const { values } = form.getState();
diff --git a/client/modules/User/components/LoginForm.tsx b/client/modules/User/components/LoginForm.tsx
index 998c1090b4..17bdaf9393 100644
--- a/client/modules/User/components/LoginForm.tsx
+++ b/client/modules/User/components/LoginForm.tsx
@@ -6,17 +6,21 @@ import { AiOutlineEye, AiOutlineEyeInvisible } from 'react-icons/ai';
import { Button, ButtonTypes } from '../../../common/Button';
import { validateLogin } from '../../../utils/reduxFormUtils';
import { validateAndLoginUser } from '../actions';
-import { useSyncFormTranslations } from '../../../common/useSyncFormTranslations';
+import {
+ FormLike,
+ useSyncFormTranslations
+} from '../../../common/useSyncFormTranslations';
+import type { LoginForm as LoginFormType } from '../../../utils/reduxFormUtils';
export function LoginForm() {
const { t, i18n } = useTranslation();
const dispatch = useDispatch();
- function onSubmit(formProps) {
+ function onSubmit(formProps: LoginFormType) {
return dispatch(validateAndLoginUser(formProps));
}
const [showPassword, setShowPassword] = useState(false);
- const formRef = useRef(null);
+ const formRef = useRef(null);
const handleVisibility = () => {
setShowPassword(!showPassword);
From 0fcc6616ce7e2377b404891214a7b022d4d46104 Mon Sep 17 00:00:00 2001
From: Claire Peng
Date: Sun, 26 Oct 2025 23:22:02 +0000
Subject: [PATCH 14/52] client/modules/User/components/Notification: delete
unused file
---
.../modules/User/components/Notification.jsx | 22 -------------------
1 file changed, 22 deletions(-)
delete mode 100644 client/modules/User/components/Notification.jsx
diff --git a/client/modules/User/components/Notification.jsx b/client/modules/User/components/Notification.jsx
deleted file mode 100644
index c7189c3af1..0000000000
--- a/client/modules/User/components/Notification.jsx
+++ /dev/null
@@ -1,22 +0,0 @@
-import { useEffect } from 'react';
-import Cookies from 'js-cookie';
-import { useDispatch } from 'react-redux';
-import { showToast, setToastText } from '../../IDE/actions/toast';
-
-function Notification() {
- const dispatch = useDispatch();
- useEffect(() => {
- const notification = Cookies.get('p5-notification');
- if (!notification) {
- // show the toast
- dispatch(showToast(30000));
- const text = `There is a scheduled outage on Sunday, April 9 3AM - 5AM UTC.
- The entire site will be down, so please plan accordingly.`;
- dispatch(setToastText(text));
- Cookies.set('p5-notification', true, { expires: 365 });
- }
- });
- return null;
-}
-
-export default Notification;
From af68d0b0802e29fcf4412669f61fe7639ddc9707 Mon Sep 17 00:00:00 2001
From: Claire Peng
Date: Sun, 26 Oct 2025 23:24:48 +0000
Subject: [PATCH 15/52] client/modules/User/pages/NewPasswordView: update to
ts, no-verify
---
.../User/pages/{NewPasswordView.jsx => NewPasswordView.tsx} | 0
1 file changed, 0 insertions(+), 0 deletions(-)
rename client/modules/User/pages/{NewPasswordView.jsx => NewPasswordView.tsx} (100%)
diff --git a/client/modules/User/pages/NewPasswordView.jsx b/client/modules/User/pages/NewPasswordView.tsx
similarity index 100%
rename from client/modules/User/pages/NewPasswordView.jsx
rename to client/modules/User/pages/NewPasswordView.tsx
From 77586121d31807c10f9b155d12a927e08c8ba26c Mon Sep 17 00:00:00 2001
From: Claire Peng
Date: Sun, 26 Oct 2025 23:25:08 +0000
Subject: [PATCH 16/52] client/modules/User/pages/NewPasswordView: add types
and update to named export
---
client/modules/User/pages/NewPasswordView.tsx | 10 +++++-----
client/routes.jsx | 2 +-
2 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/client/modules/User/pages/NewPasswordView.tsx b/client/modules/User/pages/NewPasswordView.tsx
index 2bc310e6a3..e074f9a7a5 100644
--- a/client/modules/User/pages/NewPasswordView.tsx
+++ b/client/modules/User/pages/NewPasswordView.tsx
@@ -8,13 +8,15 @@ import NewPasswordForm from '../components/NewPasswordForm';
import { validateResetPasswordToken } from '../actions';
import Nav from '../../IDE/components/Header/Nav';
import { RootPage } from '../../../components/RootPage';
+import { RootState } from '../../../reducers';
-function NewPasswordView() {
+export function NewPasswordView() {
const { t } = useTranslation();
- const params = useParams();
+ // eslint-disable-next-line camelcase
+ const params = useParams<{ reset_password_token: string }>();
const resetPasswordToken = params.reset_password_token;
const resetPasswordInvalid = useSelector(
- (state) => state.user.resetPasswordInvalid
+ (state: RootState) => state.user.resetPasswordInvalid
);
const dispatch = useDispatch();
@@ -48,5 +50,3 @@ function NewPasswordView() {
);
}
-
-export default NewPasswordView;
diff --git a/client/routes.jsx b/client/routes.jsx
index 59f5fda169..7cb77811ab 100644
--- a/client/routes.jsx
+++ b/client/routes.jsx
@@ -14,7 +14,7 @@ import { LoginView } from './modules/User/pages/LoginView';
import SignupView from './modules/User/pages/SignupView';
import ResetPasswordView from './modules/User/pages/ResetPasswordView';
import { EmailVerificationView } from './modules/User/pages/EmailVerificationView';
-import NewPasswordView from './modules/User/pages/NewPasswordView';
+import { NewPasswordView } from './modules/User/pages/NewPasswordView';
import AccountView from './modules/User/pages/AccountView';
import { CollectionView } from './modules/User/pages/CollectionView';
import DashboardView from './modules/User/pages/DashboardView';
From edbf29e2b0e750cdfebdc167799210695c092fb0 Mon Sep 17 00:00:00 2001
From: Claire Peng
Date: Sun, 26 Oct 2025 23:26:40 +0000
Subject: [PATCH 17/52] client/modules/User/components/ResponseiveForm: delete
unused file
---
.../User/components/ResponsiveForm.jsx | 54 -------------------
1 file changed, 54 deletions(-)
delete mode 100644 client/modules/User/components/ResponsiveForm.jsx
diff --git a/client/modules/User/components/ResponsiveForm.jsx b/client/modules/User/components/ResponsiveForm.jsx
deleted file mode 100644
index 829b83cd14..0000000000
--- a/client/modules/User/components/ResponsiveForm.jsx
+++ /dev/null
@@ -1,54 +0,0 @@
-import styled from 'styled-components';
-import { remSize } from '../../../theme';
-
-const ResponsiveForm = styled.div`
- .form-container__content {
- width: unset !important;
- padding-top: ${remSize(16)};
- padding-bottom: ${remSize(64)};
- }
-
- .form__input {
- min-width: unset;
- padding: 0px ${remSize(12)};
- height: ${remSize(28)};
- }
- .form-container__title {
- margin-bottom: ${remSize(14)};
- }
- p.form__field {
- margin-top: 0px !important;
- }
- label.form__label {
- margin-top: ${remSize(8)} !important;
- }
-
- .form-error {
- width: 100%;
- }
-
- .nav__items-right:last-child {
- display: none;
- }
-
- .form-container {
- height: 100%;
- }
-
- .nav__dropdown {
- right: 0 !important;
- left: unset !important;
- }
-
- .form-container__stack {
- svg {
- width: ${remSize(12)};
- height: ${remSize(12)};
- }
- a {
- padding: 0px;
- }
- }
-`;
-
-export default ResponsiveForm;
From 70d088fae2cf41bc932e3acc20d25206fe780b74 Mon Sep 17 00:00:00 2001
From: Claire Peng
Date: Mon, 27 Oct 2025 03:16:55 +0000
Subject: [PATCH 18/52] package.json: add react-helmet types
---
package-lock.json | 20 ++++++++++++++++++++
package.json | 2 +-
2 files changed, 21 insertions(+), 1 deletion(-)
diff --git a/package-lock.json b/package-lock.json
index 9edffa05e9..f948e6f8dd 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -171,6 +171,7 @@
"@types/passport": "^1.0.17",
"@types/react": "^16.14.0",
"@types/react-dom": "^16.9.25",
+ "@types/react-helmet": "^6.1.11",
"@types/react-router-dom": "^5.3.3",
"@types/sinon": "^17.0.4",
"@types/styled-components": "^5.1.34",
@@ -16646,6 +16647,16 @@
"@types/react": "^16.0.0"
}
},
+ "node_modules/@types/react-helmet": {
+ "version": "6.1.11",
+ "resolved": "https://registry.npmjs.org/@types/react-helmet/-/react-helmet-6.1.11.tgz",
+ "integrity": "sha512-0QcdGLddTERotCXo3VFlUSWO3ztraw8nZ6e3zJSgG7apwV5xt+pJUS8ewPBqT4NYB1optGLprNQzFleIY84u/g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/react": "*"
+ }
+ },
"node_modules/@types/react-redux": {
"version": "7.1.18",
"resolved": "https://registry.npmjs.org/@types/react-redux/-/react-redux-7.1.18.tgz",
@@ -53346,6 +53357,15 @@
"dev": true,
"requires": {}
},
+ "@types/react-helmet": {
+ "version": "6.1.11",
+ "resolved": "https://registry.npmjs.org/@types/react-helmet/-/react-helmet-6.1.11.tgz",
+ "integrity": "sha512-0QcdGLddTERotCXo3VFlUSWO3ztraw8nZ6e3zJSgG7apwV5xt+pJUS8ewPBqT4NYB1optGLprNQzFleIY84u/g==",
+ "dev": true,
+ "requires": {
+ "@types/react": "*"
+ }
+ },
"@types/react-redux": {
"version": "7.1.18",
"resolved": "https://registry.npmjs.org/@types/react-redux/-/react-redux-7.1.18.tgz",
diff --git a/package.json b/package.json
index 643d468aa3..df7a8dc603 100644
--- a/package.json
+++ b/package.json
@@ -144,9 +144,9 @@
"@types/nodemailer": "^7.0.1",
"@types/nodemailer-mailgun-transport": "^1.4.6",
"@types/passport": "^1.0.17",
- "@types/passport": "^1.0.17",
"@types/react": "^16.14.0",
"@types/react-dom": "^16.9.25",
+ "@types/react-helmet": "^6.1.11",
"@types/react-router-dom": "^5.3.3",
"@types/sinon": "^17.0.4",
"@types/styled-components": "^5.1.34",
From 851681a8d3ba7c8c4b324d4b783a0d4afb779cc8 Mon Sep 17 00:00:00 2001
From: Claire Peng
Date: Sun, 26 Oct 2025 23:29:11 +0000
Subject: [PATCH 19/52] client/modules/User/pages/ResetPasswordView: update to
ts, no-verify
---
.../User/pages/{ResetPasswordView.jsx => ResetPasswordView.tsx} | 0
1 file changed, 0 insertions(+), 0 deletions(-)
rename client/modules/User/pages/{ResetPasswordView.jsx => ResetPasswordView.tsx} (100%)
diff --git a/client/modules/User/pages/ResetPasswordView.jsx b/client/modules/User/pages/ResetPasswordView.tsx
similarity index 100%
rename from client/modules/User/pages/ResetPasswordView.jsx
rename to client/modules/User/pages/ResetPasswordView.tsx
From a647f19a8ad0d67f61d273ba659e918508d16437 Mon Sep 17 00:00:00 2001
From: Claire Peng
Date: Sun, 26 Oct 2025 23:29:38 +0000
Subject: [PATCH 20/52] client/modules/User/pages/ResetPasswordView: add types
& update to named export
---
client/modules/User/pages/ResetPasswordView.tsx | 7 +++----
client/routes.jsx | 2 +-
2 files changed, 4 insertions(+), 5 deletions(-)
diff --git a/client/modules/User/pages/ResetPasswordView.tsx b/client/modules/User/pages/ResetPasswordView.tsx
index e744311b7c..fe7a1eeec4 100644
--- a/client/modules/User/pages/ResetPasswordView.tsx
+++ b/client/modules/User/pages/ResetPasswordView.tsx
@@ -7,11 +7,12 @@ import { useTranslation } from 'react-i18next';
import ResetPasswordForm from '../components/ResetPasswordForm';
import { RootPage } from '../../../components/RootPage';
import Nav from '../../IDE/components/Header/Nav';
+import { RootState } from '../../../reducers';
-function ResetPasswordView() {
+export function ResetPasswordView() {
const { t } = useTranslation();
const resetPasswordInitiate = useSelector(
- (state) => state.user.resetPasswordInitiate
+ (state: RootState) => state.user.resetPasswordInitiate
);
const resetPasswordClass = classNames({
'reset-password': true,
@@ -48,5 +49,3 @@ function ResetPasswordView() {
);
}
-
-export default ResetPasswordView;
diff --git a/client/routes.jsx b/client/routes.jsx
index 7cb77811ab..e07cec91f7 100644
--- a/client/routes.jsx
+++ b/client/routes.jsx
@@ -12,7 +12,7 @@ import { PrivacyPolicy } from './modules/Legal/pages/PrivacyPolicy';
import { TermsOfUse } from './modules/Legal/pages/TermsOfUse';
import { LoginView } from './modules/User/pages/LoginView';
import SignupView from './modules/User/pages/SignupView';
-import ResetPasswordView from './modules/User/pages/ResetPasswordView';
+import { ResetPasswordView } from './modules/User/pages/ResetPasswordView';
import { EmailVerificationView } from './modules/User/pages/EmailVerificationView';
import { NewPasswordView } from './modules/User/pages/NewPasswordView';
import AccountView from './modules/User/pages/AccountView';
From 9b49e6889922223aa50134dc8bde721e3b2f0962 Mon Sep 17 00:00:00 2001
From: Claire Peng
Date: Sun, 26 Oct 2025 23:34:22 +0000
Subject: [PATCH 21/52] client/modules/User/components/NewPasswordForm: update
to ts, no-verify
---
.../User/components/{NewPasswordForm.jsx => NewPasswordForm.tsx} | 0
1 file changed, 0 insertions(+), 0 deletions(-)
rename client/modules/User/components/{NewPasswordForm.jsx => NewPasswordForm.tsx} (100%)
diff --git a/client/modules/User/components/NewPasswordForm.jsx b/client/modules/User/components/NewPasswordForm.tsx
similarity index 100%
rename from client/modules/User/components/NewPasswordForm.jsx
rename to client/modules/User/components/NewPasswordForm.tsx
From 8d0fe3ecb0b31083515a6eaf15ffe22dd170ab36 Mon Sep 17 00:00:00 2001
From: Claire Peng
Date: Sun, 26 Oct 2025 23:35:50 +0000
Subject: [PATCH 22/52] client/modules/User/components/NewPasswordForm: add
types & update to named export
---
client/modules/User/components/NewPasswordForm.tsx | 12 +++---------
.../User/components/NewPasswordForm.unit.test.jsx | 2 +-
client/modules/User/pages/NewPasswordView.tsx | 2 +-
3 files changed, 5 insertions(+), 11 deletions(-)
diff --git a/client/modules/User/components/NewPasswordForm.tsx b/client/modules/User/components/NewPasswordForm.tsx
index feca326c77..9d08f678c5 100644
--- a/client/modules/User/components/NewPasswordForm.tsx
+++ b/client/modules/User/components/NewPasswordForm.tsx
@@ -1,4 +1,3 @@
-import PropTypes from 'prop-types';
import React from 'react';
import { Form, Field } from 'react-final-form';
import { useDispatch } from 'react-redux';
@@ -6,13 +5,14 @@ import { useTranslation } from 'react-i18next';
import { validateNewPassword } from '../../../utils/reduxFormUtils';
import { updatePassword } from '../actions';
import { Button, ButtonTypes } from '../../../common/Button';
+import type { NewPasswordForm as NewPasswordFormType } from '../../../utils/reduxFormUtils';
-function NewPasswordForm(props) {
+export function NewPasswordForm(props: { resetPasswordToken: string }) {
const { resetPasswordToken } = props;
const { t } = useTranslation();
const dispatch = useDispatch();
- function onSubmit(formProps) {
+ function onSubmit(formProps: NewPasswordFormType) {
return dispatch(updatePassword(formProps, resetPasswordToken));
}
@@ -75,9 +75,3 @@ function NewPasswordForm(props) {
);
}
-
-NewPasswordForm.propTypes = {
- resetPasswordToken: PropTypes.string.isRequired
-};
-
-export default NewPasswordForm;
diff --git a/client/modules/User/components/NewPasswordForm.unit.test.jsx b/client/modules/User/components/NewPasswordForm.unit.test.jsx
index dbddf3c8cb..a55af924ed 100644
--- a/client/modules/User/components/NewPasswordForm.unit.test.jsx
+++ b/client/modules/User/components/NewPasswordForm.unit.test.jsx
@@ -4,7 +4,7 @@ import configureStore from 'redux-mock-store';
import { fireEvent } from '@storybook/testing-library';
import { reduxRender, screen, act, waitFor } from '../../../test-utils';
import { initialTestState } from '../../../testData/testReduxStore';
-import NewPasswordForm from './NewPasswordForm';
+import { NewPasswordForm } from './NewPasswordForm';
const mockStore = configureStore([thunk]);
const store = mockStore(initialTestState);
diff --git a/client/modules/User/pages/NewPasswordView.tsx b/client/modules/User/pages/NewPasswordView.tsx
index e074f9a7a5..1433624df0 100644
--- a/client/modules/User/pages/NewPasswordView.tsx
+++ b/client/modules/User/pages/NewPasswordView.tsx
@@ -4,7 +4,7 @@ import { useDispatch, useSelector } from 'react-redux';
import { Helmet } from 'react-helmet';
import { useTranslation } from 'react-i18next';
import { useParams } from 'react-router-dom';
-import NewPasswordForm from '../components/NewPasswordForm';
+import { NewPasswordForm } from '../components/NewPasswordForm';
import { validateResetPasswordToken } from '../actions';
import Nav from '../../IDE/components/Header/Nav';
import { RootPage } from '../../../components/RootPage';
From 8226165b68ffed6fe087999a5acca01f291546b3 Mon Sep 17 00:00:00 2001
From: Claire Peng
Date: Sun, 26 Oct 2025 23:37:25 +0000
Subject: [PATCH 23/52] client/modules/User/pages/SignupView: update to ts,
no-verify
---
client/modules/User/pages/{SignupView.jsx => SignupView.tsx} | 0
1 file changed, 0 insertions(+), 0 deletions(-)
rename client/modules/User/pages/{SignupView.jsx => SignupView.tsx} (100%)
diff --git a/client/modules/User/pages/SignupView.jsx b/client/modules/User/pages/SignupView.tsx
similarity index 100%
rename from client/modules/User/pages/SignupView.jsx
rename to client/modules/User/pages/SignupView.tsx
From fd922368ce6376f0825882e459de48fe10063f8e Mon Sep 17 00:00:00 2001
From: Claire Peng
Date: Sun, 26 Oct 2025 23:38:13 +0000
Subject: [PATCH 24/52] client/modules/User/pages/SignupView: update to named
export
---
client/modules/User/pages/SignupView.tsx | 4 +---
client/routes.jsx | 2 +-
2 files changed, 2 insertions(+), 4 deletions(-)
diff --git a/client/modules/User/pages/SignupView.tsx b/client/modules/User/pages/SignupView.tsx
index 3248a7bc61..50b54555c4 100644
--- a/client/modules/User/pages/SignupView.tsx
+++ b/client/modules/User/pages/SignupView.tsx
@@ -10,7 +10,7 @@ import {
import Nav from '../../IDE/components/Header/Nav';
import { RootPage } from '../../../components/RootPage';
-function SignupView() {
+export function SignupView() {
const { t } = useTranslation();
return (
@@ -49,5 +49,3 @@ function SignupView() {
);
}
-
-export default SignupView;
diff --git a/client/routes.jsx b/client/routes.jsx
index e07cec91f7..4e399561ff 100644
--- a/client/routes.jsx
+++ b/client/routes.jsx
@@ -11,7 +11,7 @@ import { CodeOfConduct } from './modules/Legal/pages/CodeOfConduct';
import { PrivacyPolicy } from './modules/Legal/pages/PrivacyPolicy';
import { TermsOfUse } from './modules/Legal/pages/TermsOfUse';
import { LoginView } from './modules/User/pages/LoginView';
-import SignupView from './modules/User/pages/SignupView';
+import { SignupView } from './modules/User/pages/SignupView';
import { ResetPasswordView } from './modules/User/pages/ResetPasswordView';
import { EmailVerificationView } from './modules/User/pages/EmailVerificationView';
import { NewPasswordView } from './modules/User/pages/NewPasswordView';
From ad990c0c21beb793bf121845946ac75968b5a22a Mon Sep 17 00:00:00 2001
From: Claire Peng
Date: Sun, 26 Oct 2025 23:39:50 +0000
Subject: [PATCH 25/52] client/modules/User/components/ResetPasswordForm:
update to ts, no-verify
---
.../components/{ResetPasswordForm.jsx => ResetPasswordForm.tsx} | 0
1 file changed, 0 insertions(+), 0 deletions(-)
rename client/modules/User/components/{ResetPasswordForm.jsx => ResetPasswordForm.tsx} (100%)
diff --git a/client/modules/User/components/ResetPasswordForm.jsx b/client/modules/User/components/ResetPasswordForm.tsx
similarity index 100%
rename from client/modules/User/components/ResetPasswordForm.jsx
rename to client/modules/User/components/ResetPasswordForm.tsx
From c58e78be2a2c3afa59cd7496af852f43f93e5ebe Mon Sep 17 00:00:00 2001
From: Claire Peng
Date: Sun, 26 Oct 2025 23:41:42 +0000
Subject: [PATCH 26/52] client/modules/User/components/ResetPasswordForm:
update named export & add types
---
client/modules/User/components/ResetPasswordForm.tsx | 10 +++++-----
client/modules/User/pages/ResetPasswordView.tsx | 2 +-
2 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/client/modules/User/components/ResetPasswordForm.tsx b/client/modules/User/components/ResetPasswordForm.tsx
index fe3752fdf4..f3668b9636 100644
--- a/client/modules/User/components/ResetPasswordForm.tsx
+++ b/client/modules/User/components/ResetPasswordForm.tsx
@@ -5,15 +5,17 @@ import { useDispatch, useSelector } from 'react-redux';
import { validateResetPassword } from '../../../utils/reduxFormUtils';
import { initiateResetPassword } from '../actions';
import { Button, ButtonTypes } from '../../../common/Button';
+import { RootState } from '../../../reducers';
+import { ResetPasswordInitiateRequestBody } from '../../../../common/types';
-function ResetPasswordForm(props) {
+export function ResetPasswordForm() {
const { t } = useTranslation();
const resetPasswordInitiate = useSelector(
- (state) => state.user.resetPasswordInitiate
+ (state: RootState) => state.user.resetPasswordInitiate
);
const dispatch = useDispatch();
- function onSubmit(formProps) {
+ function onSubmit(formProps: ResetPasswordInitiateRequestBody) {
dispatch(initiateResetPassword(formProps));
}
@@ -57,5 +59,3 @@ function ResetPasswordForm(props) {
);
}
-
-export default ResetPasswordForm;
diff --git a/client/modules/User/pages/ResetPasswordView.tsx b/client/modules/User/pages/ResetPasswordView.tsx
index fe7a1eeec4..09916047e4 100644
--- a/client/modules/User/pages/ResetPasswordView.tsx
+++ b/client/modules/User/pages/ResetPasswordView.tsx
@@ -4,7 +4,7 @@ import classNames from 'classnames';
import { useSelector } from 'react-redux';
import { Helmet } from 'react-helmet';
import { useTranslation } from 'react-i18next';
-import ResetPasswordForm from '../components/ResetPasswordForm';
+import { ResetPasswordForm } from '../components/ResetPasswordForm';
import { RootPage } from '../../../components/RootPage';
import Nav from '../../IDE/components/Header/Nav';
import { RootState } from '../../../reducers';
From b8e7361f81a1c113c87d01e7dd594a248c6ae7fb Mon Sep 17 00:00:00 2001
From: Claire Peng
Date: Sun, 26 Oct 2025 23:58:48 +0000
Subject: [PATCH 27/52] client/modules/User/components/SignupForm: update to
ts, no-verify
---
client/modules/User/components/{SignupForm.jsx => SignupForm.tsx} | 0
1 file changed, 0 insertions(+), 0 deletions(-)
rename client/modules/User/components/{SignupForm.jsx => SignupForm.tsx} (100%)
diff --git a/client/modules/User/components/SignupForm.jsx b/client/modules/User/components/SignupForm.tsx
similarity index 100%
rename from client/modules/User/components/SignupForm.jsx
rename to client/modules/User/components/SignupForm.tsx
From 023096bc4671e4a57bd4c2d8cef5cd8f4bff0f68 Mon Sep 17 00:00:00 2001
From: Claire Peng
Date: Sun, 26 Oct 2025 23:59:40 +0000
Subject: [PATCH 28/52] client/modules/User/components/SignupForm: add types
and update to named export; update useSyncFormTranslations FormlikeType to
take generic
---
client/common/useSyncFormTranslations.ts | 22 ++++++++-------
client/modules/User/components/LoginForm.tsx | 2 +-
client/modules/User/components/SignupForm.tsx | 27 +++++++++++--------
client/modules/User/pages/SignupView.tsx | 2 +-
4 files changed, 30 insertions(+), 23 deletions(-)
diff --git a/client/common/useSyncFormTranslations.ts b/client/common/useSyncFormTranslations.ts
index aed766aa43..c116db1e04 100644
--- a/client/common/useSyncFormTranslations.ts
+++ b/client/common/useSyncFormTranslations.ts
@@ -1,18 +1,19 @@
import { useEffect, MutableRefObject } from 'react';
+import type { FormApi } from 'final-form';
-export interface FormLike {
- getState(): { values: Record };
- reset(): void;
- change(field: string, value: unknown): void;
-}
+// Generic FormLike that mirrors FormApi for any form value type
+export type FormLike> = Pick<
+ FormApi,
+ 'getState' | 'reset' | 'change'
+>;
/**
* This hook ensures that form values are preserved when the language changes.
* @param formRef
* @param language
*/
-export const useSyncFormTranslations = (
- formRef: MutableRefObject,
+export const useSyncFormTranslations = >(
+ formRef: MutableRefObject | null>,
language: string
) => {
useEffect(() => {
@@ -22,9 +23,10 @@ export const useSyncFormTranslations = (
const { values } = form.getState();
form.reset();
- Object.keys(values).forEach((field) => {
- if (values[field]) {
- form.change(field, values[field]);
+ (Object.keys(values) as (keyof FormValues)[]).forEach((field) => {
+ const value = values[field];
+ if (value !== undefined && value !== null && value !== '') {
+ form.change(field, value);
}
});
}, [language]);
diff --git a/client/modules/User/components/LoginForm.tsx b/client/modules/User/components/LoginForm.tsx
index 17bdaf9393..c3741ffce7 100644
--- a/client/modules/User/components/LoginForm.tsx
+++ b/client/modules/User/components/LoginForm.tsx
@@ -20,7 +20,7 @@ export function LoginForm() {
return dispatch(validateAndLoginUser(formProps));
}
const [showPassword, setShowPassword] = useState(false);
- const formRef = useRef(null);
+ const formRef = useRef | null>(null);
const handleVisibility = () => {
setShowPassword(!showPassword);
diff --git a/client/modules/User/components/SignupForm.tsx b/client/modules/User/components/SignupForm.tsx
index 45d44da875..dbbac68932 100644
--- a/client/modules/User/components/SignupForm.tsx
+++ b/client/modules/User/components/SignupForm.tsx
@@ -7,16 +7,23 @@ import { validateSignup } from '../../../utils/reduxFormUtils';
import { validateAndSignUpUser } from '../actions';
import { Button, ButtonTypes } from '../../../common/Button';
import { apiClient } from '../../../utils/apiClient';
-import { useSyncFormTranslations } from '../../../common/useSyncFormTranslations';
+import {
+ FormLike,
+ useSyncFormTranslations
+} from '../../../common/useSyncFormTranslations';
+import {
+ CreateUserRequestBody,
+ DuplicateUserCheckQuery
+} from '../../../../common/types';
-const timeoutRef = { current: null };
+const timeoutRef: { current: (() => void) | null } = { current: null };
-function asyncValidate(fieldToValidate, value) {
+function asyncValidate(fieldToValidate: 'username' | 'email', value: string) {
if (!value || value.trim().length === 0) {
return Promise.resolve('');
}
- const queryParams = {
+ const queryParams: DuplicateUserCheckQuery = {
[fieldToValidate]: value,
check_type: fieldToValidate
};
@@ -48,18 +55,18 @@ function asyncValidate(fieldToValidate, value) {
});
}
-function validateUsername(username) {
+function validateUsername(username: CreateUserRequestBody['username']) {
return asyncValidate('username', username);
}
-function validateEmail(email) {
+function validateEmail(email: CreateUserRequestBody['email']) {
return asyncValidate('email', email);
}
-function SignupForm() {
+export function SignupForm() {
const { t, i18n } = useTranslation();
const dispatch = useDispatch();
- const formRef = useRef(null);
+ const formRef = useRef | null>(null);
const [showPassword, setShowPassword] = useState(false);
const [showConfirmPassword, setShowConfirmPassword] = useState(false);
@@ -69,7 +76,7 @@ function SignupForm() {
const handleConfirmVisibility = () =>
setShowConfirmPassword(!showConfirmPassword);
- function onSubmit(formProps) {
+ function onSubmit(formProps: CreateUserRequestBody) {
return dispatch(validateAndSignUpUser(formProps));
}
@@ -216,5 +223,3 @@ function SignupForm() {
);
}
-
-export default SignupForm;
diff --git a/client/modules/User/pages/SignupView.tsx b/client/modules/User/pages/SignupView.tsx
index 50b54555c4..36006d51ef 100644
--- a/client/modules/User/pages/SignupView.tsx
+++ b/client/modules/User/pages/SignupView.tsx
@@ -2,7 +2,7 @@ import React from 'react';
import { Link } from 'react-router-dom';
import { Helmet } from 'react-helmet';
import { useTranslation, Trans } from 'react-i18next';
-import SignupForm from '../components/SignupForm';
+import { SignupForm } from '../components/SignupForm';
import {
SocialAuthButton,
SocialAuthServices
From 248e2c201fc6d3c2fc3f4ab4aca150b860858cd7 Mon Sep 17 00:00:00 2001
From: Claire Peng
Date: Mon, 27 Oct 2025 00:14:30 +0000
Subject: [PATCH 29/52] client/modules/User/components/CookieConsent: update to
ts, no-verify
---
.../User/components/{CookieConsent.jsx => CookieConsent.tsx} | 0
1 file changed, 0 insertions(+), 0 deletions(-)
rename client/modules/User/components/{CookieConsent.jsx => CookieConsent.tsx} (100%)
diff --git a/client/modules/User/components/CookieConsent.jsx b/client/modules/User/components/CookieConsent.tsx
similarity index 100%
rename from client/modules/User/components/CookieConsent.jsx
rename to client/modules/User/components/CookieConsent.tsx
From ebe82159ad5df697d33f5b99c86e21cec8ae95ea Mon Sep 17 00:00:00 2001
From: Claire Peng
Date: Mon, 27 Oct 2025 00:16:31 +0000
Subject: [PATCH 30/52] client/modules/User/components/CookieConsent: add type
dependencies, update to named export, update types
---
client/modules/App/App.jsx | 2 +-
.../modules/User/components/CookieConsent.tsx | 84 ++++++++++++-------
package-lock.json | 32 +++++++
package.json | 2 +
4 files changed, 89 insertions(+), 31 deletions(-)
diff --git a/client/modules/App/App.jsx b/client/modules/App/App.jsx
index 53b0c82c63..242b5244c8 100644
--- a/client/modules/App/App.jsx
+++ b/client/modules/App/App.jsx
@@ -6,7 +6,7 @@ import { showReduxDevTools } from '../../store';
import DevTools from './components/DevTools';
import { setPreviousPath } from '../IDE/actions/ide';
import { setLanguage } from '../IDE/actions/preferences';
-import CookieConsent from '../User/components/CookieConsent';
+import { CookieConsent } from '../User/components/CookieConsent';
function hideCookieConsent(pathname) {
if (pathname.includes('/full/') || pathname.includes('/embed/')) {
diff --git a/client/modules/User/components/CookieConsent.tsx b/client/modules/User/components/CookieConsent.tsx
index 9cfff74ac6..ad8fa8e743 100644
--- a/client/modules/User/components/CookieConsent.tsx
+++ b/client/modules/User/components/CookieConsent.tsx
@@ -6,17 +6,21 @@ import ReactGA from 'react-ga';
import { Transition } from 'react-transition-group';
import { Link } from 'react-router-dom';
import { Trans, useTranslation } from 'react-i18next';
-import PropTypes from 'prop-types';
import { getConfig } from '../../../utils/getConfig';
import { setUserCookieConsent } from '../actions';
import { remSize, prop, device } from '../../../theme';
import { Button, ButtonKinds } from '../../../common/Button';
+import { RootState } from '../../../reducers';
+import { CookieConsentOptions } from '../../../../common/types';
+interface CookieConsentContainerState {
+ state: string;
+}
const CookieConsentContainer = styled.div`
position: fixed;
transition: 1.6s cubic-bezier(0.165, 0.84, 0.44, 1);
bottom: 0;
- transform: ${({ state }) => {
+ transform: ${({ state }: CookieConsentContainerState) => {
if (state === 'entered') {
return 'translateY(0)';
}
@@ -79,37 +83,49 @@ const CookieConsentButtons = styled.div`
const GOOGLE_ANALYTICS_ID = getConfig('GA_MEASUREMENT_ID');
-function CookieConsent({ hide }) {
- const user = useSelector((state) => state.user);
- const [cookieConsent, setBrowserCookieConsent] = useState('none');
+export function CookieConsent({ hide = false }: { hide?: boolean }) {
+ const user = useSelector((state: RootState) => state.user);
+ const [
+ cookieConsent,
+ setBrowserCookieConsent
+ ] = useState(CookieConsentOptions.NONE);
const [inProp, setInProp] = useState(false);
const dispatch = useDispatch();
const { t } = useTranslation();
function initializeCookieConsent() {
if (user.authenticated) {
+ if (!user.cookieConsent) {
+ return;
+ }
setBrowserCookieConsent(user.cookieConsent);
Cookies.set('p5-cookie-consent', user.cookieConsent, { expires: 365 });
return;
}
- setBrowserCookieConsent('none');
- Cookies.set('p5-cookie-consent', 'none', { expires: 365 });
+ setBrowserCookieConsent(CookieConsentOptions.NONE);
+ Cookies.set('p5-cookie-consent', CookieConsentOptions.NONE, {
+ expires: 365
+ });
}
function acceptAllCookies() {
if (user.authenticated) {
- dispatch(setUserCookieConsent('all'));
+ dispatch(setUserCookieConsent(CookieConsentOptions.ALL));
}
- setBrowserCookieConsent('all');
- Cookies.set('p5-cookie-consent', 'all', { expires: 365 });
+ setBrowserCookieConsent(CookieConsentOptions.ALL);
+ Cookies.set('p5-cookie-consent', CookieConsentOptions.ALL, {
+ expires: 365
+ });
}
function acceptEssentialCookies() {
if (user.authenticated) {
- dispatch(setUserCookieConsent('essential'));
+ dispatch(setUserCookieConsent(CookieConsentOptions.ESSENTIAL));
}
- setBrowserCookieConsent('essential');
- Cookies.set('p5-cookie-consent', 'essential', { expires: 365 });
+ setBrowserCookieConsent(CookieConsentOptions.ESSENTIAL);
+ Cookies.set('p5-cookie-consent', CookieConsentOptions.ESSENTIAL, {
+ expires: 365
+ });
// Remove Google Analytics Cookies
Cookies.remove('_ga');
Cookies.remove('_gat');
@@ -118,11 +134,29 @@ function CookieConsent({ hide }) {
function mergeCookieConsent() {
if (user.authenticated) {
- if (user.cookieConsent === 'none' && cookieConsent !== 'none') {
- dispatch(setUserCookieConsent(cookieConsent));
- } else if (user.cookieConsent !== 'none') {
+ if (!user.cookieConsent) {
+ return;
+ }
+ if (
+ ![
+ CookieConsentOptions.ALL,
+ CookieConsentOptions.ESSENTIAL,
+ CookieConsentOptions.NONE
+ ].includes(user.cookieConsent)
+ ) {
+ return;
+ }
+
+ if (
+ user.cookieConsent === CookieConsentOptions.NONE &&
+ cookieConsent !== CookieConsentOptions.NONE
+ ) {
+ dispatch(setUserCookieConsent(cookieConsent as CookieConsentOptions));
+ } else if (user.cookieConsent !== CookieConsentOptions.NONE) {
setBrowserCookieConsent(user.cookieConsent);
- Cookies.set('p5-cookie-consent', user.cookieConsent, { expires: 365 });
+ Cookies.set('p5-cookie-consent', user.cookieConsent, {
+ expires: 365
+ });
}
}
}
@@ -130,7 +164,7 @@ function CookieConsent({ hide }) {
useEffect(() => {
const p5CookieConsent = Cookies.get('p5-cookie-consent');
if (p5CookieConsent) {
- setBrowserCookieConsent(p5CookieConsent);
+ setBrowserCookieConsent(p5CookieConsent as CookieConsentOptions);
} else {
initializeCookieConsent();
}
@@ -165,9 +199,9 @@ function CookieConsent({ hide }) {
return (
- {(state) => (
+ {(state: CookieConsentContainerState['state']) => (
-
+
{t('Cookies.Header')}
@@ -191,13 +225,3 @@ function CookieConsent({ hide }) {
);
}
-
-CookieConsent.propTypes = {
- hide: PropTypes.bool
-};
-
-CookieConsent.defaultProps = {
- hide: false
-};
-
-export default CookieConsent;
diff --git a/package-lock.json b/package-lock.json
index f948e6f8dd..f07e6ee670 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -164,6 +164,7 @@
"@types/classnames": "^2.3.0",
"@types/friendly-words": "^1.2.2",
"@types/jest": "^29.5.14",
+ "@types/js-cookie": "^3.0.6",
"@types/mjml": "^4.7.4",
"@types/node": "^16.18.126",
"@types/nodemailer": "^7.0.1",
@@ -173,6 +174,7 @@
"@types/react-dom": "^16.9.25",
"@types/react-helmet": "^6.1.11",
"@types/react-router-dom": "^5.3.3",
+ "@types/react-transition-group": "^4.4.12",
"@types/sinon": "^17.0.4",
"@types/styled-components": "^5.1.34",
"@typescript-eslint/eslint-plugin": "^5.62.0",
@@ -16448,6 +16450,13 @@
"pretty-format": "^29.0.0"
}
},
+ "node_modules/@types/js-cookie": {
+ "version": "3.0.6",
+ "resolved": "https://registry.npmjs.org/@types/js-cookie/-/js-cookie-3.0.6.tgz",
+ "integrity": "sha512-wkw9yd1kEXOPnvEeEV1Go1MmxtBJL0RR79aOTAApecWFVu7w0NNXNqhcWgvw2YgZDYadliXkl14pa3WXw5jlCQ==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/@types/js-levenshtein": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@types/js-levenshtein/-/js-levenshtein-1.1.0.tgz",
@@ -16691,6 +16700,16 @@
"@types/react-router": "*"
}
},
+ "node_modules/@types/react-transition-group": {
+ "version": "4.4.12",
+ "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.12.tgz",
+ "integrity": "sha512-8TV6R3h2j7a91c+1DXdJi3Syo69zzIZbz7Lg5tORM5LEJG7X/E6a1V3drRyBRZq7/utz7A+c4OgYLiLcYGHG6w==",
+ "dev": true,
+ "license": "MIT",
+ "peerDependencies": {
+ "@types/react": "*"
+ }
+ },
"node_modules/@types/react/node_modules/csstype": {
"version": "3.0.8",
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.8.tgz",
@@ -53160,6 +53179,12 @@
"pretty-format": "^29.0.0"
}
},
+ "@types/js-cookie": {
+ "version": "3.0.6",
+ "resolved": "https://registry.npmjs.org/@types/js-cookie/-/js-cookie-3.0.6.tgz",
+ "integrity": "sha512-wkw9yd1kEXOPnvEeEV1Go1MmxtBJL0RR79aOTAApecWFVu7w0NNXNqhcWgvw2YgZDYadliXkl14pa3WXw5jlCQ==",
+ "dev": true
+ },
"@types/js-levenshtein": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@types/js-levenshtein/-/js-levenshtein-1.1.0.tgz",
@@ -53398,6 +53423,13 @@
"@types/react-router": "*"
}
},
+ "@types/react-transition-group": {
+ "version": "4.4.12",
+ "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.12.tgz",
+ "integrity": "sha512-8TV6R3h2j7a91c+1DXdJi3Syo69zzIZbz7Lg5tORM5LEJG7X/E6a1V3drRyBRZq7/utz7A+c4OgYLiLcYGHG6w==",
+ "dev": true,
+ "requires": {}
+ },
"@types/redux-devtools-themes": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/@types/redux-devtools-themes/-/redux-devtools-themes-1.0.0.tgz",
diff --git a/package.json b/package.json
index df7a8dc603..1f531d396f 100644
--- a/package.json
+++ b/package.json
@@ -139,6 +139,7 @@
"@types/classnames": "^2.3.0",
"@types/friendly-words": "^1.2.2",
"@types/jest": "^29.5.14",
+ "@types/js-cookie": "^3.0.6",
"@types/mjml": "^4.7.4",
"@types/node": "^16.18.126",
"@types/nodemailer": "^7.0.1",
@@ -148,6 +149,7 @@
"@types/react-dom": "^16.9.25",
"@types/react-helmet": "^6.1.11",
"@types/react-router-dom": "^5.3.3",
+ "@types/react-transition-group": "^4.4.12",
"@types/sinon": "^17.0.4",
"@types/styled-components": "^5.1.34",
"@typescript-eslint/eslint-plugin": "^5.62.0",
From 57ab54016d7e229af22d55131ef90a2ec8a351f7 Mon Sep 17 00:00:00 2001
From: Claire Peng
Date: Mon, 27 Oct 2025 00:39:03 +0000
Subject: [PATCH 31/52] client/modules/User/components/VisibilityDropdown:
update to ts, no-verify
---
.../components/{VisibilityDropdown.jsx => VisibilityDropdown.tsx} | 0
1 file changed, 0 insertions(+), 0 deletions(-)
rename client/modules/User/components/{VisibilityDropdown.jsx => VisibilityDropdown.tsx} (100%)
diff --git a/client/modules/User/components/VisibilityDropdown.jsx b/client/modules/User/components/VisibilityDropdown.tsx
similarity index 100%
rename from client/modules/User/components/VisibilityDropdown.jsx
rename to client/modules/User/components/VisibilityDropdown.tsx
From 3e93724d9fc0c62064d0f32824d527fe99977f54 Mon Sep 17 00:00:00 2001
From: Claire Peng
Date: Mon, 27 Oct 2025 00:39:23 +0000
Subject: [PATCH 32/52] client/modules/User/components/VisibilityDropdown:
update to named export and add types
---
.../modules/IDE/components/Header/Toolbar.jsx | 2 +-
.../IDE/components/SketchListRowBase.jsx | 2 +-
.../User/components/VisibilityDropdown.tsx | 54 +++++++++++--------
3 files changed, 33 insertions(+), 25 deletions(-)
diff --git a/client/modules/IDE/components/Header/Toolbar.jsx b/client/modules/IDE/components/Header/Toolbar.jsx
index fbf0b5d091..16ed74ff3e 100644
--- a/client/modules/IDE/components/Header/Toolbar.jsx
+++ b/client/modules/IDE/components/Header/Toolbar.jsx
@@ -20,7 +20,7 @@ import StopIcon from '../../../../images/stop.svg';
import PreferencesIcon from '../../../../images/preferences.svg';
import ProjectName from './ProjectName';
import VersionIndicator from '../VersionIndicator';
-import VisibilityDropdown from '../../../User/components/VisibilityDropdown';
+import { VisibilityDropdown } from '../../../User/components/VisibilityDropdown';
import { changeVisibility } from '../../actions/project';
const Toolbar = (props) => {
diff --git a/client/modules/IDE/components/SketchListRowBase.jsx b/client/modules/IDE/components/SketchListRowBase.jsx
index e1c4c80f13..d11cb8bf40 100644
--- a/client/modules/IDE/components/SketchListRowBase.jsx
+++ b/client/modules/IDE/components/SketchListRowBase.jsx
@@ -10,7 +10,7 @@ import { TableDropdown } from '../../../components/Dropdown/TableDropdown';
import { MenuItem } from '../../../components/Dropdown/MenuItem';
import { formatDateToString } from '../../../utils/formatDate';
import { getConfig } from '../../../utils/getConfig';
-import VisibilityDropdown from '../../User/components/VisibilityDropdown';
+import { VisibilityDropdown } from '../../User/components/VisibilityDropdown';
const ROOT_URL = getConfig('API_URL');
diff --git a/client/modules/User/components/VisibilityDropdown.tsx b/client/modules/User/components/VisibilityDropdown.tsx
index 972ccad88a..d5ea66f234 100644
--- a/client/modules/User/components/VisibilityDropdown.tsx
+++ b/client/modules/User/components/VisibilityDropdown.tsx
@@ -1,13 +1,29 @@
import React, { useState, useRef, useEffect } from 'react';
-import PropTypes from 'prop-types';
import { useTranslation } from 'react-i18next';
import LockIcon from '../../../images/lock.svg';
import EarthIcon from '../../../images/earth.svg';
import CheckmarkIcon from '../../../images/checkmark.svg';
-const VisibilityDropdown = ({ sketch, onVisibilityChange, location }) => {
+export interface VisibilityDropdownProps {
+ sketch: {
+ id: string;
+ name: string;
+ visibility: string;
+ };
+ onVisibilityChange: (
+ sketchId: string,
+ sketchName: string,
+ newVisibility: string
+ ) => void;
+ location?: string;
+}
+export const VisibilityDropdown = ({
+ sketch,
+ onVisibilityChange,
+ location = 'sketchlist'
+}: VisibilityDropdownProps) => {
const [isOpen, setIsOpen] = useState(false);
- const dropdownRef = useRef(null);
+ const dropdownRef = useRef(null);
const { t } = useTranslation();
@@ -31,8 +47,13 @@ const VisibilityDropdown = ({ sketch, onVisibilityChange, location }) => {
visibilityOptions[0];
useEffect(() => {
- const handleClickOutside = (event) => {
- if (dropdownRef.current && !dropdownRef.current.contains(event.target)) {
+ const handleClickOutside = (event: MouseEvent) => {
+ const target = event.target as Node | null;
+ if (
+ dropdownRef.current &&
+ target &&
+ !dropdownRef.current.contains(target)
+ ) {
setIsOpen(false);
}
};
@@ -41,14 +62,17 @@ const VisibilityDropdown = ({ sketch, onVisibilityChange, location }) => {
return () => document.removeEventListener('mousedown', handleClickOutside);
}, []);
- const handleVisibilitySelect = (newVisibility) => {
+ const handleVisibilitySelect = (newVisibility: string) => {
if (newVisibility !== sketch.visibility) {
onVisibilityChange(sketch.id, sketch.name, newVisibility);
}
setIsOpen(false);
};
- const handleKeyDown = (event, visibility) => {
+ const handleKeyDown = (
+ event: React.KeyboardEvent,
+ visibility: string
+ ) => {
if (event.key === 'Enter' || event.key === ' ') {
event.preventDefault();
handleVisibilitySelect(visibility);
@@ -104,19 +128,3 @@ const VisibilityDropdown = ({ sketch, onVisibilityChange, location }) => {
);
};
-
-VisibilityDropdown.defaultProps = {
- location: 'sketchlist'
-};
-
-VisibilityDropdown.propTypes = {
- sketch: PropTypes.shape({
- id: PropTypes.string.isRequired,
- name: PropTypes.string.isRequired,
- visibility: PropTypes.string.isRequired
- }).isRequired,
- onVisibilityChange: PropTypes.func.isRequired,
- location: PropTypes.string
-};
-
-export default VisibilityDropdown;
From 730270148b332ab9a9af072cd5ff599816d0818e Mon Sep 17 00:00:00 2001
From: Claire Peng