Skip to content

Commit

Permalink
Refactor and convert components to TypeScript, fix linting errors, an…
Browse files Browse the repository at this point in the history
…d improve typing (#1207)

* refactor: Convert Histogram component to TypeScript

* refactor: Convert Block.test.jsx to Block.test.tsx

* refactor: Convert HTML component to TypeScript

* fix(lint): Fix several linting errors

* refactor: Add unit tests for Reload component

* refactor: Convert Playback component to TypeScript

* refactor: Convert Preload component to TypeScript

* refactor: Convert webAudio.js to TypeScript

* refactor: Add TrialConfig interface for trial configuration settings

* refactor: Add Playback.ts for defining playback types and interfaces

* refactor: Improve typing of several components & utilities

* chore: Convert Playback test to TypeScript

* refactor: Update PlaybackArgs interface in Playback.ts

* refactor: Convert `Multiplayer` component to TypeScript

* refactor: Convert `PlayCard` to TypeScript and centralize `Card` interface

* refactor: Convert `ImagePlayer` component to TypeScript

* fix(lint): Fix linting issues
  • Loading branch information
drikusroor authored Aug 9, 2024
1 parent 5a7dd25 commit 2d6c594
Show file tree
Hide file tree
Showing 27 changed files with 741 additions and 158 deletions.
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import React from 'react';
import { Route, MemoryRouter, Routes } from 'react-router-dom';
import { fireEvent, render, screen, waitFor } from '@testing-library/react';
import { beforeEach, vi } from 'vitest';
Expand Down
3 changes: 2 additions & 1 deletion frontend/src/components/FeedbackForm/FeedbackForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,15 @@ import { useState, useRef } from "react";
import Question from "../Question/Question";
import Button from "../Button/Button";
import IQuestion from "@/types/Question";
import { OnResultType } from "@/hooks/useResultHandler";

interface FeedbackFormProps {
formActive: boolean;
form: IQuestion[];
buttonLabel: string;
skipLabel: string;
isSkippable: boolean;
onResult: (result: any) => void;
onResult: OnResultType
emphasizeTitle?: boolean;
}

Expand Down
82 changes: 82 additions & 0 deletions frontend/src/components/HTML/HTML.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import { describe, it, expect } from 'vitest';
import { render } from '@testing-library/react';
import HTML from './HTML';

describe('HTML', () => {
it('renders the HTML content correctly', () => {
const htmlContent = '<p>Test content</p>';
const { container } = render(<HTML body={htmlContent} />);
expect(container.innerHTML).to.include(htmlContent);
});

it('applies the default inner className', () => {
const { container } = render(<HTML body="<div>Test</div>" />);
const innerDiv = container.querySelector('.html-content');

if (!innerDiv) {
throw new Error('Inner div not found');
}

expect(innerDiv.classList.contains('text-center')).toBe(true);
expect(innerDiv.classList.contains('pb-3')).toBe(true);
});

it('applies a custom inner className', () => {
const customClass = 'custom-class';
const { container } = render(<HTML body="<div>Test</div>" innerClassName={customClass} />);
const innerDiv = container.querySelector('.html-content');

if (!innerDiv) {
throw new Error('Inner div not found');
}

expect(innerDiv.classList.contains(customClass)).toBe(true);
expect(innerDiv.classList.contains('text-center')).toBe(false);
expect(innerDiv.classList.contains('pb-3')).toBe(false);
});

it('renders complex HTML content', () => {
const complexHTML = `
<div>
<h1>Title</h1>
<p>Paragraph <strong>with bold text</strong></p>
<ul>
<li>Item 1</li>
<li>Item 2</li>
</ul>
</div>
`;
const { container } = render(<HTML body={complexHTML} />);
expect(container.innerHTML).to.include(complexHTML);
});

it('renders the outer aha__HTML class', () => {
const { container } = render(<HTML body="<div>Test</div>" />);
const outerDiv = container.firstChild;

if (!outerDiv) {
throw new Error('Outer div not found');
}

expect(outerDiv.classList.contains('aha__HTML')).toBe(true);
});

it('handles empty body content', () => {
const { container } = render(<HTML body="" />);
const innerDiv = container.querySelector('.html-content');

if (!innerDiv) {
throw new Error('Inner div not found');
}

expect(innerDiv.innerHTML).to.equal('');
});

it('handles TrustedHTML type for body prop', () => {
const trustedHTML = {
toString: () => '<p>Trusted HTML</p>',
} as TrustedHTML;
const { container } = render(<HTML body={trustedHTML} />);
expect(container.innerHTML).to.include('<p>Trusted HTML</p>');
});
});
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
import React from "react";
import classNames from "classnames";

interface HTMLProps {
body: string | TrustedHTML;
innerClassName?: string;
}

/** HTML is an block view, that shows custom HTML and a Form */
const HTML = ({ body, innerClassName = "text-center pb-3" }) => {
const HTML = ({ body, innerClassName = "text-center pb-3" }: HTMLProps) => {

return (
<div className={classNames("aha__HTML")}>
Expand Down
105 changes: 105 additions & 0 deletions frontend/src/components/Histogram/Histogram.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { render } from '@testing-library/react';
import Histogram from './Histogram';

describe('Histogram', () => {
beforeEach(() => {
vi.useFakeTimers();
});

it('renders the correct number of bars', () => {
const { container } = render(<Histogram bars={5} />);
const bars = container.querySelectorAll('.aha__histogram > div');
expect(bars.length).to.equal(5);
});

it('applies the correct spacing between bars', () => {
const { container } = render(<Histogram bars={3} spacing={10} />);
const bars = container.querySelectorAll('.aha__histogram > div');
expect(getComputedStyle(bars[0]).marginRight).to.equal('10px');
expect(getComputedStyle(bars[1]).marginRight).to.equal('10px');
expect(getComputedStyle(bars[2]).marginRight).to.equal('0px');
});

it('applies the correct margin and background color', () => {
const { container } = render(
<Histogram marginLeft={20} marginTop={10} backgroundColor="red" />
);
const histogram = container.querySelector('.aha__histogram');

if (!histogram) {
throw new Error('Histogram not found');
}

expect(getComputedStyle(histogram).marginLeft).to.equal('20px');
expect(getComputedStyle(histogram).marginTop).to.equal('10px');
expect(getComputedStyle(histogram).backgroundColor).to.equal('red');
});

it('applies the correct border radius', () => {
const { container } = render(<Histogram borderRadius="5px" />);
const histogram = container.querySelector('.aha__histogram');

if (!histogram) {
throw new Error('Histogram not found');
}

expect(getComputedStyle(histogram).borderRadius).to.equal('5px');
});

it('has active class when running', () => {
const { container } = render(<Histogram running={true} />);
const histogram = container.querySelector('.aha__histogram');

if (!histogram) {
throw new Error('Histogram not found');
}

expect(histogram.classList.contains('active')).toBe(true);
});

it('does not have active class when not running', () => {
const { container } = render(<Histogram running={false} />);
const histogram = container.querySelector('.aha__histogram');

if (!histogram) {
throw new Error('Histogram not found');
}

expect(histogram.classList.contains('active')).toBe(false);
});

it('updates bar heights when running', async () => {
const { container, rerender } = render(<Histogram running={true} />);
const getHeights = () => Array.from(container.querySelectorAll('.aha__histogram > div')).map(
(bar) => bar.style.height
);

const initialHeights = getHeights();

// Advance timer and force re-render
vi.advanceTimersByTime(100);
rerender(<Histogram running={true} />);

const updatedHeights = getHeights();

expect(initialHeights).not.to.deep.equal(updatedHeights);
});

it('does not update bar heights when not running', () => {
const { container, rerender } = render(<Histogram running={false} />);
const getHeights = () => Array.from(container.querySelectorAll('.aha__histogram > div')).map(
(bar) => bar.style.height
);

const initialHeights = getHeights();

// Advance timer and force re-render
vi.advanceTimersByTime(100);
rerender(<Histogram running={false} />);

const updatedHeights = getHeights();

expect(initialHeights).to.deep.equal(updatedHeights);
});
});
Original file line number Diff line number Diff line change
@@ -1,7 +1,18 @@
import React, { useEffect, useState } from "react";
import { useEffect, useState } from "react";
import classNames from "classnames";

// Histogram with random bar movement for decoration
interface HistogramProps {
bars?: number;
spacing?: number;
interval?: number;
running?: boolean;
marginLeft?: number;
marginTop?: number;
backgroundColor?: string;
borderRadius?: string;
}

/** Histogram with random bar movement for decoration */
const Histogram = ({
bars = 7,
spacing = 6,
Expand All @@ -11,7 +22,7 @@ const Histogram = ({
marginTop = 0,
backgroundColor = undefined,
borderRadius = '0.15rem',
}) => {
}: HistogramProps) => {
const [pulse, setPulse] = useState(true);

useEffect(() => {
Expand Down Expand Up @@ -40,7 +51,7 @@ const Histogram = ({
return (
<div
className={classNames("aha__histogram", { active: running })}
style={{ height: '100%', marginLeft, marginTop, backgroundColor, width: '100%', borderRadius: borderRadius, border: backgroundColor? `10px solid ${backgroundColor}` : undefined}}
style={{ height: '100%', marginLeft, marginTop, backgroundColor, width: '100%', borderRadius: borderRadius, border: backgroundColor ? `10px solid ${backgroundColor}` : undefined }}
>
{_bars}
</div>
Expand Down
19 changes: 4 additions & 15 deletions frontend/src/components/MatchingPairs/MatchingPairs.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import React, { useRef, useState } from "react";
import { useRef, useState } from "react";
import classNames from "classnames";

import { scoreIntermediateResult } from "../../API";
import useBoundStore from "@/util/stores";

import PlayCard from "./PlayCard";
import Section from "@/types/Section";
import { Card } from "@/types/Section";
import Session from "@/types/Session";
import Participant from "@/types/Participant";

Expand All @@ -15,17 +15,6 @@ export const SCORE_FEEDBACK_DISPLAY = {
HIDDEN: 'hidden',
}

interface Card extends Section {
turned: boolean;
inactive: boolean;
matchClass: string;
seen: boolean;
noevents: boolean;
boardposition: number;
timestamp: number;
response_interval_ms: number | string;
}

interface MatchingPairsProps {
playSection: (index: number) => void;
sections: Card[];
Expand All @@ -39,8 +28,8 @@ interface MatchingPairsProps {

const MatchingPairs = ({
playSection,
// technically these are Sections, but we're adding some extra properties to them in a hacky way,
// which should be fixed in the future
/** FIXME: technically these are Sections, but we're adding some extra properties to them in a hacky way,
// which should be fixed in the future */
sections,
playerIndex,
showAnimation,
Expand Down
Loading

0 comments on commit 2d6c594

Please sign in to comment.