Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor and convert components to TypeScript, fix linting errors, and improve typing #1207

Merged
merged 17 commits into from
Aug 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading