Skip to content
Closed
Show file tree
Hide file tree
Changes from 6 commits
Commits
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
8 changes: 8 additions & 0 deletions Sprint-3/slideshow/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
*.pdf
*.docx
prep/
package-lock.json
../package-lock.json
../../Sprint-1/package-lock.json
../../Sprint-2/package-lock.json
../alarmclock/package.json
Binary file added Sprint-3/slideshow/images/puppy1.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added Sprint-3/slideshow/images/puppy2.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added Sprint-3/slideshow/images/puppy3.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added Sprint-3/slideshow/images/puppy4.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
24 changes: 18 additions & 6 deletions Sprint-3/slideshow/index.html
Original file line number Diff line number Diff line change
@@ -1,14 +1,26 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Title here</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Image carousel</title>
<link rel="stylesheet" href="style.css">
<script defer src="slideshow.js"></script>
</head>
<body>
<img id="carousel-img" src="./assets/cute-cat-a.png" alt="cat-pic" />
<button type="button" id="backward-btn">Backwards</button>
<button type="button" id="forward-btn">Forward</button>
<h1>Image Carousel</h1>

<img id="carousel-img" src="./images/puppy1.jpg" alt="puppy-pic" />

<div class="button-row">
<button id="backward-btn">Backwards</button>
<button id="forward-btn">Forward</button>
</div>

