Skip to content

Commit

Permalink
Pull Request online-go#2845: POC - Add tooltip to modal, Modal provid…
Browse files Browse the repository at this point in the history
…er (for computer challenge, language picker, game fork)
  • Loading branch information
GreenAsJade committed Nov 4, 2024
2 parents e405026 + cdf2655 commit 16e3b91
Show file tree
Hide file tree
Showing 15 changed files with 474 additions and 173 deletions.
5 changes: 4 additions & 1 deletion jest.config.js → jest.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ module.exports = {
"src",
"node_modules"
],
"moduleNameMapper": {
'^@/(.*)': '<rootDir>/src/$1',
},
"setupFiles": ["./setup-jest.js"],
"setupFilesAfterEnv": ["jest-chain"],
"setupFilesAfterEnv": ["jest-chain", "@testing-library/jest-dom"],
}
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,7 @@
"sanitize-html": "^2.12.1",
"sweetalert2": "^11.4.17",
"ts-md5": "^1.3.1",
"ts-node": "^10.9.2",
"valid-url": "^1.0.9",
"whatwg-fetch": "^3.0.0"
},
Expand Down
89 changes: 89 additions & 0 deletions src/components/ChallengeModal/ChallengeModal.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
/*
* Copyright (C) Online-Go.com
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

/* cspell: words groupadmin cotsen */
import * as React from "react";

import * as ChallengeModal from "./ChallengeModal";
import { fireEvent, render, screen } from "@testing-library/react";
import { ModalProvider } from "../Modal/ModalProvider";
import { ModalContext, ModalTypes } from "../Modal/ModalContext";
import * as DynamicHelp from "react-dynamic-help";

jest.mock("./../Modal", () => {
return {
...jest.requireActual("./../Modal"),
};
});

describe("ChallengeModal", () => {
it("should do a computer challenge via provider", () => {
const challengeModalSpy = jest
.spyOn(ChallengeModal, "ChallengeModal")
.mockImplementation(jest.fn());

render(
<ModalProvider>
<ModalContext.Consumer>
{({ showModal }) => {
showModal(ModalTypes.Challenge);
return null;
}}
</ModalContext.Consumer>
</ModalProvider>,
);

expect(challengeModalSpy.mock.calls[0][0]).toStrictEqual({
mode: "computer",
initialState: null,
playerId: undefined,
});

challengeModalSpy.mockRestore();
});

it("should close", () => {
const DynamicHelpProviderValue = {
registerTargetItem: jest.fn().mockReturnValue({ ref: jest.fn() }),
triggerFlow: jest.fn(),
enableHelp: jest.fn(),
getFlowInfo: jest.fn(),
enableFlow: jest.fn(),
reloadUserState: jest.fn(),
signalUsed: jest.fn(),
getSystemStatus: jest.fn(),
resetHelp: jest.fn(),
};

render(
<DynamicHelp.Api.Provider value={DynamicHelpProviderValue}>
<ModalProvider>
<ModalContext.Consumer>
{({ showModal }) => {
showModal(ModalTypes.Challenge);
return null;
}}
</ModalContext.Consumer>
</ModalProvider>
</DynamicHelp.Api.Provider>,
);
const closeButton = screen.getByText("Close");
expect(closeButton).toBeInTheDocument();
fireEvent.click(closeButton);
expect(closeButton).not.toBeInTheDocument();
});
});
12 changes: 7 additions & 5 deletions src/components/ChallengeModal/ChallengeModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -61,10 +61,11 @@ import {
saveTimeControlSettings,
updateSystem,
} from "@/components/TimeControl/TimeControlUpdates";
import { ActivateTooltip } from "@/views/HelpFlows/ModalHelp";

export type ChallengeDetails = rest_api.ChallengeDetails;

type ChallengeModes = "open" | "computer" | "player" | "demo";
export type ChallengeModes = "open" | "computer" | "player" | "demo";

interface Events {}

