diff --git a/.github/workflows/unit.yml b/.github/workflows/unit.yml index ac93c2e..58f669e 100644 --- a/.github/workflows/unit.yml +++ b/.github/workflows/unit.yml @@ -14,4 +14,4 @@ jobs: run: npm install - name: Unit Test - run: npm test ./testing/sum_tests.test.js + run: npm run test-unit diff --git a/jsdom.config.json b/jsdom.config.json new file mode 100644 index 0000000..73c307e --- /dev/null +++ b/jsdom.config.json @@ -0,0 +1,5 @@ +{ + "transform": {}, + "testEnvironment": "jsdom", + "verbose": true +} diff --git a/package.json b/package.json index 39b5faa..d1b66d9 100644 --- a/package.json +++ b/package.json @@ -7,10 +7,12 @@ "globals": "^15.2.0", "jest": "^29.7.0", "jest-puppeteer": "^10.0.1", - "puppeteer": "^22.10.0" + "puppeteer": "^22.10.0", + "jest-environment-jsdom": "^29.7.0" }, "scripts": { - "test": "jest --maxWorkers=1" + "test": "jest --maxWorkers=1 testing/puppeteer.test.js", + "test-unit": "node --experimental-vm-modules node_modules/jest/bin/jest.js --config=jsdom.config.json testing/unit.test.js" }, "type": "module", "jest": { @@ -21,11 +23,11 @@ "jest-puppeteer": { "launch": { "headless": true, - "slowMo": 25, + "slowMo": 1, "defaultViewport": null } }, "dependencies": { "jsdoc": "^4.0.3" } -} \ No newline at end of file +} diff --git a/source/calendar/calendar.css b/source/calendar/calendar.css index a4e83f5..9710d94 100644 --- a/source/calendar/calendar.css +++ b/source/calendar/calendar.css @@ -1,3 +1,5 @@ +/* ********** Start of Universal Styling ********** */ + * { margin: 0; box-sizing: border-box; @@ -24,35 +26,44 @@ main { overflow: hidden; height: 100%; position: relative; - } -/* Remove background for all buttons */ +/* Universal background color for buttons to clear */ button { background-color: rgba(0,0,0,0); border: rgba(0,0,0,1); } +/* Remove outline around focused button */ +button:focus { + outline: none; +} + +/* Change mouse cursor when hovering over button */ +button:hover { + cursor: pointer; +} + /* Remove background for all select boxes */ select { background-color: rgba(0,0,0,0); border: rgba(0,0,0,1); appearance: none; } - -/* TASK LIST */ -/* Start of task list */ + +/* ********** End of Universal Styling **** */ + +/* ********** Start of Task List ********** */ + .task-list { flex: 0 0 18%; - max-width: 17rem; /* Prevents task list expanding with long task names */ + max-width: 17rem; height:100%; margin: 5px; padding: 1%; border-radius: 15px; background-color: white; box-shadow: 3px 3px 3px lightgrey; - - /* Set size of task area to be relative to list */ position: relative; } @@ -63,20 +74,8 @@ select { bottom: 0; width: 88%; overflow: auto; - - /* Hide scroll-bars for Firefox, Edge */ - -ms-overflow-style: none; /* IE and Edge */ - scrollbar-width: none; /* Firefox */ -} - -/* Hides task list when page reaches certain size */ -@media (max-width: 800px) { - .task-list { - display: none; - } - .journal-past-wrap .task-list { - display: block; - } + -ms-overflow-style: none; + scrollbar-width: none; } /* Formatting for the task list header */ @@ -96,21 +95,14 @@ select { cursor: pointer; } -/* Wrap for task name and checkbox */ -/* .task-list .input-wrap { - display: flex; - justify-content: flex-start; -} */ - /* Styling for task name */ .task-list .task-name { width: max(10em, 12vw); - resize: none; /* Prevents user from resizing */ - overflow: hidden; /* Hides scrollbar */ + resize: none; + overflow: hidden; margin-top: -4px; height: 24px; max-height: 108px; - /* Hide background and border */ background-color: rgba(0, 0, 0, 0); border: rgba(0, 0, 0, 0); } @@ -137,8 +129,7 @@ select { overflow: hidden; word-wrap: break-word; text-align: left; - list-style-type: none; /* Get rid of li bullet points */ - /* Set max height */ + list-style-type: none; transition: max-height 0.5s ease-in; max-height: 4em; } @@ -178,15 +169,14 @@ select { content: attr(placeholder); pointer-events: none; display: block; - color: rgb(97, 97, 97) } /* Positioning for trash icon */ -.fas.fa-trash-alt { - position: absolute; /* Positioning relative to the nearest positioned ancestor */ - top: 2px; /* Distance from the top edge of the li */ - right: 2px; /* Distance from the right edge of the li */ +.trash-icon { + position: absolute; + top: 2px; + right: 2px; width: 18px; height: 18px; cursor: pointer; @@ -194,7 +184,7 @@ select { /* Display buttons when hovering or actively editing */ .task:hover .color-buttons { - opacity: 1; /* Brings the buttons back into view */ + opacity: 1; } /* Checkbox appearance */ @@ -224,20 +214,19 @@ select { /* Default set up for color buttons */ .color-buttons { - opacity: 0; /* Hide buttons */ - transition: opacity 0.3s ease; /* Transition for buttons reappearing */ - max-height: 5em; /* Enough max-height to show the content */ - display: flex; /* Lays out the buttons in a row */ - justify-content: space-around; /* Spaces buttons evenly */ - margin-top: 5px; /* Space from the description */ + opacity: 0; + transition: opacity 0.3s ease; + max-height: 5em; + display: flex; + justify-content: space-around; + margin-top: 5px; } /* Size of each color button */ .color-button { - width: 15px; /* Uniform button size */ - height: 15px; /* Uniform button size */ - border-radius: 100%; /* Makes the buttons circular */ - + width: 15px; + height: 15px; + border-radius: 100%; border: solid grey 1px; &#purple { @@ -261,13 +250,14 @@ select { } } -/* ********** End of task list ********** */ +/* ********** End of Task List ********** */ -/* ********** Start of top bar ********** */ +/* ********** Start of Top Bar ********** */ + +/* Container of prev/next buttons, current date, and homepage-calendar buttons */ .top-bar { position: relative; height: 65px; - } .date-header-wrap { @@ -276,20 +266,17 @@ select { height: 100%; width: min(70em, 100%); width: 100%; - display: grid; grid-template-columns: 1fr 1fr; } -/* Next, Prev, and Header organization */ +/* Container of prev/next buttons, current date */ .date-header { display: grid; grid-template-columns: 1fr 8fr; margin-left: 1.1em; - position: absolute; bottom: 0; - width: 50%; max-width: 40em; } @@ -299,7 +286,7 @@ select { background-color: var(--secondary-color); } -/* Calendar date header */ +/* Container of the previous and next day buttons*/ .date-header-text { width: 100%; margin-top: auto; @@ -340,11 +327,6 @@ select { padding-right: 0em; } -/* Add cursor when hovering over buttons */ -button:hover { - cursor: pointer; -} - /* Jump header that contains the month and year */ .jump-header { display: flex; @@ -439,7 +421,6 @@ button:hover { .date-header-buttons { display: grid; grid-template-columns: 1.2em 1fr; - margin-top: auto; margin-bottom: min(0.6em, 14%); margin-left: 0.4em; @@ -450,10 +431,7 @@ button:hover { .prev-date-btn { width: 2em; height: 2em; - border-radius: 4em; - - /* Add button image */ background-image: url('../icons/left-icon.png'); background-size: 14px 14px; background-repeat: no-repeat; @@ -464,15 +442,10 @@ button:hover { /* Goto next month button */ .next-date-btn { float: left; - width: 2em; height: 2em; - margin-left: 0.4em; - border-radius: 4em; - - /* Add button image */ background-image: url('../icons/right-icon.png'); background-size: 14px 14px; background-repeat: no-repeat; @@ -549,7 +522,6 @@ tbody td { padding-top: 1px; } -/* Calendar date */ td .cell-date { position: absolute; left: 5px; @@ -624,6 +596,12 @@ td .productivity-icon { left: 0; } +/* Highlight boxes when hovering over specific calendar cell */ +.a-link:hover { + background-color: skyblue; + opacity: 0.3; +} + /* Media query to handle window resizing */ @media (max-height: 500px) { .task-ul { @@ -637,5 +615,42 @@ td .productivity-icon { } } -/* ********** End of Calendar ********** */ +/* ********** End of Calendar Table ********** */ + +/* ********** Start of Responsiveness ********** */ + +@media (max-width: 740px) { + main { + overflow-x: scroll; + } + + .task-list { + flex: 0 0 25%; + z-index: 100000; + transition: transform 0.3s ease; + transform: translateX(-88%); + } + + .task-list.active { + transform: translateX(0%); + } + + .task-wrapper { + display: none; + } + + .task-wrapper.active { + display: block; + } + + .full-calendar { + position: absolute; + left: 5%; + bottom: 0; + top: 0; + right: 0; + } +} + +/* ********** End of Responsiveness ********** */ diff --git a/source/calendar/calendar.js b/source/calendar/calendar.js index 7c7c5a5..3538864 100644 --- a/source/calendar/calendar.js +++ b/source/calendar/calendar.js @@ -24,6 +24,7 @@ function updateDateGlobals() { function init() { // Initiaze the jump buttons displayJump(year - 6, year + 5); + taskListViewHandler(); // Initially display the calendar, calendar header, and task colors calendarHeader(); @@ -35,8 +36,6 @@ function init() { loadTasks(); } - -// FUNCTIONS /** * Initializes the buttons for adding tasks, navigating months, and other functionalities. */ @@ -47,16 +46,16 @@ function initButtons() { addTask(); }); - // PREVIOUS MONTH BUTTON + // Previous month button let prevBtn = document.querySelector(".prev-date-btn"); prevBtn.addEventListener('click', prev); - // NEXT MONTH BUTTON + // Next month button let nextBtn = document.querySelector(".next-date-btn"); nextBtn.addEventListener('click', next); - // JUMP HEADER BUTTONS - // LIST OF MONTHS + // Jump header buttons + // List of months let monthJumpBtn = document.querySelectorAll(".month-btn"); monthJumpBtn.forEach(btn => { btn.addEventListener("click", () => { @@ -64,7 +63,7 @@ function initButtons() { jump(monthValue, year); }); }); - // LIST OF YEARS + // List of years let yearJumpBtn = document.querySelectorAll(".year-btn"); yearJumpBtn.forEach(btn => { btn.addEventListener("click", () => { @@ -73,10 +72,10 @@ function initButtons() { }); }); - // RESIZE WINDOW FOR RESPONSIVENESS + // Resize window for responsiveness window.addEventListener('resize', windowWidth); - // ADD LEFT/RIGHT ARROWS TO GOTO PREV/NEXT MONTHS + // Add left/right arrows to goto prev/next months window.addEventListener('keydown', function(event) { if (event.key === "ArrowLeft") { prev(); @@ -87,7 +86,7 @@ function initButtons() { } /** - * Updates the global currDate to the next date and displays the next month + * Updates the global currDate to the next date and displays the next month. */ function next(){ // Increment the month @@ -108,15 +107,17 @@ function prev() { /** * Adds a task to the task list upon "Add Task" button click. + * * @param {boolean} [loadTask=false] - Indicates whether the task is being loaded from storage. * @returns {HTMLElement} - The newly created task element. */ function addTask(loadTask = false) { - // add a task to an element of task container + // Add a task to an element of task container const taskList = document.querySelector(".task-container"); const task = document.createElement("li"); - task.setAttribute("class", "task"); - // add it at the first row + task.className = "task"; + + // Add it at the first row task.insertAdjacentHTML("beforeend", `
@@ -129,23 +130,25 @@ function addTask(loadTask = false) {
- Remove + Remove `); task.querySelector(".task-input").addEventListener("input", saveTasks); taskList.append(task); - // listener to stop editing when user presses enter + // Listener to stop editing when user presses enter const task_name = task.querySelector(".task-input"); task_name.addEventListener('keydown', function (event) { + // Shift + Enter pressed, insert a line break if (event.key == 'Enter') { + // Enter pressed, end editing if (!event.shiftKey) { - // Shift+Enter pressed, insert a line break - // Enter pressed, end editing - event.preventDefault(); // Prevent default behavior of Enter key - task_name.blur(); // Remove focus from the element - //li.classList.remove('active'); + // Prevent default behavior of Enter key + event.preventDefault(); + + // Remove focus from the element + task_name.blur(); } } }); @@ -161,7 +164,7 @@ function addTask(loadTask = false) { }, 0); } - // add functionality to task buttons + // Add functionality to task buttons taskButtonsFunctionality(task); return task; @@ -169,13 +172,12 @@ function addTask(loadTask = false) { /** * Adds button functionality to a task upon creation. + * * @param {HTMLElement} task - The task element to add functionality to. */ function taskButtonsFunctionality(task) { - - /* Implement color changing functionality */ + // Implement color changing functionality const colorBtns = task.querySelectorAll(".color-button"); - console.log(colorBtns); colorBtns.forEach(btn => { btn.addEventListener('click', function () { let color; @@ -200,19 +202,18 @@ function taskButtonsFunctionality(task) { }); }); - /* Trash icon delete functionality */ - const deleteIcon = task.querySelector(".fas"); + // Trash icon delete functionality + const deleteIcon = task.querySelector(".trash-icon"); deleteIcon.addEventListener("click", () => { task.remove(); saveTasks(); }); - /* Checkbox move to Completed Tasks functionality */ + // Checkbox move to completed tasks functionality const checkbox = task.querySelector(".task-checkbox"); checkbox.addEventListener('click', function () { - // Add or remove completed from class name - // Find closest li item (task) + // Add or remove completed from class name if (task.className.includes('complete')) { task.classList.remove('complete'); const taskContainer = document.querySelector('.task-container'); @@ -229,8 +230,32 @@ function taskButtonsFunctionality(task) { }); } +/** + * Expands task list from collapsed state. + */ +function taskListViewHandler() { + const taskList = document.querySelector('.task-list'); + const taskWrap = document.querySelector('.task-wrapper'); + const outSide = document.querySelector('.full-calendar'); + taskList.addEventListener('click', function(event) { + if (event.target === taskList) { + if (window.innerWidth <= 800) { + taskList.classList.toggle('active'); + taskWrap.classList.toggle('active'); + } + } + }); + outSide.addEventListener('click', function(){ + if (window.innerWidth <= 800) { + taskList.classList.remove('active'); + taskWrap.classList.remove('active'); + } + }); +} + /** * Saves the completed tasks for a specific day. + * * @param {HTMLElement} completedTaskElement - The task element that was completed. */ function saveCompleted(completedTaskElement) { @@ -273,9 +298,9 @@ function displayCalendar() { monthHeader.textContent = allMonths[parseInt(month, 10)]; yearHeader.textContent = year; - let currDay; - // BUILD CALENDAR + // Build Calendar // Loop through number of rows + let currDay; for (let i = 0; i < 6; i++) { // Create rows let row = document.createElement("tr"); @@ -330,7 +355,6 @@ function displayCalendar() { if (cellDate <= today) { loadCellDataTest(cellData, currCalendarMonth); - // loadCellData(cellData, currCalendarMonth); } // Append new cell to row row.appendChild(cellData); @@ -348,6 +372,7 @@ function displayCalendar() { /** * Loads cell data such as rating, productivity, and tasks for a specific date in the calendar. + * * @param {HTMLElement} cellData - The table cell element to populate with data. * @param {Date} currCalendarMonth - The current month being displayed in the calendar. */ @@ -365,6 +390,7 @@ function loadCellDataTest(cellData, currCalendarMonth) { sentimentIcon.src = `../icons/${RATING_FILES_NAMES[rating - 1]}`; sentimentIcon.alt = "sentiment icon"; sentimentIcon.className = "sentiment-icon"; + // Append sentiment icon to new cell cellData.appendChild(sentimentIcon); } @@ -375,6 +401,7 @@ function loadCellDataTest(cellData, currCalendarMonth) { productivityIcon.src = `../icons/${PRODUCTIVITY_FILES_NAMES[productivity - 1 - 5]}`; productivityIcon.alt = "productivity icon"; productivityIcon.className = "productivity-icon"; + // Append sentiment icon to new cell cellData.appendChild(productivityIcon); } @@ -383,6 +410,7 @@ function loadCellDataTest(cellData, currCalendarMonth) { // Create tasklist div let taskDiv = document.createElement("div"); taskDiv.className = "task-div"; + // Create unordered list let taskList = document.createElement("ul"); taskList.className = "task-ul"; @@ -397,7 +425,7 @@ function loadCellDataTest(cellData, currCalendarMonth) { } if (tasks.length > DISPLAY_TASK_COUNT) { - // extra tasks + // Handle extra tasks in calendar view let taskExtra = document.createElement("li"); taskExtra.textContent = `${tasks.length - DISPLAY_TASK_COUNT} more tasks`; taskExtra.className = "task-indicator"; @@ -407,10 +435,10 @@ function loadCellDataTest(cellData, currCalendarMonth) { // Append taskList to task div; taskDiv.appendChild(taskList); + // Append tasklist div to new cell cellData.appendChild(taskDiv); - // Create buttons that link to speciic homepage and extract selected date let aLink = document.createElement("a"); let dayLink = currCalendarMonth.getDate(); @@ -425,11 +453,12 @@ function loadCellDataTest(cellData, currCalendarMonth) { /** * Generates a dropdown for year and month selection. + * * @param {number} startYear - The start year for the dropdown range. * @param {number} endYear - The end year for the dropdown range. */ function displayJump(startYear, endYear) { - // YEARS + // Years let yearDropdown = document.getElementById("year-dropdown") // Loop through year range and append to list @@ -442,13 +471,14 @@ function displayJump(startYear, endYear) { } - // MONTHS + // Months let allMonths = [ "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" ]; - let monthDropdown = document.getElementById("month-dropdown") + // Loop through months and append to list + let monthDropdown = document.getElementById("month-dropdown") for (let mnth = 0; mnth < 12; mnth++) { let monthJump = document.createElement("button"); monthJump.value = mnth; @@ -459,7 +489,8 @@ function displayJump(startYear, endYear) { } /** - * Function to jump to a specific month and year + * Function to jump to a specific month and year. + * * @param {number} mnth - month to jump to * @param {number} yr - year to jump to */ @@ -470,7 +501,7 @@ function jump(mnth, yr) { } /** - * Creates header of the calendar + * Creates header of the calendar. */ function calendarHeader(){ // Initialize list of days of the week @@ -518,30 +549,23 @@ function windowWidth() { document.getElementById('year-dropdown').style.left = monthWidth + 5 + 'px'; } -//------------------------------------------ -// Save journal entry +/* ********** Storage and Population ********** */ // Get the all relevent elements from page const tasks = document.querySelector(".task-container"); -// Load journal entry and tasks from local storage on page load -window.onload = function () { - // loadTasks(); -} - // Save journal entry and tasks to local storage on page unload window.onbeforeunload = function () { saveTasks() } /** - * Save journal entry to local storage - * - * @param {string} data - journal entry text in parsed json format - * @param {string} dateText - date of the journal entry in locale date string format - * @param {string} key - key to store the value under - * @param {string} value - value to store + * Save journal entry to local storage. * + * @param {string} data - Journal entry text in parsed json format. + * @param {string} dateText - Date of the journal entry in locale date string format. + * @param {string} key - Key to store the value under. + * @param {string} value - Value to store. */ function saveToStorage(data, dateText, key, value) { if (!(dateText in data)) { @@ -566,9 +590,9 @@ function loadFromStorage(data, dateText, key) { } /** - * Get journal entry from local storage + * Get journal entry from local storage. * - * @returns {Object} journal entry text in parsed json format + * @returns {Object} - Journal entry text in parsed json format. */ function getJournal() { let data = JSON.parse(localStorage.getItem("journals")) @@ -579,12 +603,11 @@ function getJournal() { } /** - * Save tasks to local storage + * Save tasks to local storage. */ function saveTasks() { let tasks = []; document.querySelectorAll('.task-container li').forEach(task => { - //let checkbox = task.querySelector('input[type="task-checkbox"]'); let taskName = task.querySelector('.task-input').textContent; let taskColor = task.style['background-color'] tasks.push({ @@ -596,9 +619,9 @@ function saveTasks() { } /** - * Get tasks from local storage + * Get tasks from local storage. * - * @returns {string} tasks in parsed json format or empty array if no tasks + * @returns {string} - Tasks in parsed json format or empty array if no tasks. */ function getTasks() { let storedTasks = localStorage.getItem("tasks"); @@ -606,7 +629,7 @@ function getTasks() { } /** - * Load tasks from local storage + * Load tasks from local storage. */ function loadTasks() { let tasks = getTasks(); @@ -615,12 +638,11 @@ function loadTasks() { let curLi = addTask(true); curLi.querySelector(".task-input").textContent = task['text'] curLi.style['background-color'] = task['color'] - //curLi.querySelector('input[type="checkbox"]').checked = task['checked'] }); } } -// Save journal entry and tasks to local storage on events +// Save journal entry and tasks to local storage on events. tasks.addEventListener("blur", saveTasks) tasks.addEventListener("change", saveTasks) \ No newline at end of file diff --git a/source/homepage/homepage.css b/source/homepage/homepage.css index 7570cf4..6ac7978 100644 --- a/source/homepage/homepage.css +++ b/source/homepage/homepage.css @@ -1,23 +1,26 @@ +/* ********** Start of Universal Styling ********** */ + * { margin: 0; box-sizing: border-box; - font-family: "Poppins", sans-serif; --main-color: #f2f2f2; --secondary-color: #d1d8e3; --accent-color: #a8ceec; - } + html { height: 100%; background-color: yellow; overflow: hidden; } + body { height: 100%; background-color: var(--main-color); } + main { display: flex; overflow: hidden; @@ -31,18 +34,29 @@ button { border: rgba(0,0,0,1); } -/* ********** Start of task list ********** */ +/* Remove outline around focused button */ +button:focus { + outline: none; +} + +/* Change mouse cursor when hovering over button */ +button:hover { + cursor: pointer; +} + +/* ********** End of Universal Styling **** */ + +/* ********** Start of Task List ********** */ + .task-list { flex: 0 0 18%; - max-width: 17rem; /* Prevents task list expanding with long task names */ + max-width: 17rem; height:100%; margin: 5px; padding: 1%; border-radius: 15px; background-color: white; box-shadow: 3px 3px 3px lightgrey; - - /* Set size of task area to be relative to list */ position: relative; } @@ -64,10 +78,8 @@ button { bottom: 0; width: 88%; overflow: auto; - - /* Hide scroll-bars for Firefox, Edge */ - -ms-overflow-style: none; /* IE and Edge */ - scrollbar-width: none; /* Firefox */ + -ms-overflow-style: none; + scrollbar-width: none; } /* Wrapper for overflow of completed tasks */ @@ -77,10 +89,8 @@ button { height: 82%; width: 96%; overflow: auto; - - /* Hide scroll-bars for Firefox, Edge */ - -ms-overflow-style: none; /* IE and Edge */ - scrollbar-width: none; /* Firefox */ + -ms-overflow-style: none; + scrollbar-width: none; } /* Aligns add button to right */ @@ -98,11 +108,10 @@ button { /* Styling for task name */ .task-list .task-name { width: max(10em, 12vw); - resize: none; /* Prevents user from resizing */ + resize: none; margin-top: -4px; height: 24px; max-height: 108px; - /* Hide background and border */ background-color: rgba(0, 0, 0, 0); border: rgba(0, 0, 0, 0); } @@ -128,12 +137,12 @@ button { position: relative; word-wrap: break-word; text-align: left; - list-style-type: none; /* Get rid of li bullet points */ - /* Set max height */ + list-style-type: none; transition: max-height 0.5s ease-in; max-height: 4em; } +/* Expands individual taskbox when hovered over */ .task:hover { max-height: fit-content; } @@ -174,10 +183,10 @@ button { } /* Positioning for trash icon */ -.fas.fa-trash-alt { - position: absolute; /* Positioning relative to the nearest positioned ancestor */ - top: 2px; /* Distance from the top edge of the li */ - right: 2px; /* Distance from the right edge of the li */ +.trash-icon { + position: absolute; + top: 2px; + right: 2px; width: 18px; height: 18px; cursor: pointer; @@ -185,7 +194,7 @@ button { /* Display buttons when hovering or actively editing */ .task:hover .color-buttons { - opacity: 1; /* Brings the buttons back into view */ + opacity: 1; } /* Checkbox appearance */ @@ -215,20 +224,19 @@ button { /* Default set up for color buttons */ .color-buttons { - opacity: 0; /* Hide buttons */ - transition: opacity 0.3s ease; /* Transition for buttons reappearing */ - max-height: 5em; /* Enough max-height to show the content */ - display: flex; /* Lays out the buttons in a row */ - justify-content: space-around; /* Spaces buttons evenly */ - margin-top: 5px; /* Space from the description */ + opacity: 0; + transition: opacity 0.3s ease; + max-height: 5em; + display: flex; + justify-content: space-around; + margin-top: 5px; } -/* Size of each color button */ +/* Size, shape, and color of each color button */ .color-button { - width: 15px; /* Uniform button size */ - height: 15px; /* Uniform button size */ - border-radius: 100%; /* Makes the buttons circular */ - + width: 15px; + height: 15px; + border-radius: 100%; border: solid grey 1px; &#purple { @@ -252,19 +260,21 @@ button { } } +/* ********** End of Task List ********** */ +/* ********** Start of Main Wrap ******** */ -/* ********** End of task list ********** */ - +/* Container of top bar, journal, feeling widgets, completed tasks, and past week */ .main-wrap { flex: 1; position: relative; float: left; - height: 100%; } -/* ********** Start of top bar ********** */ +/* ========== Start of top bar ========== */ + +/* Container of prev/next buttons, current date, and homepage-calendar buttons */ .top-bar { position: relative; height: 10%; @@ -277,41 +287,34 @@ button { height: 100%; width: min(70em, 100%); width: 100%; - display: grid; grid-template-columns: 1fr 1fr; } +/* Container of prev/next buttons, current date */ .date-header { display: grid; grid-template-columns: 1fr 8fr; margin-left: 1.1em; - position: absolute; bottom: 0; - width: 50%; max-width: 40em; } -/* Wrap the previous and next day buttons*/ +/* Container of the previous and next day buttons*/ .date-header-buttons { display: grid; grid-template-columns: 1.2em 1fr; - margin-top: auto; margin-bottom: min(0.6em, 14%); margin-left: 0.4em; - } .prev-date-btn { width: 2em; height: 2em; - border-radius: 4em; - - /* Add button image */ background-image: url('../icons/left-icon.png'); background-size: 14px 14px; background-repeat: no-repeat; @@ -320,16 +323,10 @@ button { } .next-date-btn { - float: left; - width: 2em; height: 2em; - margin-left: 0.4em; - border-radius: 4em; - - /* Add button image */ background-image: url('../icons/right-icon.png'); background-size: 14px 14px; background-repeat: no-repeat; @@ -338,6 +335,7 @@ button { } +/* Change button background color when hovering over prev/next buttons */ .date-header-buttons button:hover { background-color: var(--secondary-color); } @@ -351,11 +349,6 @@ button { font-size: calc(1.4em + .7vw); } -.date-header button img { - height: 18px; - padding: 4px; -} - .nav-buttons { position: absolute; top: 0.6em; @@ -366,11 +359,6 @@ button { gap: 1em; } -.nav-buttons button { - background-color: transparent; - border: none; -} - .nav-buttons button img { width: min(3.4em, 4vw); min-width: 2.6em; @@ -378,13 +366,12 @@ button { padding-right: 0em; } -button:hover { - cursor: pointer; -} -/* ********** End of top bar ********** */ +/* ============== End of Top Bar =========== */ -/* ********** Begin Entry Content ********** */ +/* ============ Begin Entry Content ========= */ + +/* Container of journal, feeling widgets, completed tasks, and past week */ .journal-past-wrap { width: 100%; min-width: 40rem; @@ -392,6 +379,7 @@ button:hover { max-width: 94.4rem; } +/* Container of journal, feeling widgets, completed tasks, and past week */ .today-entry { width: min(66rem, 100%); height: 78%; @@ -399,7 +387,8 @@ button:hover { padding-left: min(2.1rem, 3%); } -/* ********** Start of journal ********** */ +/* ============== Start of Journal ========== */ + .journal { height: 60%; } @@ -409,11 +398,8 @@ button:hover { margin-top: 0.4%; margin-right: 2em; margin-bottom: 1rem; - padding: 0.4em 1em 0.4em 1em; - background-color: var(--secondary-color); - border-radius: 1em; } @@ -421,30 +407,31 @@ button:hover { width: 100%; height: 90%; margin-bottom: 1em; - padding-top: 0.2em; padding-left: 0.4em; - background: var(--secondary-color); - border: none; border-radius: 1em; - resize: none; } +/* Highlight journal when clicked on */ .journal-entry #textarea:focus { outline: none !important; border:1px solid var(--accent-color); box-shadow: 0 0 10px #719ECE; } -/* ********** End of journal ********** */ -/* ********** Start of rating ********** */ +/* =========== End of Journal ========= */ + +/* === Start of Rating and Completed Task ===== */ + +/* Container of rating and completed tasks */ .rating-completed-wrap { position: relative; height: 40%; } + .rating-completed-wrap h4 { text-align: center; font-size: max(calc(0.7em + 0.4vw), 15px); @@ -468,17 +455,6 @@ button:hover { padding-top: min(1em, 5%); } -.feelings { - display: flex; - justify-content: center; - gap: 0em; - padding-top: calc(0.4em - 1.2vw); - padding-bottom: min(calc(2.2em - 1.8vw), 1.3em); - padding-bottom: calc(0.4em - 1.2vw); - - padding: 0; -} - .rating-widget button img { height: max(min(3em, 6vw), 50px); height: min(50px, 8vw); @@ -486,15 +462,27 @@ button:hover { border-radius: 50%; } +/* Highlight current hovered feelings button */ .rating-widget button img:hover { - height: max(min(1em, 1vw), 60px); + transform: scale(1.2); background: radial-gradient(circle, rgb(3, 128, 237) 0%, rgba(0, 0, 0, 0) 100%); - transition: 0.3s ease-in-out; + transition: 0.1s ease-in-out; } +/* Highlight current selected feelings button */ .rating-widget img.active, .rating-widget img.active:hover { background-color: #008FC6; - transition: 0.5s ease; + transition: 0.1s ease; +} + +.feelings { + display: flex; + justify-content: center; + gap: 0em; + padding-top: calc(0.4em - 1.2vw); + padding-bottom: min(calc(2.2em - 1.8vw), 1.3em); + padding-bottom: calc(0.4em - 1.2vw); + padding: 0; } .productiveness { @@ -504,29 +492,26 @@ button:hover { padding-top: calc(1em - 1vw); } -/* ********** End of rating ********** */ - - - /* Completed List */ .journal-past-wrap .task-list { position: absolute; right: 2em; width: min(44%, 30rem); height: 100%; - margin: 0; /* Overwrite inherited margin from task-list */ + margin: 0; max-width: none; background-color: var(--secondary-color); - border-radius: 1em; - transform: none; - } -/* ********** End Entry Content ********** */ +/* === End of Rating and Completed Task ===== */ + +/* ========== End Entry Content ========= */ + +/* ======== Start of Past Week ========== */ -/* ********** Start of past week ********** */ +/* Container for past week */ .past-week-wrap { margin-top: 2%; margin-right: 5%; @@ -550,7 +535,6 @@ button:hover { border: black solid 2px; } - /* Calendar Table Styling */ .table-week { width: 100%; @@ -558,9 +542,7 @@ button:hover { table-layout: fixed; margin: 0; height: 100%; - min-width: 40rem; - font-size: min(16px, 1vw); } @@ -645,18 +627,32 @@ td .task-div { left: 0; z-index: 10; } -/* End of past week */ +/* Highlight boxes when hovering over specific calendar cell */ +.a-link:hover { + background-color: skyblue; + opacity: 0.3; +} + +/* ======== End of Past Week ========== */ + +/* ******** End of Main Wrap ********** */ + +/* ****** Start of Responsiveness ****** */ + +/* Add scroll bar to rating wiget when screen height is small */ @media (max-height: 740px) { .rating-widget { overflow: auto; } } +/* Slide task bar offscreen when screen width is small */ @media (max-width: 740px) { main { overflow-x: scroll; } + .task-list { flex: 0 0 25%; z-index: 100000; @@ -673,9 +669,11 @@ td .task-div { } .main-wrap { - transform: translateX(-160px); - - padding-left: max(2%, 0.5em); + position: absolute; + left: 3%; + bottom: 0; + top: 0; + right: 0; } .task-list.active { @@ -697,4 +695,6 @@ td .task-div { .table-week { font-size: 10px; } -} \ No newline at end of file +} + +/* ****** End of Responsiveness ****** */ \ No newline at end of file diff --git a/source/homepage/homepage.html b/source/homepage/homepage.html index 8ee40c0..879592d 100644 --- a/source/homepage/homepage.html +++ b/source/homepage/homepage.html @@ -156,7 +156,7 @@

Tasks Completed Today

- + \ No newline at end of file diff --git a/source/homepage/homepage.js b/source/homepage/homepage.js index 1ca105b..4bbf4a6 100644 --- a/source/homepage/homepage.js +++ b/source/homepage/homepage.js @@ -4,23 +4,20 @@ const PRODUCTIVITY_FILES_NAMES = ["1-icon.svg", "2-icon.svg", "3-icon.svg", "4-i window.addEventListener('DOMContentLoaded', init); -// Get current date globals +// Get current date global var currDate = new Date(); /** - * Initializes current date heading - * - * @returns {undefined} Nothing + * Initializes all necessary components */ function init() { dateQuery(); - // Display the current date displayDate(formatDate(currDate)); - displayWeek(); initButtons(); taskListViewHandler(); + // Configure going to today's homepage on refresh window.history.replaceState("stateObj", "new page", "../homepage/homepage.html"); @@ -34,10 +31,12 @@ function initButtons() { nextBtn.addEventListener("click", nextDate); const prevBtn = document.querySelector(".prev-date-btn"); prevBtn.addEventListener("click", prevDate); + const addTaskBtn = document.querySelector(".add-task-btn"); addTaskBtn.addEventListener("click", () => { addTask(); }); + const ratingSelBtn = document.querySelectorAll(".rating-select-btn"); ratingSelBtn.forEach(btn => { btn.addEventListener("click", () => { @@ -45,18 +44,29 @@ function initButtons() { selectWidget(id.substring(3,5)); }); }); + + // Add keyboard left, rigth arrow for switching dates window.addEventListener('keydown', function(event) { if ((event.target.tagName.toLowerCase() === "textarea") || (event.target.tagName.toLowerCase() === "div")) { return; } - if (event.key === "ArrowLeft") { prevDate(); } else if (event.key === "ArrowRight") { nextDate(); } }); + // Save user entry to local storage on any changes + journal.addEventListener("blur", saveJournal); + tasks.addEventListener("blur", saveTasks); + tasks.addEventListener("change", saveTasks); + tasks.addEventListener("blur", saveCompleted); + tasks.addEventListener("change", saveCompleted); + completedTasks.addEventListener("blur", saveCompleted); + completedTasks.addEventListener("change", saveCompleted); + completedTasks.addEventListener("blur", saveTasks); + completedTasks.addEventListener("change", saveTasks); } /** @@ -65,12 +75,8 @@ function initButtons() { * @param {string} date - date in string format */ function displayDate(date) { - // Display the date in the designated container const dateContainer = document.getElementById('current-date'); dateContainer.textContent = date; - - // Maybe need to call dateQuery() again depending on the type of functionality - // dateQuery(); } /** @@ -79,7 +85,8 @@ function displayDate(date) { function nextDate() { saveJournal(); let today = new Date(); - if (currDate.getDate() != today.getDate()) { + today.setHours(0, 0, 0, 0); + if (currDate <= today) { currDate.setDate(currDate.getDate() + 1); displayDate(formatDate(currDate)); } @@ -105,7 +112,7 @@ function prevDate() { * @returns {string} - properly formatted string representing the date as "Weekday, Month Day, Year" */ function formatDate() { - // Format the date (e.g., "May 8, 2024") + // Formats date as "Weekday, Month Date, Year" const formattedDate = currDate.toLocaleDateString('en-US', { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' }); return formattedDate; } @@ -114,16 +121,15 @@ function formatDate() { * Shows that a given button has been selected by adding the active property to its classname * * @param {int} buttonIndex - the index of the button selected. - * 1-5 for mental health, 6-10 for productivity */ function selectWidget(buttonIndex) { - // Clear active class from all buttons in row and - // Add active class to selected button if (buttonIndex > 5) { + // Clear active class from all buttons in row const buttons = document.querySelectorAll('.productiveness img'); buttons.forEach(button => { button.classList.remove('active'); }); + // Add active class to selected button const selection = document.querySelector(`.rating-widget .productiveness button:nth-child(${buttonIndex - 5}) img`); selection.classList.add('active'); saveWidgets(buttonIndex); @@ -159,22 +165,23 @@ function selectWidget(buttonIndex) { - Remove + Remove `); task.querySelector(".task-input").addEventListener("input", saveCompleted) - - taskList.append(task); // Listener to stop editing when user presses enter const task_name = task.querySelector(".task-input"); task_name.addEventListener('keydown', function (event) { + // Shift+Enter pressed, insert a line break if (event.key == 'Enter') { + // Enter pressed, end editing if (!event.shiftKey) { - // Shift+Enter pressed, insert a line break - // Enter pressed, end editing - event.preventDefault(); // Prevent default behavior of Enter key - task_name.blur(); // Remove focus from the element + // Prevent default behavior of Enter key + event.preventDefault(); + + // Remove focus from the element + task_name.blur(); } } }); @@ -190,9 +197,7 @@ function selectWidget(buttonIndex) { }, 0); } - // add functionality to task buttons taskButtonsFunctionality(task); - if (loadTask == false){ saveTasks(); } @@ -202,11 +207,11 @@ function selectWidget(buttonIndex) { /** * Adds button functionality to task upon creation + * * @param {HTMLElement} task - the task to have functionality */ function taskButtonsFunctionality(task) { - - /* Implement color changing functionality */ + // Implement color changing functionality const colorBtns = task.querySelectorAll(".color-button"); colorBtns.forEach(btn => { btn.addEventListener('click', function() { @@ -233,20 +238,17 @@ function taskButtonsFunctionality(task) { }); }); - /* Trash icon delete functionality */ - const deleteIcon = task.querySelector(".fas"); + // Trash icon delete functionality + const deleteIcon = task.querySelector(".trash-icon"); deleteIcon.addEventListener("click", () => { task.remove(); saveCompleted(); saveTasks(); }); - /* Checkbox move to Completed Tasks functionality */ + // Checkbox move to Completed Tasks functionality const checkbox = task.querySelector(".task-checkbox"); checkbox.addEventListener('click', function() { - // Add or remove completed from class name - // Find closest li item (task) - if (task.className.includes('complete')) { task.classList.remove('complete'); const taskContainer = document.querySelector('.task-container'); @@ -286,28 +288,21 @@ function taskButtonsFunctionality(task) { * Updates interface with Past Week view */ function displayWeek() { - - // initialize days of the week let allDays = ["Sun", "Mon", "Tue", "Wed","Thu", "Fri", "Sat"]; // Get and clear the table let table = document.getElementById("week-calendar"); table.innerHTML = ""; - // Copy the global date into local variable let currWeekDay = new Date(); currWeekDay.setDate(currWeekDay.getDate() + 1); - // BUILD CALENDAR - // Create row let row = document.createElement("tr"); - // Loop through number of columns + // Initialize each past day for (let i = 0; i < 7; i++) { - // Create data for each table cell in the row let cellData = document.createElement("td"); - // Calculate dates if (i === 0){ currWeekDay.setDate(currWeekDay.getDate() + (i-8)); } @@ -315,19 +310,13 @@ function displayWeek() { currWeekDay.setDate(currWeekDay.getDate() + 1); } - // current cell Date let cellNum = document.createElement('span'); cellNum.textContent = allDays[currWeekDay.getDay()] + " " + (currWeekDay.getMonth()+1) + "/" + currWeekDay.getDate(); cellNum.className = "cell-date"; - - // Append cell number to new cell cellData.appendChild(cellNum); loadCellData(cellData, currWeekDay); - - // Append new cell to row row.appendChild(cellData); } - // Append row to table table.appendChild(row); } @@ -352,6 +341,15 @@ window.onbeforeunload = function () { saveCompleted() } +/** + * Load all data from local storage + */ +function loadAll() { + loadJournal(); + loadWidgets(); + loadCompleted(); +} + /** * Format journal input to be stored * @@ -361,7 +359,7 @@ window.onbeforeunload = function () { * @param {string} value - value to store * */ -function saveToStorage(data, dateText, key, value) { +export function saveToStorage(data, dateText, key, value) { if (!(dateText in data)) { data[dateText] = {} } @@ -375,9 +373,9 @@ function saveToStorage(data, dateText, key, value) { * @param {string} dateText - date of the journal entry in locale date string format * @param {string} key - key to get the value from */ -function loadFromStorage(data, dateText, key) { +export function loadFromStorage(data, dateText, key) { if (!(dateText in data)) { - return; + return null; } return data[dateText][key]; } @@ -397,7 +395,7 @@ function saveJournal() { * * @returns {string} journal entry text in parsed json format */ -function getJournal() { +export function getJournal() { let data = JSON.parse(localStorage.getItem("journals")) if (data == null) { data = {} @@ -420,7 +418,6 @@ function loadJournal() { function saveTasks() { let tasks = []; document.querySelectorAll('.task-container li').forEach(task => { - //let checkbox = task.querySelector('input[type="task-checkbox"]'); let taskName = task.querySelector('.task-input').textContent; let taskColor = task.style['background-color'] tasks.push({ @@ -428,7 +425,9 @@ function saveTasks() { color: taskColor, }); }); + localStorage.setItem("tasks", JSON.stringify(tasks)); + displayWeek(); } @@ -447,6 +446,7 @@ function getTasks() { */ function loadTasks() { let tasks = getTasks(); + if (tasks.length > 0) { tasks.forEach(task => { let curLi = addTask(true); @@ -458,12 +458,13 @@ function loadTasks() { } /** - * Saves the completed tasks per day + * Saves the completed tasks and updates Past Week view */ function saveCompleted() { let data = getJournal(); let completedTask = []; let dateText = new Date(date.textContent).toLocaleDateString(); + document.querySelectorAll('.completed-task-container li').forEach(completedTaskElement => { let taskName = completedTaskElement.querySelector('.task-input').textContent; let taskColor = completedTaskElement.style['background-color'] @@ -472,8 +473,10 @@ function saveCompleted() { color: taskColor, }); }); + saveToStorage(data, dateText, "completedTasks", completedTask); localStorage.setItem("journals", JSON.stringify(data)); + displayWeek(); } @@ -501,7 +504,6 @@ function loadCompleted() { curLi.querySelector(".task-input").textContent = task['text'] curLi.style['background-color'] = task['color'] curLi.classList.add('complete') - //curLi.querySelector('input[type="checkbox"]').checked = task['checked'] }); } } @@ -516,7 +518,7 @@ function unselectAllCompleted() { } /** - * Save widgets to local storage + * Save widgets to local storage and updates Past Week view * * @param {int} value - ID value of the widget selected */ @@ -529,18 +531,21 @@ function saveWidgets(value) { else { saveToStorage(data, dateText, "productivity", value); } + localStorage.setItem("journals", JSON.stringify(data)); + displayWeek(); } /** - * Load widget ratings from local storage + * Load widget ratings from local storage and update interface */ function loadWidgets() { let data = getJournal(); let dateText = new Date(date.textContent).toLocaleDateString(); let rating = loadFromStorage(data, dateText, "rating"); let productivity = loadFromStorage(data, dateText, "productivity"); + if (rating != null) { selectWidget(rating); } @@ -550,16 +555,7 @@ function loadWidgets() { } /** - * Load all data from local storage - */ -function loadAll() { - loadJournal(); - loadWidgets(); - loadCompleted(); -} - -/** - * + * Fetches data from local storage and populates Past Week view * @param {HTMLElement} cellData - Data for specified day * @param {Date} currWeekDay - Date to populate data within */ @@ -572,33 +568,36 @@ function loadCellData(cellData, currWeekDay) { let tasks = loadFromStorage(journals, dateText, "completedTasks"); if (rating != null) { + // Add sentiment icon let sentimentIcon = document.createElement("img"); sentimentIcon.src = `../icons/${RATING_FILES_NAMES[rating - 1]}`; sentimentIcon.alt = "sentiment icon"; sentimentIcon.className = "sentiment-icon"; + // Append sentiment icon to new cell cellData.appendChild(sentimentIcon); } if (productivity != null) { + // Add productivity icon let productivityIcon = document.createElement("img"); productivityIcon.src = `../icons/${PRODUCTIVITY_FILES_NAMES[productivity - 1 - 5]}`; productivityIcon.alt = "productivity icon"; productivityIcon.className = "productivity-icon"; + // Append sentiment icon to new cell cellData.appendChild(productivityIcon); } // Add tasklist in calendar cell - // Create tasklist div let taskDiv = document.createElement("div"); taskDiv.className = "task-div"; - // Create unordered list let taskList = document.createElement("ul"); taskList.className = "task-ul"; + // Format task if (tasks != null) { for (let i = 0; i < tasks.length && i < DISPLAY_TASK_COUNT; i++) { let taskItem = document.createElement("li"); @@ -608,8 +607,8 @@ function loadCellData(cellData, currWeekDay) { taskList.appendChild(taskItem); } + // Extra tasks are indicated but not displayed if (tasks.length > DISPLAY_TASK_COUNT) { - // extra tasks let taskExtra = document.createElement("li"); taskExtra.textContent = `${tasks.length - DISPLAY_TASK_COUNT} more tasks`; taskExtra.className = "task-indicator"; @@ -627,21 +626,22 @@ function loadCellData(cellData, currWeekDay) { aLink.href = `../homepage/homepage.html?date=${monthLink}-${dayLink}-${yearLink}`; aLink.className = "a-link"; cellData.appendChild(aLink); + // Append taskList to task div; taskDiv.appendChild(taskList); + // Append tasklist div to new cell cellData.appendChild(taskDiv); } /** - * Implements date query to link to certain date + * Links calendar cell to homepage on corresponding date */ function dateQuery() { // Extract query from the page let params = new URLSearchParams(window.location.search); let date = params.get("date"); - // If a date query exists if (date) { let components = date.split('-'); currDate = new Date(components[2], components[0], components[1]); @@ -649,12 +649,13 @@ function dateQuery() { } /** - * + * Expands task list from collapsed state */ function taskListViewHandler() { const taskList = document.querySelector('.task-list'); const taskWrap = document.querySelector('.task-wrapper'); const outSide = document.querySelector('.main-wrap'); + taskList.addEventListener('click', function(event) { if (event.target === taskList) { if (window.innerWidth <= 800) { @@ -663,21 +664,11 @@ function taskListViewHandler() { } } }); + outSide.addEventListener('click', function(){ if (window.innerWidth <= 800) { taskList.classList.remove('active'); taskWrap.classList.remove('active'); } }); -} - -// Save journal entry and tasks to local storage on events -journal.addEventListener("blur", saveJournal); -tasks.addEventListener("blur", saveTasks); -tasks.addEventListener("change", saveTasks); -tasks.addEventListener("blur", saveCompleted); -tasks.addEventListener("change", saveCompleted); -completedTasks.addEventListener("blur", saveCompleted); -completedTasks.addEventListener("change", saveCompleted); -completedTasks.addEventListener("blur", saveTasks); -completedTasks.addEventListener("change", saveTasks); \ No newline at end of file +} \ No newline at end of file diff --git a/testing/puppeteer.test.js b/testing/puppeteer.test.js index 1832798..1746e22 100644 --- a/testing/puppeteer.test.js +++ b/testing/puppeteer.test.js @@ -306,8 +306,8 @@ describe('Homepage task list tests', () => { // Expect the task count to increase by 1 after clicking the add button expect(taskCount).toBe(2); - await page.click('.task-container .task .fas.fa-trash-alt'); - await page.click('.task-container .task .fas.fa-trash-alt'); + await page.click('.task-container .task .trash-icon'); + await page.click('.task-container .task .trash-icon'); const taskCountAfterDelete = await page.evaluate(() => { return document.querySelectorAll('.task-container .task').length; }); @@ -341,42 +341,117 @@ describe('Homepage task list tests', () => { * * COMPLETED TASK TESTS */ -// describe('Completing tasks', () => { -// it('Add task and mark complete', async () => { -// console.log('Adding tasks and moving to completed...'); -// // Click the "Add Task" button -// await page.click('.add-task-btn'); -// // Type into title -// await page.keyboard.type('Completed Task'); -// // Change the color -// const colorButton = '.task-container .task:last-child .color-buttons'; -// const colorButtonSelector = '.task-container .task:last-child .color-button'; -// await page.hover(colorButton); -// await page.click(colorButtonSelector); -// // Move to completed task list -// await page.click('#task3 #complete3'); -// //Check there are no tasks in task list -// const taskCountAfterComplete = await page.evaluate(() => { -// return document.querySelectorAll('.task-container .task').length; -// }); -// expect(taskCountAfterComplete).toBe(0); -// // Check there is the correct task in completed tasks -// const completeTaskCountAfterComplete = await page.evaluate(() => { -// return document.querySelectorAll('.completed-task-container .task').length; -// }); -// expect(completeTaskCountAfterComplete).toBe(1); -// // Check the title and color of task in completed list -// const taskTitle = await page.evaluate(selector => { -// return document.querySelector(selector).textContent; -// }, '#task3 .task-input'); -// const backgroundColor = await page.evaluate(selector => { -// const task = document.querySelector(selector); -// return window.getComputedStyle(task).backgroundColor; -// }, '.completed-task-container .task:last-child'); -// expect(backgroundColor).toBe('rgb(195, 128, 204)'); -// expect(taskTitle).toBe('Completed Task'); -// }); -// }); +describe('Completing tasks', () => { + it('Add task and mark complete', async () => { + console.log('Adding tasks and moving to completed...'); + + // Click the "Add Task" button + await page.click('.add-task-btn'); + // Type into title + await page.keyboard.type('Completed Task'); + + // Change the color + const colorButton = '.task-container .task:last-child .color-buttons'; + const colorButtonSelector = '.task-container .task:last-child .color-button'; + await page.hover(colorButton); + await page.click(colorButtonSelector); + + // Move to completed task list + await page.click('.task-checkbox'); + + //Check there are no tasks in task list + const taskCountAfterComplete = await page.evaluate(() => { + return document.querySelectorAll('.task-container .task').length; + }); + expect(taskCountAfterComplete).toBe(0); + + // Check there is the correct task in completed tasks + const completeTaskCountAfterComplete = await page.evaluate(() => { + return document.querySelectorAll('.completed-task-container .task').length; + }); + expect(completeTaskCountAfterComplete).toBe(1); + + // Check the title and color of task in completed list + const taskTitle = await page.evaluate(selector => { + return document.querySelector(selector).textContent; + }, '.completed-task-container .task .task-input'); + const backgroundColor = await page.evaluate(selector => { + const task = document.querySelector(selector); + return window.getComputedStyle(task).backgroundColor; + }, '.completed-task-container .task:last-child'); + expect(backgroundColor).toBe('rgb(195, 128, 204)'); + expect(taskTitle).toBe('Completed Task'); + }); + + // Change title and color of task + it('Editing task in completed container', async () => { + console.log('Editing task in completed task container...'); + + // Click into title and add more text then press enter to leave + await page.click('.completed-task-container .task .task-input'); + await page.keyboard.type(' is now edited >:)'); + // await page.keyboard.press('Enter'); + + // Hovering over the color button and selecting a new color + const colorButton = '.completed-task-container .task:last-child .color-buttons'; + await page.hover(colorButton); + + await page.evaluate(() => { + const colorButtonSelector = '.completed-task-container .task:last-child .color-buttons #blue'; + document.querySelector(colorButtonSelector).click(); + }); + + // Check the title and color of task in completed list + const taskTitle = await page.evaluate(selector => { + return document.querySelector(selector).textContent; + }, '.completed-task-container .task .task-input'); + const backgroundColor = await page.evaluate(selector => { + const task = document.querySelector(selector); + return window.getComputedStyle(task).backgroundColor; + }, '.completed-task-container .task:last-child'); + expect(backgroundColor).toBe('rgb(107, 177, 217)'); + expect(taskTitle).toBe('Completed Task is now edited >:)'); + }); + + // Reload page and ensure task is saved + it('Reload and check task', async () => { + console.log('Reloading page...'); + + // Reload the page + await page.reload(); + + // Task count should still be 1 + const completeTaskCountAfterComplete = await page.evaluate(() => { + return document.querySelectorAll('.completed-task-container .task').length; + }); + expect(completeTaskCountAfterComplete).toBe(1); + + // Check the title and color of task in completed list + const taskTitle = await page.evaluate(selector => { + return document.querySelector(selector).textContent; + }, '.completed-task-container .task .task-input'); + const backgroundColor = await page.evaluate(selector => { + const task = document.querySelector(selector); + return window.getComputedStyle(task).backgroundColor; + }, '.completed-task-container .task:last-child'); + expect(backgroundColor).toBe('rgb(107, 177, 217)'); + expect(taskTitle).toBe('Completed Task is now edited >:)'); + }); + + // Delete the task to not interfere with future tests + it('Delete task', async () => { + console.log('Deleting task...'); + + // Delete task from completed tasks + await page.click('.completed-task-container .task .trash-icon'); + + // Task count should be 0 + const completeTaskCountAfterComplete = await page.evaluate(() => { + return document.querySelectorAll('.completed-task-container .task').length; + }); + expect(completeTaskCountAfterComplete).toBe(0); + }); +}); /** @@ -518,9 +593,7 @@ describe('Homepage Top-Bar functionality', () => { const updatedTodaysDateText = await page.$eval('.date-header-text', (el) => { return el.textContent; }); - console.log("updatedTodays: ", updatedTodaysDateText); const currDate = new Date(); - console.log("yuoooooo", currDate); const currDateText = currDate.toLocaleDateString('en-US', { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' }); expect(updatedTodaysDateText).toBe(currDateText); @@ -737,4 +810,492 @@ describe('Past Week View testing', () => { expect(sentimentRating).toBe("../icons/3neutral.png"); expect(productivityRating).toBe("../icons/3-icon.svg"); }); -}); \ No newline at end of file +}); + // =========================== CALENDAR TESTS =================================== + +describe('Basic user path in calendar', () => { + // Open the webpage + beforeAll(async () => { + await page.goto('http://127.0.0.1:5500/source/calendar/calendar.html'); + }); + // Edit Journal + it('Add a task and check addition', async () => { + console.log('Testing task addition...'); + // Click the "Add Task" button + await page.click('.add-task-btn'); + // Check the number of tasks in the task-container + const taskCount = await page.evaluate(() => { + return document.querySelectorAll('.task-list li').length; + }); + // Expect the task count to increase by 1 after clicking the add button + expect(taskCount).toBe(1); // Modify the expected value based on initial number of tasks + }); + + it('Add a task, write the task, and choose a color', async () => { + console.log('Testing task addition, title setting, and color selection...'); + //Clcik the "Add Task" button + await page.click('.add-task-btn'); + + const taskInputSelector = '.task-list .task:last-child .task-input'; + await page.waitForSelector(taskInputSelector); + const taskInput = await page.$(taskInputSelector); + //type in the tasklist + const taskTitle = 'New Task Title for Testing'; + await taskInput.type(taskTitle); + //click a color button + const colorButton = '.task-list .task:last-child .color-button'; + const colorButtonSelector = '.task-list .task:last-child .color-button'; + await page.hover(colorButton); + await page.click(colorButtonSelector); + //expect the text on the tasklist to be added + const enteredTitle = await page.evaluate(selector => { + return document.querySelector(selector).textContent; + }, taskInputSelector); + //expect the color on the tasklist to be changed + const backgroundColor = await page.evaluate(selector => { + const task = document.querySelector(selector); + return window.getComputedStyle(task).backgroundColor; + }, '.task-list .task:last-child'); + + expect(enteredTitle).toBe(taskTitle); + expect(backgroundColor).toBe('rgb(195, 128, 204)'); + }, 500000); + + it('Edit the task and delete it', async() => { + console.log('Edit task and delete it'); + //select current tasklist recently made + const taskInputSelector = '.task-list .task:last-child .task-input'; + await page.waitForSelector(taskInputSelector); + const taskInput = await page.$(taskInputSelector); + //delete the whole title of the tasklist + for (let i = 0; i < 27; i++) { + await taskInput.press('Backspace'); + } + //add the title to tasklist + const taskTitle = 'Editing Task Title'; + await taskInput.type(taskTitle); + //change the color of tasklist + const colorButton = '.task-list .task:last-child .color-buttons'; + await page.hover(colorButton); + + await page.evaluate(() => { + const colorButtonSelector = '.task-list .task:last-child .color-buttons #blue'; + document.querySelector(colorButtonSelector).click(); + }, 50000); + //expect new title to be added in the tasklist + const enteredTitle = await page.evaluate(selector => { + return document.querySelector(selector).textContent; + }, taskInputSelector); + //expect new color to be added in the tasklist + const backgroundColor = await page.evaluate(selector => { + const task = document.querySelector(selector); + return window.getComputedStyle(task).backgroundColor; + }, '.task-list .task:last-child'); + + expect(enteredTitle).toBe(taskTitle); + expect(backgroundColor).toBe('rgb(107, 177, 217)'); + }, 50000); + + + it('Add a task a lot and then delete all after', async () => { + console.log('Testing task addition...'); + //check to see if there is any tasklist added + const isActive = await page.evaluate(() => { + return document.querySelector('.task-list').classList.contains('active'); + }); + if (!isActive) { + await page.evaluate(() => { + document.querySelector('.task-list').click(); + }); + } + + // Loop the click of the "Add Task" button + for(let i = 0; i < 25; i++) { + await page.click('.add-task-btn'); + + } + // Check the number of tasks in the task-container + const taskCount = await page.evaluate(() => { + return document.querySelectorAll('.task-list .task').length; + }); + + // Expect the task count to increase by 25 after the loop + expect(taskCount).toBe(27); // Modify the expected value based on initial number of tasks + //Loop the deletion of the tasklist by 27 after the loop + for(let i = 0; i < 27; i++) { + await page.click('.task-list .task .trash-icon'); + + } + //check to see if the tasklist length is zero + const taskCountAfterDelete = await page.evaluate(() => { + return document.querySelectorAll('.task-list .task').length; + }); + + expect(taskCountAfterDelete).toBe(0); + }, 50000); + + + + + it('Resize window', async () => { + console.log('Testing window resize'); + // Resize the window to a smaller size + await page.setViewport({ width: 600, height: 800 }); + // Check the class name for the task-list to see if it has moved + const fullCalendar = await page.evaluate(() => { + return document.querySelector('.task-list').className; + }); + //expect window sized to be changed + expect(fullCalendar.includes('active')).toBe(false); + }); + + + + + it('Click the previous date button 1 time', async () => { + console.log('Testing going back multiple days'); + await page.setViewport({ width: 1200, height: 1600 }); + + // Click prev date button + const prevDateBtn = await page.$('.prev-date-btn'); + await prevDateBtn.click(); + + + + // get displayed date at the top + const displayedDateText = await page.$eval("#month", (n) => { + return n.textContent; + }); + + // expect prevDate to change + let expectedDateText = "May"; + expect(displayedDateText).toBe(expectedDateText); + }); + + it('Click the next date button 1 time', async () => { + console.log('Testing going forward once from yesterday'); + + // Click next date button + const nextDateBtn = await page.$('.next-date-btn'); + await nextDateBtn.click(); + + + + // get displayed date at the top + const displayedDateText = await page.$eval("#month", (n) => { + return n.textContent; + }); + + // expect next date to change + let expectedDateText = "June"; + expect(displayedDateText).toBe(expectedDateText); + }); + + it('testing the dropdown menu for changing month', async () => { + console.log('Change to next month'); + await page.setViewport({ width: 1200, height: 1600 }); + + + // Click January in the dropdown menu + const jumpBtn = await page.$('.jump-btn'); + + await jumpBtn.hover(); + const monthButton = await page.$('.month-btn'); + + await monthButton.click(); + + + + // get displayed date at the top + const displayedDateText = await page.$eval("#month", (n) => { + return n.textContent; + }); + + // expect month header to be January + let expectedDateText = "January"; + expect(displayedDateText).toBe(expectedDateText); + }); + + it('testing if day retains homepage info', async () => { + console.log('Change to next month'); + //click on current day of the calendar + const dateButton = await page.$('a'); + await dateButton.click(); + + await page.waitForSelector('#textarea'); + const journal = await page.$('#textarea'); + // Click into text area + await journal.click(); + // Define strings to type into journal + const input_text = 'Example journal entry: I was so productive today!!'; + // Type into text area + for (let i = 0; i < 50; i++) { + await page.keyboard.press('Backspace'); + } + await page.keyboard.type(input_text); + //leave homepage and into calendar + await page.goto('http://127.0.0.1:5500/source/calendar/calendar.html'); + const navHomepage = await page.$('.nav-homepage-btn'); + //go back to the current day of the homepage + await navHomepage.click(); + await page.waitForSelector('#textarea'); + //add new input text + const journal2 = await page.$('#textarea'); + //get the value of the text area + const text = await journal2.getProperty('value'); + const journal_text = await text.jsonValue(); + + + // Expect journal text to be the same + expect(journal_text).toBe(input_text); + }, 500000); + + + + it('Add a 8-10 task, complete them, and check storage of task', async () => { + console.log('Testing task completion'); + await page.goto('http://127.0.0.1:5500/source/calendar/calendar.html'); + + + // Loop the click of the "Add Task" button + for(let i = 0; i < 8; i++) { + await page.click('.add-task-btn'); + + } + // Check the number of tasks in the task-container + const taskCount = await page.evaluate(() => { + return document.querySelectorAll('.task-list .task').length; + }); + + //Loop checkmarking the task + for(let i = 0; i < 8; i++) { + await page.click('.task-list .task .task-checkbox'); + + } + + await page.goto('http://127.0.0.1:5500/source/homepage/homepage.html'); + + await page.setViewport({ width: 1366, height: 768 }); + + //check to see if the tasklist completed is length 8 + const taskCountHomepage = await page.evaluate(() => { + return document.querySelectorAll('.completed-task-container li').length; + }); + + expect(taskCountHomepage).toBe(taskCount); + + }, 50000); + + it('check if calendar shows how many more tasks need to be done', async () => { + await page.goto('http://127.0.0.1:5500/source/calendar/calendar.html'); + + console.log('Testing how many tasks need to be done'); + + + //check to see if the Calendar tells how many more task done + const displayedText = await page.$eval(".task-indicator", (n) => { + return n.textContent; + }); + const expected = "2 more tasks"; + //expect calendar date box to say 6 more tasks + expect(displayedText).toBe(expected); + }, 50000); + + + it('check to see future days are unavailable to be clicked', async () => { + + console.log('Testing future days are unavailable to be clicked'); + //click future date + const dateButton = await page.$('.future-date'); + await dateButton.click(); + + //check if we're still in the calendar page + const displayedDateText = await page.$eval("#month", (n) => { + return n.textContent; + }); + const expected = "June"; + //expect the display date text to still be june + expect(displayedDateText).toBe(expected); + }, 50000); + + it('check to see past days are available to be clicked', async () => { + + console.log('Testing past days are available to be clicked'); + //click a past date + const dateButton = await page.$('.past-date'); + await dateButton.click(); + const input_text = "Past day"; + //check if we're still in the homepage + await page.waitForSelector('#textarea'); + //add new input text + const journal = await page.$('#textarea'); + await journal.click(); + await page.keyboard.type(input_text); + //get the value of the text area + const text = await journal.getProperty('value'); + const journal_text = await text.jsonValue(); + //expect journel text to be filled in + expect(journal_text).toBe(input_text); + }, 50000); + + it('test sentiment and productive icon', async () => { + + console.log('Testing feelings widget and productive'); + await page.goto('http://127.0.0.1:5500/source/homepage/homepage.html'); + + // Click happiest rating button + const happy = await page.$('#btn4'); + await happy.click(); + //clcik productive rating button + const four = await page.$('#btn9'); + await four.click(); + //check if class is active + const class_name = await page.evaluate(() => { + const img = document.querySelector('#btn4 img'); + return img.className; + }); + + const class_name2 = await page.evaluate(() => { + const img = document.querySelector('#btn9 img'); + return img.className; + }); + + // Expect active got added to happy class name + expect(class_name).toBe("active"); + expect(class_name2).toBe("active"); + + }, 50000); + + it('test sentiment and productive icon stays when leaving', async () => { + + console.log('Testing feelings widget and productive'); + //leave page to check if input is saved + await page.goto('http://127.0.0.1:5500/source/calendar/calendar.html'); + await page.goto('http://127.0.0.1:5500/source/homepage/homepage.html'); + //check if class is active + const class_name = await page.evaluate(() => { + const img = document.querySelector('#btn4 img'); + return img.className; + }); + + const class_name2 = await page.evaluate(() => { + const img = document.querySelector('#btn9 img'); + return img.className; + }); + + // Expect active got added to happy class name + expect(class_name).toBe("active"); + expect(class_name2).toBe("active"); + + + }, 50000); + + + it('Click homepage icon', async () => { + //get date header + const todaysDateText = page.$eval('.date-header-text', (el) => { + return el.textContent; + }); + //click previous button 4 times + for (let i = 0; i < 4; i++) { await page.click('.prev-date-btn'); } + //go to homepage + await page.click('.nav-homepage-btn'); + //get todays datetext + const updatedTodaysDateText = await page.$eval('.date-header-text', (el) => { + return el.textContent; + }); + //format to get todays date + const currDate = new Date(); + const currDateText = currDate.toLocaleDateString('en-US', { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' }); + + expect(updatedTodaysDateText).toBe(currDateText); + }); + + it('Click calendar icon', async () => { + //get todays date text + const todaysDateText = page.$eval('.date-header-text', (el) => { + return el.textContent; + }); + //click previous button and go to calendar page + for (let i = 0; i < 10; i++) { await page.click('.prev-date-btn'); } + await page.click('.nav-calendar-btn'); + //get month in the page + const updatedTodaysDateText = await page.$eval('#month', (el) => { + return el.textContent; + }); + //get todays month + const currDate = new Date(); + const currDateText = currDate.toLocaleDateString('en-US', { month: 'long'}); + //expect to get todays month + expect(updatedTodaysDateText).toBe(currDateText); + }, 50000); + + + it('Reload and check task', async () => { + console.log('Reloading page...'); + //add a task + await page.click('.add-task-btn'); + //get task title + const taskInputSelector = '.task-list .task:last-child .task-input'; + await page.waitForSelector(taskInputSelector); + const taskInput = await page.$(taskInputSelector); + //type in the tasklist + const title = 'Completed Task is now edited >:('; + await taskInput.type(title); + //click a color button + const colorButton = '.task-list .task:last-child .color-button'; + const colorButtonSelector = '.task-list .task:last-child .color-button'; + await page.hover(colorButton); + await page.click(colorButtonSelector); + + // Reload the page + await page.reload(); + + // Task count should still be 1 + const taskcount = await page.evaluate(() => { + return document.querySelectorAll('.task-list .task').length; + }); + expect(taskcount).toBe(1); + + // Check the title and color of task in completed list + const taskTitle = await page.evaluate(selector => { + return document.querySelector(selector).textContent; + }, '.task-list .task .task-input'); + const backgroundColor = await page.evaluate(selector => { + const task = document.querySelector(selector); + return window.getComputedStyle(task).backgroundColor; + }, '.task-list .task:last-child'); + expect(backgroundColor).toBe('rgb(195, 128, 204)'); + expect(taskTitle).toBe('Completed Task is now edited >:('); + }, 50000); + + it('Reload and go to homepage and check task', async () => { + console.log('Reloading page...'); + //reload page and go to homepage + await page.reload(); + await page.goto('http://127.0.0.1:5500/source/homepage/homepage.html'); + await page.click('.completed-task-container .task .task-input'); + //type in tasklist + await page.keyboard.type('Completed Task is now edited >:)'); + // Reload the page + await page.reload(); + + + // Task count completed should be 8 + const completeTaskCountAfterComplete = await page.evaluate(() => { + return document.querySelectorAll('.completed-task-container .task').length; + }); + expect(completeTaskCountAfterComplete).toBe(8); + + // Check the title and color of task in completed list + const taskTitle = await page.evaluate(selector => { + return document.querySelector(selector).textContent; + }, '.completed-task-container .task .task-input'); + const backgroundColor = await page.evaluate(selector => { + const task = document.querySelector(selector); + return window.getComputedStyle(task).backgroundColor; + }, '.completed-task-container .task:last-child'); + expect(backgroundColor).toBe('rgb(242, 242, 242)'); + expect(taskTitle).toBe('Completed Task is now edited >:)'); + }); +}); diff --git a/testing/sum_tests.test.js b/testing/sum_tests.test.js deleted file mode 100644 index c482d20..0000000 --- a/testing/sum_tests.test.js +++ /dev/null @@ -1,3 +0,0 @@ -test('adds 2 + 2 to equal 4', () => { - expect(2+2).toBe(4); -}); \ No newline at end of file diff --git a/testing/unit.test.js b/testing/unit.test.js new file mode 100644 index 0000000..b4c79c2 --- /dev/null +++ b/testing/unit.test.js @@ -0,0 +1,55 @@ +// storage.test.js + +import { + saveToStorage, + loadFromStorage, + getJournal + +} from '../source/homepage/homepage.js'; + +beforeEach(() => { +}); + +test('Single saveToStorage and loadFromStorage', () => { + let journals = []; + let dateText = "5/30/2024"; + saveToStorage(journals, dateText, "rating", 5) + expect(loadFromStorage(journals, dateText, "rating")).toBe(5); +}); + +test('Multiple saveToStorage and loadFromStorage', () => { + let journals = []; + let dateText = "5/30/2024"; + saveToStorage(journals, dateText, "rating", 5) + saveToStorage(journals, dateText, "productivity", 8) + let dateText2 = "5/31/2024"; + saveToStorage(journals, dateText2, "rating", 3) + saveToStorage(journals, dateText2, "productivity", 7) + expect(loadFromStorage(journals, dateText, "rating")).toBe(5); + expect(loadFromStorage(journals, dateText, "productivity")).toBe(8); + expect(loadFromStorage(journals, dateText2, "rating")).toBe(3); + expect(loadFromStorage(journals, dateText2, "productivity")).toBe(7); +}); + +test('loadFromStorage null check', () => { + let journals = []; + let dateText = "4/1/2024"; + expect(loadFromStorage(journals, dateText, "rating")).toBe(null); +}); + +test('getJournal null check', () => { + expect(getJournal()).toStrictEqual({}); + + +}); + +test('getJournal', () => { + let journals = { + "5/30/2024": { + rating: 5, + productivity: 8 + } + }; + localStorage.setItem("journals", JSON.stringify(journals)); + expect(getJournal()).toStrictEqual(journals); +}); \ No newline at end of file