<div class="button-row">
<button id="auto-backward">Auto Backwards</button>
<button id="auto-forward">Auto Forward</button>
<button id="stop">Stop</button>
</div>

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The html updates are logical!
NTH (Nice to Have/ Not required for completion): semantic html
How might you refactor this to use semantic html to improve accessibility and maintainability? (https://www.freecodecamp.org/news/semantic-html5-elements/)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for great feedback! I have updated slideshow.js so that clicking the forward or back buttons while auto-advancing now stops the auto-advance as per the acceptance criteria. I also added a test to verify that manual navigation stops auto-advance mode.

Copy link
Author

@MalusiS MalusiS Aug 14, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks again for the suggestion. I have now wrapped all slideshow controls (manual and automatic) in a single semantic section with a heading. Manual and auto buttons are visually separated into rows, but screen readers and assistive technologies will now recognize all controls as part of one cohesive group. This improves accessibility, semantic clarity, and maintainability without changing the existing functionality.

</body>
</html>
56 changes: 52 additions & 4 deletions Sprint-3/slideshow/slideshow.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,56 @@
const images = [
"./assets/cute-cat-a.png",
"./assets/cute-cat-b.jpg",
"./assets/cute-cat-c.jpg",
"./images/puppy1.jpg",
"./images/puppy2.jpg",
"./images/puppy3.jpg",
"./images/puppy4.jpg",
];

let currentIndex = 0;
let intervalId = null;
const intervalTime = 2000; // 2 seconds

// Write your code here
// Get DOM elements
const carouselImg = document.getElementById("carousel-img");
const forwardBtn = document.getElementById("forward-btn");
const backwardBtn = document.getElementById("backward-btn");
const autoForwardBtn = document.getElementById("auto-forward");
const autoBackwardBtn = document.getElementById("auto-backward");
const stopBtn = document.getElementById("stop");

// Manual navigation
function showImage(index) {
carouselImg.src = images[index];
}

function nextImage() {
currentIndex = (currentIndex + 1) % images.length;
showImage(currentIndex);
}

function prevImage() {
currentIndex = (currentIndex - 1 + images.length) % images.length;
showImage(currentIndex);
}

forwardBtn.addEventListener("click", nextImage);
backwardBtn.addEventListener("click", prevImage);

// Auto navigation
function startAuto(directionFn) {
// Disable auto buttons while running
autoForwardBtn.disabled = true;
autoBackwardBtn.disabled = true;

intervalId = setInterval(directionFn, intervalTime);
}

function stopAuto() {
clearInterval(intervalId);
intervalId = null;
autoForwardBtn.disabled = false;
autoBackwardBtn.disabled = false;
}

autoForwardBtn.addEventListener("click", () => startAuto(nextImage));
autoBackwardBtn.addEventListener("click", () => startAuto(prevImage));
stopBtn.addEventListener("click", stopAuto);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good job separating concerns! The individual functions make the code more readable.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I appreciate all the great feedback you have given.

131 changes: 93 additions & 38 deletions Sprint-3/slideshow/slideshow.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ beforeEach(async () => {
runScripts: "dangerously",
});

// do this so students can use element.innerText which jsdom does not implement
Object.defineProperty(page.window.HTMLElement.prototype, "innerText", {
get() {
return this.textContent;
Expand All @@ -36,9 +35,10 @@ afterEach(() => {
describe("Level 1 challenge", () => {
test("renders the first image with control buttons", () => {
const images = [
"./assets/cute-cat-a.png",
"./assets/cute-cat-b.jpg",
"./assets/cute-cat-c.jpg",
"./images/puppy1.jpg",
"./images/puppy2.jpg",
"./images/puppy3.jpg",
"./images/puppy4.jpg",
];
const image = page.window.document.querySelector("#carousel-img");
const forwardBtn = page.window.document.querySelector("#forward-btn");
Expand All @@ -48,11 +48,13 @@ describe("Level 1 challenge", () => {
expect(forwardBtn).toBeInTheDocument();
expect(backwardBtn).toBeInTheDocument();
});

test("can move the image forwards once", () => {
const images = [
"./assets/cute-cat-a.png",
"./assets/cute-cat-b.jpg",
"./assets/cute-cat-c.jpg",
"./images/puppy1.jpg",
"./images/puppy2.jpg",
"./images/puppy3.jpg",
"./images/puppy4.jpg",
];
const image = page.window.document.querySelector("#carousel-img");
const forwardBtn = page.window.document.querySelector("#forward-btn");
Expand All @@ -66,9 +68,10 @@ describe("Level 1 challenge", () => {

test("can move the image forwards multiple times", () => {
const images = [
"./assets/cute-cat-a.png",
"./assets/cute-cat-b.jpg",
"./assets/cute-cat-c.jpg",
"./images/puppy1.jpg",
"./images/puppy2.jpg",
"./images/puppy3.jpg",
"./images/puppy4.jpg",
];
const image = page.window.document.querySelector("#carousel-img");
const forwardBtn = page.window.document.querySelector("#forward-btn");
Expand All @@ -79,11 +82,49 @@ describe("Level 1 challenge", () => {
expect(image).toHaveAttribute("src", images[2]);
});

test("can move the image backwards to the end", () => {
test("can move the image forwards to the last image", () => {
const images = [
"./images/puppy1.jpg",
"./images/puppy2.jpg",
"./images/puppy3.jpg",
"./images/puppy4.jpg",
];
const image = page.window.document.querySelector("#carousel-img");
const forwardBtn = page.window.document.querySelector("#forward-btn");

userEvent.click(forwardBtn);
userEvent.click(forwardBtn);
userEvent.click(forwardBtn);

expect(image).toHaveAttribute("src", images[3]);
});

test("moving forwards will eventually wrap around to the start", () => {
const images = [
"./images/puppy1.jpg",
"./images/puppy2.jpg",
"./images/puppy3.jpg",
"./images/puppy4.jpg",
];
const image = page.window.document.querySelector("#carousel-img");
const forwardBtn = page.window.document.querySelector("#forward-btn");

expect(image).toHaveAttribute("src", images[0]);

userEvent.click(forwardBtn);
userEvent.click(forwardBtn);
userEvent.click(forwardBtn);
userEvent.click(forwardBtn);

expect(image).toHaveAttribute("src", images[0]);
});

test("can move the image backwards to the last image", () => {
const images = [
"./assets/cute-cat-a.png",
"./assets/cute-cat-b.jpg",
"./assets/cute-cat-c.jpg",
"./images/puppy1.jpg",
"./images/puppy2.jpg",
"./images/puppy3.jpg",
"./images/puppy4.jpg",
];
const image = page.window.document.querySelector("#carousel-img");
const backwardBtn = page.window.document.querySelector("#backward-btn");
Expand All @@ -92,39 +133,41 @@ describe("Level 1 challenge", () => {

userEvent.click(backwardBtn);

expect(image).toHaveAttribute("src", images[2]);
expect(image).toHaveAttribute("src", images[3]);
});

test("can move the image backwards multiple times", () => {
const images = [
"./assets/cute-cat-a.png",
"./assets/cute-cat-b.jpg",
"./assets/cute-cat-c.jpg",
"./images/puppy1.jpg",
"./images/puppy2.jpg",
"./images/puppy3.jpg",
"./images/puppy4.jpg",
];
const image = page.window.document.querySelector("#carousel-img");
const backwardBtn = page.window.document.querySelector("#backward-btn");
expect(image).toHaveAttribute("src", images[0]);

userEvent.click(backwardBtn);
userEvent.click(backwardBtn);

expect(image).toHaveAttribute("src", images[1]);
expect(image).toHaveAttribute("src", images[2]);
});

test("moving forwards will eventually wrap around to the start", () => {
test("moving backwards will eventually wrap around to the start", () => {
const images = [
"./assets/cute-cat-a.png",
"./assets/cute-cat-b.jpg",
"./assets/cute-cat-c.jpg",
"./images/puppy1.jpg",
"./images/puppy2.jpg",
"./images/puppy3.jpg",
"./images/puppy4.jpg",
];
const image = page.window.document.querySelector("#carousel-img");
const forwardBtn = page.window.document.querySelector("#forward-btn");
const backwardBtn = page.window.document.querySelector("#backward-btn");

expect(image).toHaveAttribute("src", images[0]);

userEvent.click(forwardBtn);
userEvent.click(forwardBtn);
userEvent.click(forwardBtn);
userEvent.click(backwardBtn);
userEvent.click(backwardBtn);
userEvent.click(backwardBtn);
userEvent.click(backwardBtn);

expect(image).toHaveAttribute("src", images[0]);
});
Expand All @@ -137,11 +180,13 @@ describe("Level 2 challenge", () => {
afterEach(() => {
jest.useRealTimers();
});

test("can start moving images forward automatically", () => {
const images = [
"./assets/cute-cat-a.png",
"./assets/cute-cat-b.jpg",
"./assets/cute-cat-c.jpg",
"./images/puppy1.jpg",
"./images/puppy2.jpg",
"./images/puppy3.jpg",
"./images/puppy4.jpg",
];
const image = page.window.document.querySelector("#carousel-img");
const autoForwardBtn = page.window.document.querySelector("#auto-forward");
Expand All @@ -161,14 +206,19 @@ describe("Level 2 challenge", () => {
jest.advanceTimersByTime(interval);
expect(image).toHaveAttribute("src", images[2]);

jest.advanceTimersByTime(interval);
expect(image).toHaveAttribute("src", images[3]);

jest.advanceTimersByTime(interval);
expect(image).toHaveAttribute("src", images[0]);
});

test("can start moving images backward automatically", () => {
const images = [
"./assets/cute-cat-a.png",
"./assets/cute-cat-b.jpg",
"./assets/cute-cat-c.jpg",
"./images/puppy1.jpg",
"./images/puppy2.jpg",
"./images/puppy3.jpg",
"./images/puppy4.jpg",
];
const image = page.window.document.querySelector("#carousel-img");
const autoForwardBtn = page.window.document.querySelector("#auto-forward");
Expand All @@ -182,6 +232,9 @@ describe("Level 2 challenge", () => {
expect(autoForwardBtn).toBeDisabled();
expect(autoBackBtn).toBeDisabled();

jest.advanceTimersByTime(interval);
expect(image).toHaveAttribute("src", images[3]);

jest.advanceTimersByTime(interval);
expect(image).toHaveAttribute("src", images[2]);

Expand All @@ -191,11 +244,13 @@ describe("Level 2 challenge", () => {
jest.advanceTimersByTime(interval);
expect(image).toHaveAttribute("src", images[0]);
});

test("can stop the automatic timer", () => {
const images = [
"./assets/cute-cat-a.png",
"./assets/cute-cat-b.jpg",
"./assets/cute-cat-c.jpg",
"./images/puppy1.jpg",
"./images/puppy2.jpg",
"./images/puppy3.jpg",
"./images/puppy4.jpg",
];
const image = page.window.document.querySelector("#carousel-img");
const autoForwardBtn = page.window.document.querySelector("#auto-forward");
Expand Down Expand Up @@ -224,4 +279,4 @@ describe("Level 2 challenge", () => {
jest.runOnlyPendingTimers();
expect(image).toHaveAttribute("src", images[2]);
});
});
});
47 changes: 46 additions & 1 deletion Sprint-3/slideshow/style.css
Original file line number Diff line number Diff line change
@@ -1 +1,46 @@
/** Write your CSS in here **/
body {
font-family: Arial, sans-serif;
background-color: #f4f4f4;
margin: 0;
padding: 20px;
text-align: center;
}

h1 {
margin-bottom: 20px;
}

#carousel-img {
width: 400px;
max-width: 100%;
border-radius: 8px;
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.2);
margin-bottom: 15px;
}

.button-row {
display: flex;
justify-content: center;
gap: 10px;
margin-bottom: 10px;
}

button {
background-color: #0077cc;
color: white;
border: none;
padding: 10px 15px;
border-radius: 5px;
cursor: pointer;
font-size: 14px;
min-width: 120px;
}

button:hover:not(:disabled) {
background-color: #005fa3;
}

button:disabled {
background-color: #cccccc;
cursor: not-allowed;
}