@react-chess-tools/react-chess-puzzle 0.6.2 → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,255 @@
1
+ {/* Theming.mdx */}
2
+ import { Meta, ColorPalette, ColorItem } from '@storybook/blocks';
3
+
4
+ <Meta title="react-chess-puzzle/Theming" />
5
+
6
+ # Puzzle Theming
7
+
8
+ React Chess Puzzle extends the game theming system with puzzle-specific colors for hints, success, and failure states.
9
+
10
+ ## Quick Start
11
+
12
+ Pass a theme to `ChessPuzzle.Root` to customize puzzle appearance:
13
+
14
+ ```tsx
15
+ import { ChessPuzzle } from "@react-chess-tools/react-chess-puzzle";
16
+
17
+ const puzzle = {
18
+ fen: "4kb1r/p2r1ppp/4qn2/1B2p1B1/4P3/1Q6/PPP2PPP/2KR4 w k - 0 1",
19
+ moves: ["Bxd7+", "Nxd7", "Qb8+", "Nxb8", "Rd8#"],
20
+ };
21
+
22
+ function App() {
23
+ return (
24
+ <ChessPuzzle.Root
25
+ puzzle={puzzle}
26
+ theme={{ puzzle: { hint: "rgba(255, 215, 0, 0.6)" } }}
27
+ >
28
+ <ChessPuzzle.Board />
29
+ <ChessPuzzle.Hint>Show Hint</ChessPuzzle.Hint>
30
+ </ChessPuzzle.Root>
31
+ );
32
+ }
33
+ ```
34
+
35
+ ## Puzzle Theme Structure
36
+
37
+ The puzzle theme extends `ChessGameTheme` with an additional `puzzle` section:
38
+
39
+ ```typescript
40
+ interface ChessPuzzleTheme extends ChessGameTheme {
41
+ puzzle: {
42
+ success: string; // Color for correct moves (RGBA)
43
+ failure: string; // Color for incorrect moves (RGBA)
44
+ hint: string; // Color for hint squares (RGBA)
45
+ };
46
+ }
47
+ ```
48
+
49
+ ## Puzzle-Specific Colors
50
+
51
+ The puzzle theme adds three new color options:
52
+
53
+ <table>
54
+ <thead>
55
+ <tr>
56
+ <th>Color</th>
57
+ <th>Usage</th>
58
+ </tr>
59
+ </thead>
60
+ <tbody>
61
+ <tr>
62
+ <td>
63
+ <code>puzzle.success</code>
64
+ </td>
65
+ <td>Highlights squares when a correct move is made</td>
66
+ </tr>
67
+ <tr>
68
+ <td>
69
+ <code>puzzle.failure</code>
70
+ </td>
71
+ <td>Highlights squares when an incorrect move is made</td>
72
+ </tr>
73
+ <tr>
74
+ <td>
75
+ <code>puzzle.hint</code>
76
+ </td>
77
+ <td>Highlights the hint square(s) when hint is requested</td>
78
+ </tr>
79
+ </tbody>
80
+ </table>
81
+
82
+ ## Creating a Custom Puzzle Theme
83
+
84
+ You can customize both game and puzzle colors:
85
+
86
+ ```tsx
87
+ import { ChessPuzzle } from "@react-chess-tools/react-chess-puzzle";
88
+ import type { PartialChessPuzzleTheme } from "@react-chess-tools/react-chess-puzzle";
89
+
90
+ const neonTheme: PartialChessPuzzleTheme = {
91
+ // Customize board colors (inherited from game theme)
92
+ board: {
93
+ lightSquare: { backgroundColor: "#2a2a2a" },
94
+ darkSquare: { backgroundColor: "#1a1a1a" },
95
+ },
96
+ // Customize game state colors
97
+ state: {
98
+ lastMove: "rgba(0, 255, 255, 0.4)",
99
+ },
100
+ // Customize puzzle-specific colors
101
+ puzzle: {
102
+ success: "rgba(0, 255, 127, 0.6)",
103
+ failure: "rgba(255, 0, 127, 0.6)",
104
+ hint: "rgba(0, 191, 255, 0.6)",
105
+ },
106
+ };
107
+
108
+ function App() {
109
+ return (
110
+ <ChessPuzzle.Root puzzle={puzzle} theme={neonTheme}>
111
+ <ChessPuzzle.Board />
112
+ </ChessPuzzle.Root>
113
+ );
114
+ }
115
+ ```
116
+
117
+ ## Partial Theme Overrides
118
+
119
+ Override only the puzzle colors you need:
120
+
121
+ ```tsx
122
+ // Only change the hint color to gold
123
+ const goldHint: PartialChessPuzzleTheme = {
124
+ puzzle: {
125
+ hint: "rgba(255, 215, 0, 0.6)",
126
+ },
127
+ };
128
+
129
+ // Only change success/failure colors
130
+ const customFeedback: PartialChessPuzzleTheme = {
131
+ puzzle: {
132
+ success: "rgba(0, 200, 100, 0.7)",
133
+ failure: "rgba(200, 0, 50, 0.7)",
134
+ },
135
+ };
136
+ ```
137
+
138
+ ## Theme Inheritance
139
+
140
+ Puzzle themes inherit all game theme properties. You can:
141
+
142
+ 1. **Only specify puzzle colors** - game colors use defaults
143
+ 2. **Only specify game colors** - puzzle colors use defaults
144
+ 3. **Mix both** - customize any combination
145
+
146
+ ```tsx
147
+ // Example: Custom board + default puzzle colors
148
+ const customBoard: PartialChessPuzzleTheme = {
149
+ board: {
150
+ lightSquare: { backgroundColor: "#e8e8e8" },
151
+ darkSquare: { backgroundColor: "#4a4a4a" },
152
+ },
153
+ };
154
+
155
+ // Example: Default board + custom puzzle colors
156
+ const customPuzzle: PartialChessPuzzleTheme = {
157
+ puzzle: {
158
+ success: "rgba(100, 255, 100, 0.5)",
159
+ failure: "rgba(255, 100, 100, 0.5)",
160
+ hint: "rgba(100, 100, 255, 0.5)",
161
+ },
162
+ };
163
+ ```
164
+
165
+ ## Using Theme Utilities
166
+
167
+ ### mergePuzzleTheme
168
+
169
+ Merge a partial puzzle theme with defaults:
170
+
171
+ ```tsx
172
+ import {
173
+ mergePuzzleTheme,
174
+ defaultPuzzleTheme,
175
+ } from "@react-chess-tools/react-chess-puzzle";
176
+
177
+ const myTheme = mergePuzzleTheme({
178
+ puzzle: { hint: "rgba(255, 215, 0, 0.6)" },
179
+ });
180
+ // myTheme is now a complete ChessPuzzleTheme
181
+ ```
182
+
183
+ ### useChessPuzzleTheme Hook
184
+
185
+ Access the current puzzle theme from any component:
186
+
187
+ ```tsx
188
+ import { useChessPuzzleTheme } from "@react-chess-tools/react-chess-puzzle";
189
+
190
+ function HintButton() {
191
+ const theme = useChessPuzzleTheme();
192
+
193
+ return (
194
+ <button style={{ backgroundColor: theme.puzzle.hint }}>Show Hint</button>
195
+ );
196
+ }
197
+ ```
198
+
199
+ ## Default Puzzle Colors
200
+
201
+ <ColorPalette>
202
+ <ColorItem
203
+ title="Puzzle Colors"
204
+ subtitle="Puzzle state feedback"
205
+ colors={{
206
+ Success: "rgba(172, 206, 89, 0.5)",
207
+ Failure: "rgba(201, 52, 48, 0.5)",
208
+ Hint: "rgba(27, 172, 166, 0.5)",
209
+ }}
210
+ />
211
+ </ColorPalette>
212
+
213
+ ## Example Theme Variations
214
+
215
+ ### Pastel Theme
216
+
217
+ ```tsx
218
+ const pastelTheme: PartialChessPuzzleTheme = {
219
+ puzzle: {
220
+ success: "rgba(152, 251, 152, 0.6)", // Pale green
221
+ failure: "rgba(255, 182, 193, 0.6)", // Light pink
222
+ hint: "rgba(173, 216, 230, 0.6)", // Light blue
223
+ },
224
+ };
225
+ ```
226
+
227
+ ### High Contrast Theme
228
+
229
+ ```tsx
230
+ const highContrastTheme: PartialChessPuzzleTheme = {
231
+ puzzle: {
232
+ success: "rgba(0, 255, 0, 0.7)",
233
+ failure: "rgba(255, 0, 0, 0.7)",
234
+ hint: "rgba(0, 255, 255, 0.7)",
235
+ },
236
+ };
237
+ ```
238
+
239
+ ## TypeScript Support
240
+
241
+ All puzzle theme types are exported:
242
+
243
+ ```typescript
244
+ import type {
245
+ ChessPuzzleTheme,
246
+ PartialChessPuzzleTheme,
247
+ PuzzleStateTheme,
248
+ } from "@react-chess-tools/react-chess-puzzle";
249
+ ```
250
+
251
+ ## Next Steps
252
+
253
+ - Try the **[Puzzle Theme Playground](/story/react-chess-puzzle-theme-playground--puzzle-playground)** to experiment with colors
254
+ - See **[Puzzle Theme Examples](/story/react-chess-puzzle-theme-playground--puzzle-theme-examples)** for inspiration
255
+ - Check out **[Game Theming](/docs/react-chess-game-theming--docs)** for board and state color options
package/src/index.ts CHANGED
@@ -13,3 +13,17 @@ export type { HintProps } from "./components/ChessPuzzle/parts/Hint";
13
13
  export type { ResetProps } from "./components/ChessPuzzle/parts/Reset";