Expand Down Expand Up @@ -1778,7 +1779,11 @@ export class ChallengeModalBody extends React.Component<
&nbsp; {player_username}
</span>
)}
{mode === "computer" && <span>{_("Computer")}</span>}
{mode === "computer" && (
<ActivateTooltip flow="modal-help" item="intro">
<span>{_("Computer")}</span>
</ActivateTooltip>
)}
</h2>
</div>
<div className="body">
Expand Down Expand Up @@ -1960,9 +1965,6 @@ export function createDemoBoard(
/>,
);
}
export function challengeComputer() {
return challenge(undefined, null, true);
}
export function challengeRematch(
goban: GobanRenderer,
opponent: GobanEnginePlayerEntry,
Expand Down
123 changes: 49 additions & 74 deletions src/components/ChallengeModal/ForkModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,94 +18,69 @@
import * as React from "react";
import * as data from "@/lib/data";
import { _ } from "@/lib/translate";
import { Modal, openModal } from "@/components/Modal";
import { GobanRenderer } from "goban";
import { PlayerAutocomplete } from "@/components/PlayerAutocomplete";
import { MiniGoban } from "@/components/MiniGoban";
import { challenge } from "@/components/ChallengeModal";
import { PlayerCacheEntry } from "@/lib/player_cache";

interface Events {}
import { ModalContext } from "../Modal/ModalContext";

interface ForkModalProperties {
goban: GobanRenderer;
}

export class ForkModal extends Modal<Events, ForkModalProperties, any> {
constructor(props: ForkModalProperties) {
super(props);
export const ForkModal = ({ goban }: ForkModalProperties) => {
const [currPlayer, setCurrPlayer] = React.useState(null as PlayerCacheEntry | null);
const { hideModal } = React.useContext(ModalContext);

const goban = this.props.goban;
this.state = {
player: null,
fork_preview: {
//"moves": goban.engine.cur_move.getMoveStringToThisPoint(),
//"initial_state": goban.engine.initial_state,
//"initial_player": goban.engine.config.initial_player,
moves: [],
initial_state: goban.engine.computeInitialStateForForkedGame(),
initial_player: goban.engine.colorToMove(),
width: goban.engine.width,
height: goban.engine.height,
rules: goban.engine.rules,
handicap: goban.engine.handicap,
komi: goban.engine.komi,
move_number: goban.engine.getMoveNumber(),
game_name: goban.engine.name,
},
};
}
const forkPreview = {
moves: [],
initial_state: goban.engine.computeInitialStateForForkedGame(),
initial_player: goban.engine.colorToMove(),
width: goban.engine.width,
height: goban.engine.height,
rules: goban.engine.rules,
handicap: goban.engine.handicap,
komi: goban.engine.komi,
move_number: goban.engine.getMoveNumber(),
game_name: goban.engine.name,
};

openChallengeModal = () => {
this.close();
challenge(this.state.player.id, this.state.fork_preview);
const openChallengeModal = () => {
hideModal();
if (currPlayer) {
challenge(currPlayer.id, forkPreview);
}
};

setPlayer = (player: PlayerCacheEntry | null) => {
this.setState({ player: player });
const setPlayer = (player: PlayerCacheEntry | null) => {
setCurrPlayer(player);
};

render() {
return (
<div className="Modal ForkModal">
<div className="header space-around">
<h2>{_("Player to challenge")}:</h2>{" "}
<PlayerAutocomplete onComplete={this.setPlayer} />
</div>
<div className="body space-around">
<MiniGoban
game_id={0}
black={undefined}
white={undefined}
json={this.state.fork_preview}
noLink
/>
</div>
<div className="buttons">
<button onClick={this.close}>{_("Cancel")}</button>
<button
className="primary"
disabled={
this.state.player == null ||
this.state.player.id === data.get("user").id
}
onClick={this.openChallengeModal}
>
{_("Game settings")} &rarr;
</button>
</div>
return (
<div className="Modal ForkModal">
<div className="header space-around">
<h2>{_("Player to challenge")}:</h2> <PlayerAutocomplete onComplete={setPlayer} />
</div>
);
}
}

export function openForkModal(goban: GobanRenderer) {
return openModal(<ForkModal goban={goban} />);
}
export function challengeFromBoardPosition(goban: GobanRenderer) {
if (!goban) {
return;
}

openForkModal(goban);
}
<div className="body space-around">
<MiniGoban
game_id={0}
black={undefined}
white={undefined}
json={forkPreview}
noLink
/>
</div>
<div className="buttons">
<button onClick={hideModal}>{_("Cancel")}</button>
<button
className="primary"
disabled={currPlayer == null || currPlayer.id === data.get("user").id}
onClick={openChallengeModal}
>
{_("Game settings")} &rarr;
</button>
</div>
</div>
);
};
Loading

0 comments on commit 16e3b91

Please sign in to comment.