diff --git a/client/modules/IDE/components/Admonition.jsx b/client/modules/IDE/components/Admonition.jsx deleted file mode 100644 index 8d9a6c6db1..0000000000 --- a/client/modules/IDE/components/Admonition.jsx +++ /dev/null @@ -1,22 +0,0 @@ -import React from 'react'; -import PropTypes from 'prop-types'; - -export default function Admonition({ children, title }) { - return ( -
-

- {title} -

- {children} -
- ); -} - -Admonition.propTypes = { - title: PropTypes.string.isRequired, - children: PropTypes.node -}; - -Admonition.defaultProps = { - children: undefined -}; diff --git a/client/modules/IDE/components/Admonition.test.tsx b/client/modules/IDE/components/Admonition.test.tsx new file mode 100644 index 0000000000..38e3fc3e09 --- /dev/null +++ b/client/modules/IDE/components/Admonition.test.tsx @@ -0,0 +1,25 @@ +import React from 'react'; +import { render, screen } from '@testing-library/react'; +import { Admonition } from './Admonition'; + +describe('Admonition Component', () => { + it('renders with the correct title', () => { + render(); + expect(screen.getByText('Important Note')).toBeInTheDocument(); + }); + + it('renders the children content correctly', () => { + render( + +

This is a warning message.

+
+ ); + expect(screen.getByText('This is a warning message.')).toBeInTheDocument(); + }); + + it('renders the title as a bold element', () => { + const { container } = render(); + const strongElement = container.querySelector('strong'); + expect(strongElement).toHaveTextContent('Bold Title'); + }); +}); diff --git a/client/modules/IDE/components/Admonition.tsx b/client/modules/IDE/components/Admonition.tsx new file mode 100644 index 0000000000..07cbaf27fc --- /dev/null +++ b/client/modules/IDE/components/Admonition.tsx @@ -0,0 +1,15 @@ +import React, { ReactNode } from 'react'; + +interface AdmonitionProps { + children?: ReactNode; + title: string; +} + +export const Admonition = ({ children, title }: AdmonitionProps) => ( +
+

+ {title} +

+ {children} +
+); diff --git a/client/modules/IDE/components/FloatingActionButton.stories.jsx b/client/modules/IDE/components/FloatingActionButton.stories.jsx index d80247a0d9..f83ac58a79 100644 --- a/client/modules/IDE/components/FloatingActionButton.stories.jsx +++ b/client/modules/IDE/components/FloatingActionButton.stories.jsx @@ -1,4 +1,4 @@ -import FloatingActionButton from './FloatingActionButton'; +import { FloatingActionButton } from './FloatingActionButton'; export default { title: 'IDE/FloatingActionButton', diff --git a/client/modules/IDE/components/FloatingActionButton.test.tsx b/client/modules/IDE/components/FloatingActionButton.test.tsx new file mode 100644 index 0000000000..4d636907f7 --- /dev/null +++ b/client/modules/IDE/components/FloatingActionButton.test.tsx @@ -0,0 +1,41 @@ +import React from 'react'; +import { render, screen, fireEvent } from '../../../test-utils'; +import { FloatingActionButton } from './FloatingActionButton'; +import { startSketch, stopSketch } from '../actions/ide'; + +jest.mock('../actions/ide', () => ({ + startSketch: jest.fn(() => ({ type: 'START_SKETCH' })), + stopSketch: jest.fn(() => ({ type: 'STOP_SKETCH' })) +})); + +describe('FloatingActionButton', () => { + const defaultProps = { + syncFileContent: jest.fn(), + offsetBottom: 20 + }; + + it('renders PlayIcon when not playing', () => { + render(, { + initialState: { ide: { isPlaying: false } } + }); + // PlayIcon is rendered (SVG) + expect(screen.getByRole('button')).toBeInTheDocument(); + }); + + it('calls syncFileContent and startSketch when clicked and not playing', () => { + render(, { + initialState: { ide: { isPlaying: false } } + }); + fireEvent.click(screen.getByRole('button')); + expect(defaultProps.syncFileContent).toHaveBeenCalled(); + expect(startSketch).toHaveBeenCalled(); + }); + + it('calls stopSketch when clicked and playing', () => { + render(, { + initialState: { ide: { isPlaying: true } } + }); + fireEvent.click(screen.getByRole('button')); + expect(stopSketch).toHaveBeenCalled(); + }); +}); diff --git a/client/modules/IDE/components/FloatingActionButton.jsx b/client/modules/IDE/components/FloatingActionButton.tsx similarity index 71% rename from client/modules/IDE/components/FloatingActionButton.jsx rename to client/modules/IDE/components/FloatingActionButton.tsx index 52d2827523..5a6900c88e 100644 --- a/client/modules/IDE/components/FloatingActionButton.jsx +++ b/client/modules/IDE/components/FloatingActionButton.tsx @@ -2,11 +2,11 @@ import React from 'react'; import styled from 'styled-components'; import classNames from 'classnames'; import { useDispatch, useSelector } from 'react-redux'; -import PropTypes from 'prop-types'; import PlayIcon from '../../../images/triangle-arrow-right.svg'; import StopIcon from '../../../images/stop.svg'; import { prop, remSize } from '../../../theme'; import { startSketch, stopSketch } from '../actions/ide'; +import { RootState } from '../../../reducers'; const Button = styled.button` position: fixed; @@ -38,8 +38,16 @@ const Button = styled.button` } `; -const FloatingActionButton = ({ syncFileContent, offsetBottom }) => { - const isPlaying = useSelector((state) => state.ide.isPlaying); +interface FloatingActionButtonProps { + syncFileContent: () => void; + offsetBottom: number; +} + +export const FloatingActionButton = ({ + syncFileContent, + offsetBottom +}: FloatingActionButtonProps) => { + const isPlaying = useSelector((state: RootState) => state.ide.isPlaying); const dispatch = useDispatch(); return ( @@ -53,17 +61,16 @@ const FloatingActionButton = ({ syncFileContent, offsetBottom }) => { if (!isPlaying) { syncFileContent(); dispatch(startSketch()); - } else dispatch(stopSketch()); + } else { + dispatch(stopSketch()); + } }} > - {isPlaying ? : } + {isPlaying ? ( +