From a8eba37113c666d059ba0d64af944525428fec4c Mon Sep 17 00:00:00 2001
From: Sebastian Karlsson <sebka991@student.liu.se>
Date: Mon, 24 May 2021 12:57:52 +0000
Subject: [PATCH] Resolve "Comment presentation editor"

---
 .../PresentationEditorPage.tsx                | 27 ++++++++++++--
 .../components/BackgroundImageSelect.tsx      | 35 ++++++++++++-------
 .../components/CompetitionSettings.tsx        | 15 ++++++--
 .../components/ImageComponentDisplay.tsx      |  6 ++++
 .../components/QuestionComponentDisplay.tsx   |  6 ++--
 .../components/RndComponent.tsx               | 33 ++++++++++++++++-
 .../components/SettingsPanel.tsx              |  7 ++++
 .../components/SlideDisplay.tsx               | 15 +++++++-
 .../components/SlideSettings.tsx              |  7 +++-
 .../presentationEditor/components/Teams.tsx   | 13 +++++++
 .../components/TextComponentDisplay.tsx       |  6 ++++
 .../components/TextComponentEdit.tsx          | 10 ++++++
 .../slideSettingsComponents/Texts.tsx         | 11 ++++++
 .../slideSettingsComponents/Timer.tsx         | 13 ++++++-
 .../presentationEditor/components/styled.tsx  |  3 ++
 15 files changed, 183 insertions(+), 24 deletions(-)

diff --git a/client/src/pages/presentationEditor/PresentationEditorPage.tsx b/client/src/pages/presentationEditor/PresentationEditorPage.tsx
index 9b040b9d..d67fc328 100644
--- a/client/src/pages/presentationEditor/PresentationEditorPage.tsx
+++ b/client/src/pages/presentationEditor/PresentationEditorPage.tsx
@@ -1,3 +1,10 @@
+/**
+ * This file contains the PresentationEditorPage function, which returns the presentation editor page component.
+ * This component is used when editing a presentation, and allows creating, modifying and deleting
+ * slides, questions, text components, image components, teams, and any other data relating to a competition.
+ *
+ */
+
 import { Button, ButtonGroup, CircularProgress, Divider, Menu, MenuItem } from '@material-ui/core'
 import CssBaseline from '@material-ui/core/CssBaseline'
 import ListItemText from '@material-ui/core/ListItemText'
@@ -48,9 +55,11 @@ interface CompetitionParams {
   competitionId: string
 }
 
+/** Creates and renders the presentation editor page */
 const PresentationEditorPage: React.FC = () => {
   const { competitionId }: CompetitionParams = useParams()
   const dispatch = useAppDispatch()
+  //Available state variables:
   const [sortedSlides, setSortedSlides] = useState<RichSlide[]>([])
   const activeSlideId = useAppSelector((state) => state.editor.activeSlideId)
   const activeViewTypeId = useAppSelector((state) => state.editor.activeViewTypeId)
@@ -66,24 +75,28 @@ const PresentationEditorPage: React.FC = () => {
     setSortedSlides(competition.slides.sort((a, b) => (a.order > b.order ? 1 : -1)))
   }, [competition])
 
+  /** Sets active slide ID and updates the state */
   const setActiveSlideId = (id: number) => {
     dispatch(setEditorSlideId(id))
     dispatch(getEditorCompetition(competitionId))
   }
 
+  /** Creates API call to create a new slide in the database, and updates the state */
   const createNewSlide = async () => {
     await axios.post(`/api/competitions/${competitionId}/slides`, { title: 'Ny sida' })
     dispatch(getEditorCompetition(competitionId))
   }
 
+  /** State used by the right-click context menu */
   const [contextState, setContextState] = React.useState<{
     mouseX: null | number
     mouseY: null | number
     slideId: null | number
   }>(initialState)
 
