From 8ef68e29ece15e29ea178329ce41d3676b2cb6c5 Mon Sep 17 00:00:00 2001 From: Jeffrey Carl Faden Date: Tue, 23 May 2023 09:19:43 -0700 Subject: [PATCH] Convert ConfirmModal to TypeScript --- package.json | 2 +- src/actions/tests/decisions.test.ts | 9 ++---- src/actions/users.ts | 4 +-- src/actions/websockets.ts | 28 +++++++++---------- src/components/AddUserForm/AddUserForm.tsx | 5 ++-- ...rmContainer.js => AddUserFormContainer.ts} | 7 +++-- .../ChangeTeamURLModalContainer.ts | 7 ++--- ...irmModal.test.js => ConfirmModal.test.tsx} | 4 +-- .../{ConfirmModal.js => ConfirmModal.tsx} | 21 +++++++------- ...lContainer.js => ConfirmModalContainer.ts} | 10 +++++-- src/interfaces.ts | 15 ++++++++-- 11 files changed, 60 insertions(+), 52 deletions(-) rename src/components/AddUserForm/{AddUserFormContainer.js => AddUserFormContainer.ts} (70%) rename src/components/ConfirmModal/{ConfirmModal.test.js => ConfirmModal.test.tsx} (86%) rename src/components/ConfirmModal/{ConfirmModal.js => ConfirmModal.tsx} (70%) rename src/components/ConfirmModal/{ConfirmModalContainer.js => ConfirmModalContainer.ts} (69%) diff --git a/package.json b/package.json index 1bd765585..0d89641d6 100644 --- a/package.json +++ b/package.json @@ -266,4 +266,4 @@ "prepare": "husky install" }, "packageManager": "yarn@3.5.1" -} \ No newline at end of file +} diff --git a/src/actions/tests/decisions.test.ts b/src/actions/tests/decisions.test.ts index 83abde6e7..06e866887 100644 --- a/src/actions/tests/decisions.test.ts +++ b/src/actions/tests/decisions.test.ts @@ -1,7 +1,6 @@ /* eslint-env mocha */ /* eslint-disable no-unused-expressions, no-underscore-dangle, import/no-duplicates, arrow-body-style */ -import { ThunkDispatch } from "@reduxjs/toolkit"; import { expect } from "chai"; import { configureMockStore, @@ -10,17 +9,13 @@ import { import fetchMock from "fetch-mock"; import thunk from "redux-thunk"; import * as decisions from "../decisions"; -import { Action, State } from "../../interfaces"; +import { Action, Dispatch, State } from "../../interfaces"; const middlewares = [thunk]; const mockStore = configureMockStore(middlewares); describe("actions/decisions", () => { - let store: MockStoreEnhanced< - State, - Action, - ThunkDispatch - >; + let store: MockStoreEnhanced; beforeEach(() => { store = mockStore({}); diff --git a/src/actions/users.ts b/src/actions/users.ts index d70216b60..da4f699a9 100644 --- a/src/actions/users.ts +++ b/src/actions/users.ts @@ -114,7 +114,7 @@ export function removeUser( }; } -export function postUser(obj: User): Action { +export function postUser(obj: Partial): Action { return { type: "POST_USER", user: obj, @@ -129,7 +129,7 @@ export function userPosted(json: User): Action { } export function addUser( - payload: User + payload: Partial ): ThunkAction { return (dispatch) => { dispatch(postUser(payload)); diff --git a/src/actions/websockets.ts b/src/actions/websockets.ts index 33650b172..4330cc017 100644 --- a/src/actions/websockets.ts +++ b/src/actions/websockets.ts @@ -1,11 +1,11 @@ +import { ThunkAction } from "@reduxjs/toolkit"; import { sortRestaurants } from "./restaurants"; import { notify } from "./notifications"; -import { Action, State } from "../interfaces"; -import { ThunkAction, ThunkDispatch } from "@reduxjs/toolkit"; +import { Action, Dispatch, State } from "../interfaces"; let sortTimeout: NodeJS.Timer; -const sort = (dispatch: ThunkDispatch) => { +const sort = (dispatch: Dispatch) => { clearTimeout(sortTimeout); sortTimeout = setTimeout(() => { dispatch(sortRestaurants()); @@ -47,17 +47,17 @@ const actionMap: Partial<{ data: Action ) => ThunkAction; }> = { - ["RESTAURANT_POSTED"]: dispatchSortNotify, - ["RESTAURANT_DELETED"]: notifyDispatch, - ["RESTAURANT_RENAMED"]: notifyDispatchSort, - ["VOTE_POSTED"]: notifyDispatchSort, - ["VOTE_DELETED"]: notifyDispatchSort, - ["POSTED_TAG_TO_RESTAURANT"]: dispatchNotify, - ["POSTED_NEW_TAG_TO_RESTAURANT"]: dispatchNotify, - ["DELETED_TAG_FROM_RESTAURANT"]: dispatchNotify, - ["TAG_DELETED"]: notifyDispatch, - ["DECISION_POSTED"]: dispatchSortNotify, - ["DECISIONS_DELETED"]: dispatchSortNotify, + RESTAURANT_POSTED: dispatchSortNotify, + RESTAURANT_DELETED: notifyDispatch, + RESTAURANT_RENAMED: notifyDispatchSort, + VOTE_POSTED: notifyDispatchSort, + VOTE_DELETED: notifyDispatchSort, + POSTED_TAG_TO_RESTAURANT: dispatchNotify, + POSTED_NEW_TAG_TO_RESTAURANT: dispatchNotify, + DELETED_TAG_FROM_RESTAURANT: dispatchNotify, + TAG_DELETED: notifyDispatch, + DECISION_POSTED: dispatchSortNotify, + DECISIONS_DELETED: dispatchSortNotify, }; export function messageReceived( diff --git a/src/components/AddUserForm/AddUserForm.tsx b/src/components/AddUserForm/AddUserForm.tsx index 0ec7bc09c..315b1e659 100644 --- a/src/components/AddUserForm/AddUserForm.tsx +++ b/src/components/AddUserForm/AddUserForm.tsx @@ -5,11 +5,12 @@ import Form from "react-bootstrap/Form"; import Row from "react-bootstrap/Row"; import { IntlShape } from "react-intl"; import { globalMessageDescriptor as gm } from "../../helpers/generateMessageDescriptor"; +import { RoleType } from "../../interfaces"; interface AddUserFormState { email?: string; name?: string; - type?: string; + type?: RoleType; } export interface AddUserFormProps { @@ -21,7 +22,7 @@ export interface AddUserFormProps { } class AddUserForm extends Component { - static defaultState = { + static defaultState: AddUserFormState = { email: "", name: "", type: "member", diff --git a/src/components/AddUserForm/AddUserFormContainer.js b/src/components/AddUserForm/AddUserFormContainer.ts similarity index 70% rename from src/components/AddUserForm/AddUserFormContainer.js rename to src/components/AddUserForm/AddUserFormContainer.ts index c5baf4850..05248ad0a 100644 --- a/src/components/AddUserForm/AddUserFormContainer.js +++ b/src/components/AddUserForm/AddUserFormContainer.ts @@ -3,16 +3,17 @@ import { injectIntl } from "react-intl"; import { addUser } from "../../actions/users"; import { currentUserHasRole, isUserListReady } from "../../selectors"; import AddUserForm from "./AddUserForm"; +import { Dispatch, State, User } from "../../interfaces"; -const mapStateToProps = (state) => ({ +const mapStateToProps = (state: State) => ({ hasGuestRole: currentUserHasRole(state, "guest"), hasMemberRole: currentUserHasRole(state, "member"), hasOwnerRole: currentUserHasRole(state, "owner"), userListReady: isUserListReady(state), }); -const mapDispatchToProps = (dispatch) => ({ - addUserToTeam: (payload) => dispatch(addUser(payload)), +const mapDispatchToProps = (dispatch: Dispatch) => ({ + addUserToTeam: (payload: Partial) => dispatch(addUser(payload)), }); export default connect( diff --git a/src/components/ChangeTeamURLModal/ChangeTeamURLModalContainer.ts b/src/components/ChangeTeamURLModal/ChangeTeamURLModalContainer.ts index 9b993f176..796def237 100644 --- a/src/components/ChangeTeamURLModal/ChangeTeamURLModalContainer.ts +++ b/src/components/ChangeTeamURLModal/ChangeTeamURLModalContainer.ts @@ -1,8 +1,7 @@ import { connect } from "react-redux"; -import { ThunkDispatch } from "@reduxjs/toolkit"; import { updateTeam } from "../../actions/team"; import { hideModal } from "../../actions/modals"; -import { Action, State, Team } from "../../interfaces"; +import { Dispatch, State, Team } from "../../interfaces"; import { getTeam } from "../../selectors/team"; import ChangeTeamURLModal from "./ChangeTeamURLModal"; @@ -14,9 +13,7 @@ const mapStateToProps = (state: State) => ({ shown: !!state.modals[modalName].shown, }); -const mapDispatchToProps = ( - dispatch: ThunkDispatch -) => ({ +const mapDispatchToProps = (dispatch: Dispatch) => ({ hideModal: () => { dispatch(hideModal(modalName)); }, diff --git a/src/components/ConfirmModal/ConfirmModal.test.js b/src/components/ConfirmModal/ConfirmModal.test.tsx similarity index 86% rename from src/components/ConfirmModal/ConfirmModal.test.js rename to src/components/ConfirmModal/ConfirmModal.test.tsx index 95fa20337..bbff3cffc 100644 --- a/src/components/ConfirmModal/ConfirmModal.test.js +++ b/src/components/ConfirmModal/ConfirmModal.test.tsx @@ -5,10 +5,10 @@ import { expect } from "chai"; import React from "react"; import sinon from "sinon"; import { render, screen } from "../../../test/test-utils"; -import ConfirmModal from "./ConfirmModal"; +import ConfirmModal, { ConfirmModalProps } from "./ConfirmModal"; describe("ConfirmModal", () => { - let props; + let props: ConfirmModalProps; beforeEach(() => { props = { diff --git a/src/components/ConfirmModal/ConfirmModal.js b/src/components/ConfirmModal/ConfirmModal.tsx similarity index 70% rename from src/components/ConfirmModal/ConfirmModal.js rename to src/components/ConfirmModal/ConfirmModal.tsx index 6804a9a03..370d1ff79 100644 --- a/src/components/ConfirmModal/ConfirmModal.js +++ b/src/components/ConfirmModal/ConfirmModal.tsx @@ -1,17 +1,24 @@ -import PropTypes from "prop-types"; -import React from "react"; +import React, { ReactNode } from "react"; import Modal from "react-bootstrap/Modal"; import ModalBody from "react-bootstrap/ModalBody"; import ModalFooter from "react-bootstrap/ModalFooter"; import Button from "react-bootstrap/Button"; +export interface ConfirmModalProps { + actionLabel: string; + shown: boolean; + hideModal: () => void; + body: ReactNode; + handleSubmit: () => void; +} + const ConfirmModal = ({ actionLabel, shown, hideModal, body, handleSubmit, -}) => ( +}: ConfirmModalProps) => ( {body} @@ -31,12 +38,4 @@ const ConfirmModal = ({ ); -ConfirmModal.propTypes = { - actionLabel: PropTypes.node.isRequired, - body: PropTypes.node.isRequired, - shown: PropTypes.bool.isRequired, - hideModal: PropTypes.func.isRequired, - handleSubmit: PropTypes.func.isRequired, -}; - export default ConfirmModal; diff --git a/src/components/ConfirmModal/ConfirmModalContainer.js b/src/components/ConfirmModal/ConfirmModalContainer.ts similarity index 69% rename from src/components/ConfirmModal/ConfirmModalContainer.js rename to src/components/ConfirmModal/ConfirmModalContainer.ts index c777ea0bc..71a8396dc 100644 --- a/src/components/ConfirmModal/ConfirmModalContainer.js +++ b/src/components/ConfirmModal/ConfirmModalContainer.ts @@ -1,22 +1,26 @@ import { connect } from "react-redux"; import { hideModal } from "../../actions/modals"; import ConfirmModal from "./ConfirmModal"; +import { Dispatch, State } from "../../interfaces"; const modalName = "confirm"; -const mapStateToProps = (state) => ({ +const mapStateToProps = (state: State) => ({ actionLabel: state.modals[modalName].actionLabel, body: state.modals[modalName].body, action: state.modals[modalName].action, shown: !!state.modals[modalName].shown, }); -const mapDispatchToProps = (dispatch) => ({ +const mapDispatchToProps = (dispatch: Dispatch) => ({ dispatch, hideModal: () => dispatch(hideModal("confirm")), }); -const mergeProps = (stateProps, dispatchProps) => ({ +const mergeProps = ( + stateProps: ReturnType, + dispatchProps: ReturnType +) => ({ ...stateProps, ...dispatchProps, handleSubmit: () => { diff --git a/src/interfaces.ts b/src/interfaces.ts index 84941a6cd..6930ea105 100644 --- a/src/interfaces.ts +++ b/src/interfaces.ts @@ -8,6 +8,8 @@ import { User as UserModel, Vote as VoteModel, } from "./db"; +import { ThunkDispatch } from "@reduxjs/toolkit"; +import { ReactNode } from "react"; export interface ExtWebSocket extends WebSocket { teamId?: number; @@ -239,7 +241,7 @@ export type Action = } | { type: "POST_USER"; - user: User; + user: Partial; } | { type: "USER_POSTED"; @@ -453,7 +455,14 @@ interface BaseState { flashes: Flash[]; host: string; notifications: Notification[]; - modals: { [index: string]: { shown: boolean } }; + modals: { + [index: string]: { + action: () => void; + actionLabel: string; + body: ReactNode; + shown: boolean; + }; + }; listUi: { editNameFormValue?: string; flipMove: boolean; @@ -555,3 +564,5 @@ export type Reducer = ( state: State[T], action: Action ) => State[T]; + +export type Dispatch = ThunkDispatch;