14
14
  export type { PuzzleBoardProps } from "./components/ChessPuzzle/parts/PuzzleBoard";
15
15
  export type { RootProps } from "./components/ChessPuzzle/parts/Root";
16
+
17
+ // Theme - Types
18
+ export type {
19
+ ChessPuzzleTheme,
20
+ PuzzleStateTheme,
21
+ PartialChessPuzzleTheme,
22
+ } from "./theme/types";
23
+
24
+ // Theme - Values
25
+ export { defaultPuzzleTheme } from "./theme/defaults";
26
+
27
+ // Theme - Utilities
28
+ export { mergePuzzleTheme } from "./theme/utils";
29
+ export { useChessPuzzleTheme, ChessPuzzleThemeContext } from "./theme/context";
@@ -0,0 +1,66 @@
1
+ import React from "react";
2
+ import { renderHook } from "@testing-library/react";
3
+ import { useChessPuzzleTheme, PuzzleThemeProvider } from "../context";
4
+ import { defaultPuzzleTheme } from "../defaults";
5
+
6
+ describe("useChessPuzzleTheme", () => {
7
+ it("should return default puzzle theme when no provider is present", () => {
8
+ const { result } = renderHook(() => useChessPuzzleTheme());
9
+ expect(result.current).toEqual(defaultPuzzleTheme);
10
+ });
11
+
12
+ it("should return provided theme when wrapped in PuzzleThemeProvider", () => {
13
+ const customTheme = {
14
+ ...defaultPuzzleTheme,
15
+ puzzle: {
16
+ ...defaultPuzzleTheme.puzzle,
17
+ hint: "rgba(0, 255, 255, 0.5)",
18
+ },
19
+ };
20
+
21
+ const wrapper = ({ children }: { children: React.ReactNode }) => (
22
+ <PuzzleThemeProvider theme={customTheme}>{children}</PuzzleThemeProvider>
23
+ );
24
+
25
+ const { result } = renderHook(() => useChessPuzzleTheme(), { wrapper });
26
+ expect(result.current.puzzle.hint).toBe("rgba(0, 255, 255, 0.5)");
27
+ });
28
+
29
+ it("should return all puzzle-specific properties", () => {
30
+ const { result } = renderHook(() => useChessPuzzleTheme());
31
+
32
+ expect(result.current.puzzle).toHaveProperty("success");
33
+ expect(result.current.puzzle).toHaveProperty("failure");
34
+ expect(result.current.puzzle).toHaveProperty("hint");
35
+ });
36
+
37
+ it("should return inherited game theme properties", () => {
38
+ const { result } = renderHook(() => useChessPuzzleTheme());
39
+
40
+ expect(result.current.board).toBeDefined();
41
+ expect(result.current.state).toBeDefined();
42
+ expect(result.current.indicators).toBeDefined();
43
+ });
44
+ });
45
+
46
+ describe("PuzzleThemeProvider", () => {
47
+ it("should allow nested providers with inner provider winning", () => {
48
+ const outerTheme = {
49
+ ...defaultPuzzleTheme,
50
+ puzzle: { ...defaultPuzzleTheme.puzzle, hint: "outer" },
51
+ };
52
+ const innerTheme = {
53
+ ...defaultPuzzleTheme,
54
+ puzzle: { ...defaultPuzzleTheme.puzzle, hint: "inner" },
55
+ };
56
+
57
+ const wrapper = ({ children }: { children: React.ReactNode }) => (
58
+ <PuzzleThemeProvider theme={outerTheme}>
59
+ <PuzzleThemeProvider theme={innerTheme}>{children}</PuzzleThemeProvider>
60
+ </PuzzleThemeProvider>
61
+ );
62
+
63
+ const { result } = renderHook(() => useChessPuzzleTheme(), { wrapper });
64
+ expect(result.current.puzzle.hint).toBe("inner");
65
+ });
66
+ });
@@ -0,0 +1,48 @@
1
+ import { defaultPuzzleTheme } from "../defaults";
2
+ import { defaultGameTheme } from "@react-chess-tools/react-chess-game";
3
+
4
+ describe("defaultPuzzleTheme", () => {
5
+ describe("backward compatibility", () => {
6
+ it("should have the original success color", () => {
7
+ expect(defaultPuzzleTheme.puzzle.success).toBe("rgba(172, 206, 89, 0.5)");
8
+ });
9
+
10
+ it("should have the original failure color", () => {
11
+ expect(defaultPuzzleTheme.puzzle.failure).toBe("rgba(201, 52, 48, 0.5)");
12
+ });
13
+
14
+ it("should have the original hint color", () => {
15
+ expect(defaultPuzzleTheme.puzzle.hint).toBe("rgba(27, 172, 166, 0.5)");
16
+ });
17
+ });
18
+
19
+ describe("inheritance from game theme", () => {
20
+ it("should include all game theme properties", () => {
21
+ expect(defaultPuzzleTheme.board).toBeDefined();
22
+ expect(defaultPuzzleTheme.state).toBeDefined();
23
+ expect(defaultPuzzleTheme.indicators).toBeDefined();
24
+ });
25
+
26
+ it("should have the same board colors as game theme", () => {
27
+ expect(defaultPuzzleTheme.board).toEqual(defaultGameTheme.board);
28
+ });
29
+
30
+ it("should have the same state colors as game theme", () => {
31
+ expect(defaultPuzzleTheme.state).toEqual(defaultGameTheme.state);
32
+ });
33
+
34
+ it("should have the same indicator colors as game theme", () => {
35
+ expect(defaultPuzzleTheme.indicators).toEqual(
36
+ defaultGameTheme.indicators,
37
+ );
38
+ });
39
+ });
40
+
41
+ describe("structure", () => {
42
+ it("should have puzzle property with all required colors", () => {
43
+ expect(defaultPuzzleTheme.puzzle).toHaveProperty("success");
44
+ expect(defaultPuzzleTheme.puzzle).toHaveProperty("failure");
45
+ expect(defaultPuzzleTheme.puzzle).toHaveProperty("hint");
46
+ });
47
+ });
48
+ });
@@ -0,0 +1,76 @@
1
+ import { mergePuzzleTheme } from "../utils";
2
+ import { defaultPuzzleTheme } from "../defaults";
3
+
4
+ describe("mergePuzzleTheme", () => {
5
+ it("should return default puzzle theme when no partial theme is provided", () => {
6
+ const result = mergePuzzleTheme();
7
+ expect(result).toEqual(defaultPuzzleTheme);
8
+ });
9
+
10
+ it("should return default puzzle theme when undefined is provided", () => {
11
+ const result = mergePuzzleTheme(undefined);
12
+ expect(result).toEqual(defaultPuzzleTheme);
13
+ });
14
+
15
+ it("should return a new object, not a reference to default", () => {
16
+ const result = mergePuzzleTheme();
17
+ expect(result).not.toBe(defaultPuzzleTheme);
18
+ });
19
+
20
+ it("should override puzzle-specific colors", () => {
21
+ const result = mergePuzzleTheme({
22
+ puzzle: { hint: "rgba(0, 255, 255, 0.5)" },
23
+ });
24
+
25
+ expect(result.puzzle.hint).toBe("rgba(0, 255, 255, 0.5)");
26
+ // Other puzzle colors should remain default
27
+ expect(result.puzzle.success).toBe(defaultPuzzleTheme.puzzle.success);
28
+ expect(result.puzzle.failure).toBe(defaultPuzzleTheme.puzzle.failure);
29
+ });
30
+
31
+ it("should override all puzzle colors at once", () => {
32
+ const result = mergePuzzleTheme({
33
+ puzzle: {
34
+ success: "rgba(0, 255, 0, 0.5)",
35
+ failure: "rgba(255, 0, 0, 0.5)",
36
+ hint: "rgba(0, 0, 255, 0.5)",
37
+ },
38
+ });
39
+
40
+ expect(result.puzzle.success).toBe("rgba(0, 255, 0, 0.5)");
41
+ expect(result.puzzle.failure).toBe("rgba(255, 0, 0, 0.5)");
42
+ expect(result.puzzle.hint).toBe("rgba(0, 0, 255, 0.5)");
43
+ });
44
+
45
+ it("should override inherited game theme properties", () => {
46
+ const result = mergePuzzleTheme({
47
+ state: { lastMove: "rgba(100, 200, 100, 0.6)" },
48
+ board: { lightSquare: { backgroundColor: "#ffffff" } },
49
+ });
50
+
51
+ expect(result.state.lastMove).toBe("rgba(100, 200, 100, 0.6)");
52
+ expect(result.board.lightSquare).toEqual({ backgroundColor: "#ffffff" });
53
+ // Puzzle colors should remain default
54
+ expect(result.puzzle).toEqual(defaultPuzzleTheme.puzzle);
55
+ });
56
+
57
+ it("should allow overriding both game and puzzle properties", () => {
58
+ const result = mergePuzzleTheme({
59
+ state: { check: "rgba(255, 100, 100, 0.8)" },
60
+ puzzle: { success: "rgba(100, 255, 100, 0.8)" },
61
+ });
62
+
63
+ expect(result.state.check).toBe("rgba(255, 100, 100, 0.8)");
64
+ expect(result.puzzle.success).toBe("rgba(100, 255, 100, 0.8)");
65
+ });
66
+
67
+ it("should preserve unmentioned theme properties", () => {
68
+ const result = mergePuzzleTheme({
69
+ puzzle: { hint: "rgba(0, 255, 255, 0.5)" },
70
+ });
71
+
72
+ expect(result.board).toEqual(defaultPuzzleTheme.board);
73
+ expect(result.state).toEqual(defaultPuzzleTheme.state);
74
+ expect(result.indicators).toEqual(defaultPuzzleTheme.indicators);
75
+ });
76
+ });
@@ -0,0 +1,36 @@
1
+ import React, { createContext, useContext } from "react";
2
+ import type { ChessPuzzleTheme } from "./types";
3
+ import { defaultPuzzleTheme } from "./defaults";
4
+
5
+ /**
6
+ * Context for ChessPuzzle theme
7
+ */
8
+ export const ChessPuzzleThemeContext =
9
+ createContext<ChessPuzzleTheme>(defaultPuzzleTheme);
10
+
11
+ /**
12
+ * Hook to access the current ChessPuzzle theme.
13
+ * Returns the default puzzle theme if no ThemeProvider is present.
14
+ */
15
+ export const useChessPuzzleTheme = (): ChessPuzzleTheme => {
16
+ return useContext(ChessPuzzleThemeContext);
17
+ };
18
+
19
+ export interface PuzzleThemeProviderProps {
20
+ theme: ChessPuzzleTheme;
21
+ children: React.ReactNode;
22
+ }
23
+
24
+ /**
25
+ * Internal provider component used by Puzzle Root when a theme prop is provided.
26
+ */
27
+ export const PuzzleThemeProvider: React.FC<PuzzleThemeProviderProps> = ({
28
+ theme,
29
+ children,
30
+ }) => {
31
+ return (
32
+ <ChessPuzzleThemeContext.Provider value={theme}>
33
+ {children}
34
+ </ChessPuzzleThemeContext.Provider>
35
+ );
36
+ };
@@ -0,0 +1,16 @@
1
+ import { defaultGameTheme } from "@react-chess-tools/react-chess-game";
2
+ import type { ChessPuzzleTheme } from "./types";
3
+
4
+ /**
5
+ * Default theme for ChessPuzzle component.
6
+ * Extends the default game theme with puzzle-specific colors.
7
+ * These values match the original hardcoded colors for backward compatibility.
8
+ */
9
+ export const defaultPuzzleTheme: ChessPuzzleTheme = {
10
+ ...defaultGameTheme,
11
+ puzzle: {
12
+ success: "rgba(172, 206, 89, 0.5)",
13
+ failure: "rgba(201, 52, 48, 0.5)",
14
+ hint: "rgba(27, 172, 166, 0.5)",
15
+ },
16
+ };
@@ -0,0 +1,20 @@
1
+ // Types
2
+ export type {
3
+ ChessPuzzleTheme,
4
+ PuzzleStateTheme,
5
+ PartialChessPuzzleTheme,
6
+ } from "./types";
7
+
8
+ // Default theme
9
+ export { defaultPuzzleTheme } from "./defaults";
10
+
11
+ // Utilities
12
+ export { mergePuzzleTheme } from "./utils";
13
+
14
+ // Context and hook
15
+ export {
16
+ ChessPuzzleThemeContext,
17
+ useChessPuzzleTheme,
18
+ PuzzleThemeProvider,
19
+ } from "./context";
20
+ export type { PuzzleThemeProviderProps } from "./context";
@@ -0,0 +1,29 @@
1
+ import type {
2
+ ChessGameTheme,
3
+ DeepPartial,
4
+ } from "@react-chess-tools/react-chess-game";
5
+
6
+ /**
7
+ * Puzzle-specific state colors (RGBA color strings)
8
+ */
9
+ export interface PuzzleStateTheme {
10
+ /** Background color for successful moves */
11
+ success: string;
12
+ /** Background color for failed moves */
13
+ failure: string;
14
+ /** Background color for hint squares */
15
+ hint: string;
16
+ }
17
+
18
+ /**
19
+ * Complete theme configuration for ChessPuzzle component.
20
+ * Extends ChessGameTheme with puzzle-specific colors.
21
+ */
22
+ export interface ChessPuzzleTheme extends ChessGameTheme {
23
+ puzzle: PuzzleStateTheme;
24
+ }
25
+
26
+ /**
27
+ * Partial theme for puzzle customization - allows overriding only specific properties
28
+ */
29
+ export type PartialChessPuzzleTheme = DeepPartial<ChessPuzzleTheme>;
@@ -0,0 +1,28 @@
1
+ import { merge } from "lodash";
2
+ import type { ChessPuzzleTheme, PartialChessPuzzleTheme } from "./types";
3
+ import { defaultPuzzleTheme } from "./defaults";
4
+
5
+ /**
6
+ * Deep merges a partial puzzle theme with the default puzzle theme.
7
+ * Allows users to override only specific theme properties while keeping defaults for the rest.
8
+ *
9
+ * @param partialTheme - Partial theme with only the properties to override
10
+ * @returns Complete puzzle theme with overridden properties merged with defaults
11
+ *
12
+ * @example
13
+ * ```typescript
14
+ * const customTheme = mergePuzzleTheme({
15
+ * puzzle: { hint: "rgba(0, 255, 255, 0.5)" }
16
+ * });
17
+ * // Returns full puzzle theme with only hint color changed
18
+ * ```
19
+ */
20
+ export const mergePuzzleTheme = (
21
+ partialTheme?: PartialChessPuzzleTheme,
22
+ ): ChessPuzzleTheme => {
23
+ if (!partialTheme) {
24
+ return { ...defaultPuzzleTheme };
25
+ }
26
+
27
+ return merge({}, defaultPuzzleTheme, partialTheme);
28
+ };
@@ -1,8 +1,6 @@
1
1
  import { Chess, Move } from "chess.js";
2
- import React from "react";
3
2
  import {
4
3
  getOrientation,
5
- isClickableElement,
6
4
  getCustomSquareStyles,
7
5
  stringToMove,
8
6
  Puzzle,
@@ -40,21 +38,6 @@ describe("Puzzle Utilities", () => {
40
38
  });
41
39
  });
42
40
 
43
- describe("isClickableElement", () => {
44
- it("should return true for valid clickable elements", () => {
45
- const clickableElement = React.createElement("button", {
46
- onClick: () => {},
47
- });
48
- expect(isClickableElement(clickableElement)).toBe(true);
49
- });
50
-
51
- it("should return false for non-React elements", () => {
52
- expect(isClickableElement("not an element")).toBe(false);
53
- expect(isClickableElement(null)).toBe(false);
54
- expect(isClickableElement(undefined)).toBe(false);
55
- });
56
- });
57
-
58
41
  describe("getCustomSquareStyles", () => {
59
42
  const game = new Chess();
60
43