+  /** Shows context menu when right clicking a slide in the slide list */
   const handleRightClick = (event: React.MouseEvent<HTMLDivElement>, slideId: number) => {
-    event.preventDefault()
+    event.preventDefault() //Prevents the standard browser context menu from opening
     setContextState({
       mouseX: event.clientX - 2,
       mouseY: event.clientY - 4,
@@ -91,16 +104,19 @@ const PresentationEditorPage: React.FC = () => {
     })
   }
 
+  /** Closes the context menu */
   const handleClose = () => {
     setContextState(initialState)
   }
 
+  /** Creates API call to remove a slide from the database, updates the state, and closes the menu */
   const handleRemoveSlide = async () => {
     await axios.delete(`/api/competitions/${competitionId}/slides/${contextState.slideId}`)
     dispatch(getEditorCompetition(competitionId))
     setContextState(initialState)
   }
 
+  /** Creates API call to duplicate a slide in the database, updates the state, and closes the menu */
   const handleDuplicateSlide = async () => {
     await axios.post(`/api/competitions/${competitionId}/slides/${contextState.slideId}/copy`)
     dispatch(getEditorCompetition(competitionId))
@@ -109,6 +125,8 @@ const PresentationEditorPage: React.FC = () => {
 
   const viewTypes = useAppSelector((state) => state.types.viewTypes)
   const [activeViewTypeName, setActiveViewTypeName] = useState('Audience')
+
+  /** Changes active view type */
   const changeView = (clickedViewTypeName: string) => {
     setActiveViewTypeName(clickedViewTypeName)
     const clickedViewTypeId = viewTypes.find((viewType) => viewType.name === clickedViewTypeName)?.id
@@ -118,8 +136,9 @@ const PresentationEditorPage: React.FC = () => {
     dispatch(getEditorCompetition(competitionId))
   }
 
+  /** Changes slide order */
   const onDragEnd = async (result: DropResult) => {
-    // dropped outside the list or same place
+    // if dropped outside the list or same place, do nothing
     if (!result.destination || result.destination.index === result.source.index) {
       return
     }
@@ -138,6 +157,7 @@ const PresentationEditorPage: React.FC = () => {
   return (
     <PresentationEditorContainer>
       <CssBaseline />
+      {/** Top toolbar */}
       <AppBarEditor $leftDrawerWidth={leftDrawerWidth} $rightDrawerWidth={rightDrawerWidth} position="fixed">
         <ToolBarContainer>
           <Button component={Link} to="/admin/competition-manager" style={{ padding: 0 }}>
@@ -165,6 +185,7 @@ const PresentationEditorPage: React.FC = () => {
           </ButtonGroup>
         </ToolBarContainer>
       </AppBarEditor>
+      {/** Left slide list */}
       <LeftDrawer $leftDrawerWidth={leftDrawerWidth} $rightDrawerWidth={undefined} variant="permanent" anchor="left">
         <FillLeftContainer $leftDrawerWidth={leftDrawerWidth} $rightDrawerWidth={undefined}>
           <ToolbarMargin />
@@ -208,6 +229,7 @@ const PresentationEditorPage: React.FC = () => {
         </FillLeftContainer>
       </LeftDrawer>
       <ToolbarMargin />
+      {/** Right settings panel */}
       <RightDrawer $leftDrawerWidth={undefined} $rightDrawerWidth={rightDrawerWidth} variant="permanent" anchor="right">
         <FillRightContainer $leftDrawerWidth={undefined} $rightDrawerWidth={rightDrawerWidth}>
           <RightPanelScroll>
@@ -227,6 +249,7 @@ const PresentationEditorPage: React.FC = () => {
           <SlideDisplay variant="editor" activeViewTypeId={activeViewTypeId} />
         </InnerContent>
       </Content>
+      {/** Context menu which opens when right clicking a slide in the slide list */}
       <Menu
         keepMounted
         open={contextState.mouseY !== null}
diff --git a/client/src/pages/presentationEditor/components/BackgroundImageSelect.tsx b/client/src/pages/presentationEditor/components/BackgroundImageSelect.tsx
index 14dbaa63..eed166ac 100644
--- a/client/src/pages/presentationEditor/components/BackgroundImageSelect.tsx
+++ b/client/src/pages/presentationEditor/components/BackgroundImageSelect.tsx
@@ -1,28 +1,34 @@
+/**
+ * This file contains the BackgroundImageSelect function, which returns a component used to select a background image.
+ * This component is used to set a background for either the entire competition, or for a specific slide.
+ * It is used in CompetitionSettings and in SlideSettings.
+ */
 import { ListItem, ListItemText, Typography } from '@material-ui/core'
-import React, { useState } from 'react'
+import CloseIcon from '@material-ui/icons/Close'
+import axios from 'axios'
+import React from 'react'
+import { getEditorCompetition } from '../../../actions/editor'
 import { useAppDispatch, useAppSelector } from '../../../hooks'
+import { uploadFile } from '../../../utils/uploadImage'
 import {
-  AddButton,
   AddBackgroundButton,
+  AddButton,
   Center,
   HiddenInput,
-  ImportedImage,
-  SettingsList,
   ImageNameText,
   ImageTextContainer,
+  ImportedImage,
+  SettingsList,
 } from './styled'
-import CloseIcon from '@material-ui/icons/Close'
-import axios from 'axios'
-import { Media } from '../../../interfaces/ApiModels'
-import { getEditorCompetition } from '../../../actions/editor'
-import { uploadFile } from '../../../utils/uploadImage'
 
 type BackgroundImageSelectProps = {
   variant: 'competition' | 'slide'
 }
 
+/** Creates and renders a background image selection component */
 const BackgroundImageSelect = ({ variant }: BackgroundImageSelectProps) => {
   const activeSlideId = useAppSelector((state) => state.editor.activeSlideId)
+  /** Gets a background image from either the competition state or the slide state, depending on variant */
   const backgroundImage = useAppSelector((state) => {
     if (variant === 'competition') return state.editor.competition.background_image
     else return state.editor.competition.slides.find((slide) => slide.id === activeSlideId)?.background_image
@@ -30,8 +36,8 @@ const BackgroundImageSelect = ({ variant }: BackgroundImageSelectProps) => {
   const competitionId = useAppSelector((state) => state.editor.competition.id)
   const dispatch = useAppDispatch()
 
+  /** Creates a new background image component for the competition or slide on the database using API call. */
   const updateBackgroundImage = async (mediaId: number) => {
-    // Creates a new image component on the database using API call.
     if (variant === 'competition') {
       await axios
         .put(`/api/competitions/${competitionId}`, { background_image_id: mediaId })
@@ -49,8 +55,8 @@ const BackgroundImageSelect = ({ variant }: BackgroundImageSelectProps) => {
     }
   }
 
+  /** Removes background image media and from competition using API calls. */
   const removeBackgroundImage = async () => {
-    // Removes background image media and from competition using API calls.
     await axios.delete(`/api/media/images/${backgroundImage?.id}`).catch(console.log)
     if (variant === 'competition') {
       await axios
@@ -69,9 +75,10 @@ const BackgroundImageSelect = ({ variant }: BackgroundImageSelectProps) => {
     }
   }
 
+  /** Reads the selected image file and uploads it to the server.
+   * Creates a new background image component containing the file.
+   */
   const handleFileSelected = async (e: React.ChangeEvent<HTMLInputElement>) => {
-    // Reads the selected image file and uploads it to the server.
-    // Creates a new image component containing the file.
     if (e.target.files !== null && e.target.files[0]) {
       const files = Array.from(e.target.files)
       const file = files[0]
@@ -86,6 +93,7 @@ const BackgroundImageSelect = ({ variant }: BackgroundImageSelectProps) => {
 
   return (
     <SettingsList>
+      {/** Choose an image */}
       {!backgroundImage && (
         <ListItem button style={{ padding: 0 }}>
           <HiddenInput
@@ -102,6 +110,7 @@ const BackgroundImageSelect = ({ variant }: BackgroundImageSelectProps) => {
           </AddBackgroundButton>
         </ListItem>
       )}
+      {/** Display thumbnail for chosen image, and remove image */}
       {backgroundImage && (
         <>
           <ListItem divider>
diff --git a/client/src/pages/presentationEditor/components/CompetitionSettings.tsx b/client/src/pages/presentationEditor/components/CompetitionSettings.tsx
index 64b4a820..bcd77da5 100644
--- a/client/src/pages/presentationEditor/components/CompetitionSettings.tsx
+++ b/client/src/pages/presentationEditor/components/CompetitionSettings.tsx
@@ -1,3 +1,8 @@
+/**
+ * This file contains the CompetitionSettings function, which returns the right hand side competition settings panel.
+ * This component is used to change settings which apply to the entire competition.
+ * It is contained in the SettingsPanel, alongside the SlideSettings.
+ */
 import { Divider, FormControl, InputLabel, ListItem, MenuItem, Select, TextField, Typography } from '@material-ui/core'
 import axios from 'axios'
 import React, { useState } from 'react'
@@ -13,6 +18,7 @@ interface CompetitionParams {
   competitionId: string
 }
 
+/** Creates and renders a competition settings component */
 const CompetitionSettings: React.FC = () => {
   const { competitionId }: CompetitionParams = useParams()
   const [nameErrorText, setNameErrorText] = useState<string | undefined>(undefined)
@@ -20,6 +26,7 @@ const CompetitionSettings: React.FC = () => {
   const competition = useAppSelector((state) => state.editor.competition)
   const cities = useAppSelector((state) => state.cities.cities)
 
+  /** Sets the name of the competition in the database */
   const updateCompetitionName = async (event: React.ChangeEvent<HTMLTextAreaElement | HTMLInputElement>) => {
     await axios
       .put(`/api/competitions/${competitionId}`, { name: event.target.value })
@@ -32,6 +39,7 @@ const CompetitionSettings: React.FC = () => {
       })
   }
 
+  /** Sets the city of the competition in the database */
   const updateCompetitionCity = async (city: City) => {
     await axios
       .put(`/api/competitions/${competitionId}`, { city_id: city.id })
@@ -53,6 +61,7 @@ const CompetitionSettings: React.FC = () => {
   return (
     <PanelContainer>
       <SettingsList>
+        {/** Text field for setting the competition name */}
         <FirstItem>
           <ListItem>
             <TextField
@@ -68,7 +77,7 @@ const CompetitionSettings: React.FC = () => {
           </ListItem>
         </FirstItem>
         <Divider />
-
+        {/** Set region */}
         <ListItem>
           <FormControl fullWidth variant="outlined">
             <InputLabel>Region</InputLabel>
@@ -86,9 +95,9 @@ const CompetitionSettings: React.FC = () => {
           </FormControl>
         </ListItem>
       </SettingsList>
-
+      {/** Set teams */}
       <Teams competitionId={competitionId} />
-
+      {/** Set background image */}
       <BackgroundImageSelect variant="competition" />
     </PanelContainer>
   )
diff --git a/client/src/pages/presentationEditor/components/ImageComponentDisplay.tsx b/client/src/pages/presentationEditor/components/ImageComponentDisplay.tsx
index 273748f3..6afe7121 100644
--- a/client/src/pages/presentationEditor/components/ImageComponentDisplay.tsx
+++ b/client/src/pages/presentationEditor/components/ImageComponentDisplay.tsx
@@ -1,3 +1,8 @@
+/**
+ * This file contains the ImageComponentDisplay function, which returns an image component.
+ * This component is used to display images in an active competition presentation or in the editor.
+ * When used in the editor, the image component is wrapped in an Rnd component, to make it editable.
+ */
 import React from 'react'
 import { ImageComponent } from '../../../interfaces/ApiModels'
 
@@ -7,6 +12,7 @@ type ImageComponentProps = {
   height: number
 }
 
+/** Creates and renders and image component */
 const ImageComponentDisplay = ({ component, width, height }: ImageComponentProps) => {
   return (
     <img
diff --git a/client/src/pages/presentationEditor/components/QuestionComponentDisplay.tsx b/client/src/pages/presentationEditor/components/QuestionComponentDisplay.tsx
index 75690ea6..e8b220f3 100644
--- a/client/src/pages/presentationEditor/components/QuestionComponentDisplay.tsx
+++ b/client/src/pages/presentationEditor/components/QuestionComponentDisplay.tsx
@@ -25,7 +25,9 @@ type QuestionComponentProps = {
   currentSlideId?: number
 }
 
+/** Creates and renders a question component */
 const QuestionComponentDisplay = ({ variant, currentSlideId }: QuestionComponentProps) => {
+  /** Gets the slide from the relevant state (presentation or editor) */
   const activeSlide = useAppSelector((state) => {
     if (variant === 'presentation' && currentSlideId)
       return state.presentation.competition.slides.find((slide) => slide.id === currentSlideId)
@@ -35,7 +37,7 @@ const QuestionComponentDisplay = ({ variant, currentSlideId }: QuestionComponent
   })
 
   const timer = activeSlide?.timer
-  const total_score = activeSlide?.questions[0].total_score
+  const total_score = activeSlide?.questions[0].total_score //[0] is because each slide can only have one question currently.
   const questionName = activeSlide?.questions[0].name
 
   const questionTypeId = activeSlide?.questions[0].type_id
@@ -110,7 +112,7 @@ const QuestionComponentDisplay = ({ variant, currentSlideId }: QuestionComponent
         </div>
       </AppBar>
       <Divider />
-      {getAlternatives()}
+      {getAlternatives() /*This displays the actual alternatives for the question*/}
     </Card>
   )
 }
diff --git a/client/src/pages/presentationEditor/components/RndComponent.tsx b/client/src/pages/presentationEditor/components/RndComponent.tsx
index 3b40ad74..740f982a 100644
--- a/client/src/pages/presentationEditor/components/RndComponent.tsx
+++ b/client/src/pages/presentationEditor/components/RndComponent.tsx
@@ -1,3 +1,8 @@
+/**
+ * This file contains the RndComponent function, which returns a resizable and draggable component.
+ * This component is used by text, image and question components in the presentation editor.
+ * It uses the React-Rnd library.
+ */
 import { Card, IconButton, Menu, MenuItem, Tooltip } from '@material-ui/core'
 import axios from 'axios'
 import React, { useEffect, useState } from 'react'
@@ -21,9 +26,12 @@ type RndComponentProps = {
   scale: number
 }
 
+/** State for right click menu; closed initially */
 const initialMenuState = { menuIsOpen: false, mouseX: null, mouseY: null, componentId: null }
 
+/** Creates and renders a resizable and draggable component */
 const RndComponent = ({ component, width, height, scale }: RndComponentProps) => {
+  //States
   const [hover, setHover] = useState(false)
   const [currentPos, setCurrentPos] = useState<Position>({ x: component.x, y: component.y })
   const [currentSize, setCurrentSize] = useState<Size>({ w: component.w, h: component.h })
@@ -33,6 +41,7 @@ const RndComponent = ({ component, width, height, scale }: RndComponentProps) =>
   const typeName = useAppSelector(
     (state) => state.types.componentTypes.find((componentType) => componentType.id === component.type_id)?.name
   )
+  /** State for right click menu */
   const [menuState, setMenuState] = useState<{
     menuIsOpen: boolean
     mouseX: null | number
@@ -41,30 +50,39 @@ const RndComponent = ({ component, width, height, scale }: RndComponentProps) =>
   }>(initialMenuState)
   const dispatch = useAppDispatch()
 
+  /** Sets position of the component in the database */
   const handleUpdatePos = (pos: Position) => {
     axios.put(`/api/competitions/${competitionId}/slides/${slideId}/components/${component.id}`, {
       x: pos.x,
       y: pos.y,
     })
   }
+
+  /** Sets size of the component in the database */
   const handleUpdateSize = (size: Size) => {
     axios.put(`/api/competitions/${competitionId}/slides/${slideId}/components/${component.id}`, {
       w: size.w,
       h: size.h,
     })
   }
+
+  /** Positions the component centered horizontally */
   const handleCenterHorizontal = () => {
     const centerX = width / (2 * scale) - currentSize.w / 2
     setCurrentPos({ x: centerX, y: currentPos.y })
     handleUpdatePos({ x: centerX, y: currentPos.y })
   }
+
+  /** Positions the component centered vertically */
   const handleCenterVertical = () => {
     const centerY = height / (2 * scale) - currentSize.h / 2
     setCurrentPos({ x: currentPos.x, y: centerY })
     handleUpdatePos({ x: currentPos.x, y: centerY })
   }
+
+  /** Opens right click context menu */
   const handleRightClick = (event: React.MouseEvent<HTMLDivElement>, componentId: number) => {
-    event.preventDefault()
+    event.preventDefault() //Prevents browser-native context menu from being opened
     setMenuState({
       menuIsOpen: true,
       mouseX: event.clientX - 2,
@@ -72,9 +90,13 @@ const RndComponent = ({ component, width, height, scale }: RndComponentProps) =>
       componentId: componentId,
     })
   }
+
+  /** Closes right click context menu */
   const handleCloseMenu = () => {
     setMenuState(initialMenuState)
   }
+
+  /** Creates a copy of the component in the database. The copy will be placed in the supplied slide type (e.g. viewer, participant). */
   const handleDuplicateComponent = async (viewTypeId: number) => {
     console.log('Duplicate')
     await axios
@@ -85,6 +107,8 @@ const RndComponent = ({ component, width, height, scale }: RndComponentProps) =>
       .catch(console.log)
     setMenuState(initialMenuState)
   }
+
+  /** Deletes the component from the database */
   const handleRemoveComponent = async () => {
     console.log('Remove')
     await axios
@@ -94,6 +118,9 @@ const RndComponent = ({ component, width, height, scale }: RndComponentProps) =>
     setMenuState(initialMenuState)
   }
 
+  /** Handles key presses:
+   * -Holding down shift retains the aspect ratio of the component when resizing
+   */
   useEffect(() => {
     const downHandler = (ev: KeyboardEvent) => {
       if (ev.key === 'Shift') setShiftPressed(true)
@@ -109,6 +136,7 @@ const RndComponent = ({ component, width, height, scale }: RndComponentProps) =>
     }
   }, [])
 
+  /** Renders the contained text, image och question component */
   const renderInnerComponent = () => {
     switch (component.type_id) {
       case ComponentTypes.Text:
@@ -139,6 +167,7 @@ const RndComponent = ({ component, width, height, scale }: RndComponentProps) =>
   }
 
   return (
+    /** Renders an Rnd component from the react-rnd library */
     <Rnd
       minWidth={75 * scale}
       minHeight={75 * scale}
@@ -171,6 +200,7 @@ const RndComponent = ({ component, width, height, scale }: RndComponentProps) =>
         setCurrentPos({ x: position.x / scale, y: position.y / scale })
       }}
     >
+      {/** Buttons for centering the component */}
       {hover && (
         <Card elevation={6} style={{ position: 'absolute', zIndex: 10 }}>
           <Tooltip title="Centrera horisontellt">
@@ -182,6 +212,7 @@ const RndComponent = ({ component, width, height, scale }: RndComponentProps) =>
         </Card>
       )}
       {renderInnerComponent()}
+      {/** Context menu */}
       <Menu
         keepMounted
         open={menuState.menuIsOpen}
diff --git a/client/src/pages/presentationEditor/components/SettingsPanel.tsx b/client/src/pages/presentationEditor/components/SettingsPanel.tsx
index 2b214728..fac5eb1e 100644
--- a/client/src/pages/presentationEditor/components/SettingsPanel.tsx
+++ b/client/src/pages/presentationEditor/components/SettingsPanel.tsx
@@ -1,3 +1,8 @@
+/**
+ * This file contains the SettingsPanel function, which returns the right hand side settings panel.
+ * This component contains both the SlideSettings and the CompetitionSettings components.
+ */
+
 import { Tabs } from '@material-ui/core'
 import AppBar from '@material-ui/core/AppBar'
 import React from 'react'
@@ -9,6 +14,7 @@ interface TabPanelProps {
   activeTab: number
 }
 
+/** Returns either the competition settings or the slide settings panel, depending on the selected tab */
 function TabContent(props: TabPanelProps) {
   const { activeTab } = props
   if (activeTab === 0) {
@@ -17,6 +23,7 @@ function TabContent(props: TabPanelProps) {
   return <SlideSettings />
 }
 
+/** Creates and renders the settings panel component */
 const SettingsPanel: React.FC = () => {
   const [activeTab, setActiveTab] = React.useState(0)
   return (
diff --git a/client/src/pages/presentationEditor/components/SlideDisplay.tsx b/client/src/pages/presentationEditor/components/SlideDisplay.tsx
index 05c87d30..26a0b19f 100644
--- a/client/src/pages/presentationEditor/components/SlideDisplay.tsx
+++ b/client/src/pages/presentationEditor/components/SlideDisplay.tsx
@@ -1,3 +1,7 @@
+/**
+ * This file contains the SlideDisplay function, which returns the slide component.
+ * It is used both in the presentation editor and in active competitions.
+ */
 import { Card, Typography } from '@material-ui/core'
 import TimerIcon from '@material-ui/icons/Timer'
 import React, { useEffect, useLayoutEffect, useRef, useState } from 'react'
@@ -16,7 +20,9 @@ type SlideDisplayProps = {
   currentSlideId?: number
 }
 
+/** Creates and renders a slide component */
 const SlideDisplay = ({ variant, activeViewTypeId, currentSlideId }: SlideDisplayProps) => {
+  /** Returns a slide from either the editor or presentation (competition) state */
   const slide = useAppSelector((state) => {
     if (currentSlideId && variant === 'presentation')
       return state.presentation.competition.slides.find((slide) => slide.id === currentSlideId)
@@ -24,11 +30,15 @@ const SlideDisplay = ({ variant, activeViewTypeId, currentSlideId }: SlideDispla
       return state.editor.competition.slides.find((slide) => slide.id === state.editor.activeSlideId)
     return state.presentation.competition.slides.find((slide) => slide.id === state.presentation.activeSlideId)
   })
+
+  /** Returns the number of slides */
   const totalSlides = useAppSelector((state) => {
     if (variant === 'presentation') return state.presentation.competition.slides.length
     return state.editor.competition.slides.length
   })
   const components = slide?.components
+
+  /** Returns the background image */
   const competitionBackgroundImage = useAppSelector((state) => {
     if (variant === 'editor') return state.editor.competition.background_image
     return state.presentation.competition.background_image
@@ -39,12 +49,13 @@ const SlideDisplay = ({ variant, activeViewTypeId, currentSlideId }: SlideDispla
   const editorPaperRef = useRef<HTMLDivElement>(null)
   const [width, setWidth] = useState(0)
   const [height, setHeight] = useState(0)
-  //Makes scale close to 1, 800 height is approxemately for a 1920 by 1080 monitor
+  //Makes scale close to 1, 800 height is approximately for a 1920 by 1080 monitor
   const scale = height / 800
   useEffect(() => {
     dispatch(getTypes())
   }, [])
 
+  /** Resizes the slide if the browser window is resized */
   useLayoutEffect(() => {
     const updateScale = () => {
       if (editorPaperRef.current) {
@@ -56,6 +67,7 @@ const SlideDisplay = ({ variant, activeViewTypeId, currentSlideId }: SlideDispla
     updateScale()
     return () => window.removeEventListener('resize', updateScale)
   }, [])
+
   return (
     <SlideEditorContainer>
       <SlideEditorContainerRatio>
@@ -81,6 +93,7 @@ const SlideDisplay = ({ variant, activeViewTypeId, currentSlideId }: SlideDispla
               draggable={false}
             />
           )}
+          {/** Renders editable components if in the editor, and non-editable if in a competition */}
           {components &&
             components
               .filter((component) => component.view_type_id === activeViewTypeId)
diff --git a/client/src/pages/presentationEditor/components/SlideSettings.tsx b/client/src/pages/presentationEditor/components/SlideSettings.tsx
index 68abd240..8d341d3c 100644
--- a/client/src/pages/presentationEditor/components/SlideSettings.tsx
+++ b/client/src/pages/presentationEditor/components/SlideSettings.tsx
@@ -1,4 +1,6 @@
-/* This file compiles and renders the right hand slide settings bar, under the tab "SIDA".
+/**
+ * This file contains the SlideSettings function, which returns the right hand slide settings bar.
+ * This component is used to edit settings associated to a slide, such as question, image and text components.
  */
 import { Divider } from '@material-ui/core'
 import React from 'react'
@@ -20,6 +22,7 @@ interface CompetitionParams {
   competitionId: string
 }
 
+/** Creates and renders the slide settings component */
 const SlideSettings: React.FC = () => {
   const { competitionId }: CompetitionParams = useParams()
 
@@ -59,10 +62,12 @@ const SlideSettings: React.FC = () => {
         <MatchAlternatives activeSlide={activeSlide} competitionId={competitionId} />
       )}
 
+      {/** Text components */}
       {activeSlide && (
         <Texts activeViewTypeId={activeViewTypeId} activeSlide={activeSlide} competitionId={competitionId} />
       )}
 
+      {/** Image components */}
       {activeSlide && (
         <Images activeViewTypeId={activeViewTypeId} activeSlide={activeSlide} competitionId={competitionId} />
       )}
diff --git a/client/src/pages/presentationEditor/components/Teams.tsx b/client/src/pages/presentationEditor/components/Teams.tsx
index 8e4a47b4..1666fcb8 100644
--- a/client/src/pages/presentationEditor/components/Teams.tsx
+++ b/client/src/pages/presentationEditor/components/Teams.tsx
@@ -1,3 +1,8 @@
+/**
+ * This file contains the Teams function, which returns the part of the editor used to edit teams.
+ * This component is shown in the PresentationEditorPage as a part of CompetitionSettings.
+ */
+
 import {
   Button,
   Dialog,
@@ -31,10 +36,13 @@ type TeamsProps = {
   competitionId: string
 }
 
+/** Creates and renders a teams component */
 const Teams = ({ competitionId }: TeamsProps) => {
   const dispatch = useAppDispatch()
   const competition = useAppSelector((state) => state.editor.competition)
   const [errorActive, setErrorActive] = React.useState(false)
+
+  /** Adds or edits a team connected to the competition in the database, depending on the state */
   const editTeam = async () => {
     if (editTeamState.variant === 'Add') {
       await axios
@@ -44,6 +52,7 @@ const Teams = ({ competitionId }: TeamsProps) => {
         })
         .catch(console.log)
     } else if (editTeamState.team) {
+      // Edit existing team
       await axios
         .put(`/api/competitions/${competitionId}/teams/${editTeamState.team.id}`, { name: selectedTeamName })
         .then(() => {
@@ -62,6 +71,7 @@ const Teams = ({ competitionId }: TeamsProps) => {
     selectedTeamName = event.target.value
   }
 
+  /** Removes the team with teamId=tid */
   const removeTeam = async (tid: number) => {
     await axios
       .delete(`/api/competitions/${competitionId}/teams/${tid}`)
@@ -78,6 +88,7 @@ const Teams = ({ competitionId }: TeamsProps) => {
           <ListItemText primary="Lag" />
         </Center>
       </ListItem>
+      {/** One list item for each team in the competition */}
       {competition.teams &&
         competition.teams.map((team) => (
           <div key={team.id}>
@@ -92,11 +103,13 @@ const Teams = ({ competitionId }: TeamsProps) => {
             </ListItem>
           </div>
         ))}
+      {/** Button to add team */}
       <ListItem button onClick={() => setEditTeamState({ variant: 'Add', open: true })}>
         <Center>
           <AddButton variant="button">Lägg till lag</AddButton>
         </Center>
       </ListItem>
+      {/** Dialog box which opens when adding or editing a team */}
       <Dialog open={editTeamState.open} onClose={() => setEditTeamState({ open: false })}>
         <DialogTitle>
           {editTeamState.variant === 'Edit' && editTeamState.team
diff --git a/client/src/pages/presentationEditor/components/TextComponentDisplay.tsx b/client/src/pages/presentationEditor/components/TextComponentDisplay.tsx
index 5cc87ec9..861e044e 100644
--- a/client/src/pages/presentationEditor/components/TextComponentDisplay.tsx
+++ b/client/src/pages/presentationEditor/components/TextComponentDisplay.tsx
@@ -1,6 +1,12 @@
+/**
+ * This file contains the TextComponentDisplay function, which returns the text component used in competitions.
+ * This component only displays the text, it cannot be edited.
+ * When a text component is displayed in the editor, the file TextComponentEdit is used instead.
+ */
 import React from 'react'
 import { TextComponent } from '../../../interfaces/ApiModels'
 
+/** Creates and renders an image component displaying a text */
 type TextComponentDisplayProps = {
   component: TextComponent
   scale: number
diff --git a/client/src/pages/presentationEditor/components/TextComponentEdit.tsx b/client/src/pages/presentationEditor/components/TextComponentEdit.tsx
index 61dc1284..51ccf87d 100644
--- a/client/src/pages/presentationEditor/components/TextComponentEdit.tsx
+++ b/client/src/pages/presentationEditor/components/TextComponentEdit.tsx
@@ -1,3 +1,9 @@
+/**
+ * This file contains the TextComponentEdit function, which returns the text component used in the editor.
+ * This component is used when editing a text component. It uses TinyMCE.
+ * When a text component is displayed in a competition, the file TextComponentDisplay is used instead.
+ */
+
 import { Editor } from '@tinymce/tinymce-react'
 import axios from 'axios'
 import React, { useEffect, useState } from 'react'
@@ -15,6 +21,7 @@ interface CompetitionParams {
   competitionId: string
 }
 
+/** Creates and renders an editable text component */
 const TextComponentEdit = ({ component }: ImageComponentProps) => {
   const { competitionId }: CompetitionParams = useParams()
   const [content, setContent] = useState('')
@@ -27,6 +34,7 @@ const TextComponentEdit = ({ component }: ImageComponentProps) => {
     setContent(component.text)
   }, [])
 
+  /** Saves the supplied text to the database */
   const handleSaveText = async (newText: string) => {
     setContent(newText)
     if (timerHandle) {
@@ -44,6 +52,7 @@ const TextComponentEdit = ({ component }: ImageComponentProps) => {
     )
   }
 
+  /** Deletes the text component */
   const handleDeleteText = async (componentId: number) => {
     await axios.delete(`/api/competitions/${competitionId}/slides/${activeSlideId}/components/${componentId}`)
     dispatch(getEditorCompetition(competitionId))
@@ -51,6 +60,7 @@ const TextComponentEdit = ({ component }: ImageComponentProps) => {
 
   return (
     <>
+      {/** TinyMCE component */}
       <Editor
         value={content || ''}
         init={{
diff --git a/client/src/pages/presentationEditor/components/slideSettingsComponents/Texts.tsx b/client/src/pages/presentationEditor/components/slideSettingsComponents/Texts.tsx
index dd934e60..4fd7c2d3 100644
--- a/client/src/pages/presentationEditor/components/slideSettingsComponents/Texts.tsx
+++ b/client/src/pages/presentationEditor/components/slideSettingsComponents/Texts.tsx
@@ -1,3 +1,8 @@
+/**
+ * This file contains the Texts function, which returns a component containing text components.
+ * This component is used to edit and add text components in a slide.
+ * It is used in SlideSettings.
+ */
 import { Divider, ListItem, ListItemText } from '@material-ui/core'
 import axios from 'axios'
 import React from 'react'
@@ -14,7 +19,9 @@ type TextsProps = {
   competitionId: string
 }
 
+/** Creates and renders a texts component */
 const Texts = ({ activeViewTypeId, activeSlide, competitionId }: TextsProps) => {
+  /** Gets all text components from the slide state */
   const texts = useAppSelector(
     (state) =>
       state.editor.competition.slides
@@ -23,6 +30,8 @@ const Texts = ({ activeViewTypeId, activeSlide, competitionId }: TextsProps) =>
   )
 
   const dispatch = useAppDispatch()
+
+  /** Adds a new text component to the slide using API call */
   const handleAddText = async () => {
     if (activeSlide) {
       await axios.post(`/api/competitions/${competitionId}/slides/${activeSlide?.id}/components`, {
@@ -43,6 +52,7 @@ const Texts = ({ activeViewTypeId, activeSlide, competitionId }: TextsProps) =>
           <ListItemText primary="Text" />
         </Center>
       </ListItem>
+      {/** Shows list of all text components in the slide */}
       {texts &&
         texts
           .filter((text) => text.view_type_id === activeViewTypeId)
@@ -52,6 +62,7 @@ const Texts = ({ activeViewTypeId, activeSlide, competitionId }: TextsProps) =>
               <Divider />
             </TextCard>
           ))}
+      {/** Button to create new text component */}
       <ListItem button onClick={handleAddText}>
         <Center>
           <AddButton variant="button">Lägg till text</AddButton>
diff --git a/client/src/pages/presentationEditor/components/slideSettingsComponents/Timer.tsx b/client/src/pages/presentationEditor/components/slideSettingsComponents/Timer.tsx
index 1cc012b8..ca5c8c7e 100644
--- a/client/src/pages/presentationEditor/components/slideSettingsComponents/Timer.tsx
+++ b/client/src/pages/presentationEditor/components/slideSettingsComponents/Timer.tsx
@@ -1,3 +1,8 @@
+/**
+ * This file contains the Timer function, which returns the timer component.
+ * This component is used to set the allowed time for a slide.
+ * It is used in SlideSettings.
+ */
 import { ListItem, TextField } from '@material-ui/core'
 import axios from 'axios'
 import React, { useEffect, useState } from 'react'
@@ -11,20 +16,24 @@ type TimerProps = {
   competitionId: string
 }
 
+/** Creates and renders a timer component */
 const Timer = ({ activeSlide, competitionId }: TimerProps) => {
   const maxTime = 1000000 // ms
   const dispatch = useAppDispatch()
   const [timerHandle, setTimerHandle] = useState<number | undefined>(undefined)
+
+  /** Handles changes in the timer text field. Updates the timer every 300 ms */
   const handleChangeTimer = (event: React.ChangeEvent<HTMLTextAreaElement | HTMLInputElement>) => {
     if (timerHandle) {
       clearTimeout(timerHandle)
       setTimerHandle(undefined)
     }
-    //Only updates slide and api 300s after last input was made
+    //Only updates slide and api 300 ms after last input was made
     setTimerHandle(window.setTimeout(() => updateTimer(event), 300))
     setTimer(+event.target.value)
   }
 
+  /** Updates the database with the new timer value.  */
   const updateTimer = async (event: React.ChangeEvent<HTMLTextAreaElement | HTMLInputElement>) => {
     /** If timer value is above the max value, set the timer value to max value to not overflow the server */
     // Sets score to event.target.value if it's between 0 and max
@@ -44,10 +53,12 @@ const Timer = ({ activeSlide, competitionId }: TimerProps) => {
   useEffect(() => {
     setTimer(activeSlide?.timer)
   }, [activeSlide])
+
   return (
     <ListItem>
       <Center>
         <SettingsItemContainer>
+          {/** Text files to change timer value */}
           <TextField
             id="standard-number"
             fullWidth={true}
diff --git a/client/src/pages/presentationEditor/components/styled.tsx b/client/src/pages/presentationEditor/components/styled.tsx
index 106e360e..6f6a868f 100644
--- a/client/src/pages/presentationEditor/components/styled.tsx
+++ b/client/src/pages/presentationEditor/components/styled.tsx
@@ -1,3 +1,6 @@
+/**
+ * This file supplies CSS styles to the presentation editor components.
+ */
 import { Button, Card, List, ListItemText, Tab, TextField, Typography } from '@material-ui/core'
 import styled from 'styled-components'
 
-- 
GitLab