mirror of
https://github.com/taskgenius/taskgenius-plugin.git
synced 2026-07-22 06:40:25 +00:00
feat: support hide weekends in both forecast or events view
This commit is contained in:
parent
325096d4b6
commit
f83052749b
19 changed files with 1210 additions and 34 deletions
71
debug-settings.js
Normal file
71
debug-settings.js
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
// Debug script to check weekend hiding settings
|
||||
// Run this in the browser console when the plugin is loaded
|
||||
|
||||
function debugWeekendSettings() {
|
||||
console.log("=== Weekend Hiding Debug ===");
|
||||
|
||||
// Try to find the plugin instance
|
||||
const plugin = app.plugins.plugins['task-progress-bar'];
|
||||
|
||||
if (!plugin) {
|
||||
console.error("Plugin not found!");
|
||||
return;
|
||||
}
|
||||
|
||||
console.log("Plugin found:", plugin);
|
||||
console.log("Plugin settings:", plugin.settings);
|
||||
|
||||
// Check view configurations
|
||||
const viewConfigs = plugin.settings.viewConfiguration;
|
||||
console.log("All view configurations:", viewConfigs);
|
||||
|
||||
// Find calendar view config
|
||||
const calendarConfig = viewConfigs.find(v => v.id === 'calendar');
|
||||
console.log("Calendar view config:", calendarConfig);
|
||||
|
||||
if (calendarConfig && calendarConfig.specificConfig) {
|
||||
console.log("Calendar specific config:", calendarConfig.specificConfig);
|
||||
console.log("hideWeekends setting:", calendarConfig.specificConfig.hideWeekends);
|
||||
} else {
|
||||
console.log("No calendar specific config found!");
|
||||
}
|
||||
|
||||
// Check DOM elements
|
||||
const calendarContainers = document.querySelectorAll('.full-calendar-container');
|
||||
console.log("Found calendar containers:", calendarContainers.length);
|
||||
|
||||
calendarContainers.forEach((container, index) => {
|
||||
console.log(`Container ${index}:`, container);
|
||||
console.log(`Has hide-weekends class:`, container.classList.contains('hide-weekends'));
|
||||
|
||||
const weekdayHeaders = container.querySelectorAll('.calendar-weekday');
|
||||
console.log(`Weekday headers count:`, weekdayHeaders.length);
|
||||
|
||||
const dayCells = container.querySelectorAll('.calendar-day-cell');
|
||||
console.log(`Day cells count:`, dayCells.length);
|
||||
|
||||
// Check for weekend day cells
|
||||
const weekendCells = Array.from(dayCells).filter(cell => {
|
||||
const dateStr = cell.getAttribute('data-date');
|
||||
if (dateStr) {
|
||||
const date = new Date(dateStr);
|
||||
const dayOfWeek = date.getDay();
|
||||
return dayOfWeek === 0 || dayOfWeek === 6; // Sunday or Saturday
|
||||
}
|
||||
return false;
|
||||
});
|
||||
console.log(`Weekend cells found:`, weekendCells.length);
|
||||
});
|
||||
|
||||
console.log("=== End Debug ===");
|
||||
}
|
||||
|
||||
// Auto-run if in browser console
|
||||
if (typeof window !== 'undefined') {
|
||||
debugWeekendSettings();
|
||||
}
|
||||
|
||||
// Export for Node.js if needed
|
||||
if (typeof module !== 'undefined') {
|
||||
module.exports = debugWeekendSettings;
|
||||
}
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
module.exports = {
|
||||
preset: "ts-jest",
|
||||
testEnvironment: "node",
|
||||
testEnvironment: "jsdom",
|
||||
testMatch: ["**/__tests__/**/*.test.ts"],
|
||||
moduleNameMapper: {
|
||||
"^obsidian$": "<rootDir>/src/__mocks__/obsidian.ts",
|
||||
|
|
@ -20,4 +20,5 @@ module.exports = {
|
|||
],
|
||||
},
|
||||
moduleFileExtensions: ["ts", "tsx", "js", "jsx", "json", "node"],
|
||||
setupFilesAfterEnv: ["<rootDir>/src/test-setup.ts"],
|
||||
};
|
||||
|
|
|
|||
226
manual-weekend-test.html
Normal file
226
manual-weekend-test.html
Normal file
|
|
@ -0,0 +1,226 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Weekend Hiding Test</title>
|
||||
<style>
|
||||
body {
|
||||
font-family: Arial, sans-serif;
|
||||
margin: 20px;
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
|
||||
.test-container {
|
||||
background: white;
|
||||
padding: 20px;
|
||||
border-radius: 8px;
|
||||
margin-bottom: 20px;
|
||||
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
||||
}
|
||||
|
||||
.calendar-container {
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 4px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.calendar-weekday-header {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(7, 1fr);
|
||||
background-color: #f8f9fa;
|
||||
border-bottom: 1px solid #ddd;
|
||||
}
|
||||
|
||||
.calendar-weekday-header.hide-weekends {
|
||||
grid-template-columns: repeat(5, 1fr);
|
||||
}
|
||||
|
||||
.calendar-weekday {
|
||||
padding: 10px;
|
||||
text-align: center;
|
||||
font-weight: bold;
|
||||
border-right: 1px solid #ddd;
|
||||
}
|
||||
|
||||
.calendar-weekday:last-child {
|
||||
border-right: none;
|
||||
}
|
||||
|
||||
.calendar-month-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(7, 1fr);
|
||||
gap: 1px;
|
||||
background-color: #ddd;
|
||||
}
|
||||
|
||||
.calendar-month-grid.hide-weekends {
|
||||
grid-template-columns: repeat(5, 1fr);
|
||||
}
|
||||
|
||||
.calendar-day-cell {
|
||||
background: white;
|
||||
padding: 10px;
|
||||
min-height: 60px;
|
||||
border: 1px solid #eee;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.calendar-day-number {
|
||||
font-weight: bold;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.test-result {
|
||||
margin-top: 10px;
|
||||
padding: 10px;
|
||||
border-radius: 4px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.test-pass {
|
||||
background-color: #d4edda;
|
||||
color: #155724;
|
||||
border: 1px solid #c3e6cb;
|
||||
}
|
||||
|
||||
.test-fail {
|
||||
background-color: #f8d7da;
|
||||
color: #721c24;
|
||||
border: 1px solid #f5c6cb;
|
||||
}
|
||||
|
||||
button {
|
||||
background-color: #007bff;
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 10px 20px;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
margin: 5px;
|
||||
}
|
||||
|
||||
button:hover {
|
||||
background-color: #0056b3;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Weekend Hiding Test</h1>
|
||||
|
||||
<div class="test-container">
|
||||
<h2>Test 1: 7-Day Calendar (Weekends Shown)</h2>
|
||||
<button onclick="renderCalendar('test1', false)">Render 7-Day Calendar</button>
|
||||
<div id="test1" class="calendar-container"></div>
|
||||
<div id="test1-result"></div>
|
||||
</div>
|
||||
|
||||
<div class="test-container">
|
||||
<h2>Test 2: 5-Day Calendar (Weekends Hidden)</h2>
|
||||
<button onclick="renderCalendar('test2', true)">Render 5-Day Calendar</button>
|
||||
<div id="test2" class="calendar-container"></div>
|
||||
<div id="test2-result"></div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function renderCalendar(containerId, hideWeekends) {
|
||||
const container = document.getElementById(containerId);
|
||||
const resultDiv = document.getElementById(containerId + '-result');
|
||||
|
||||
// Clear container
|
||||
container.innerHTML = '';
|
||||
|
||||
// Create weekday header
|
||||
const header = document.createElement('div');
|
||||
header.className = 'calendar-weekday-header';
|
||||
if (hideWeekends) {
|
||||
header.classList.add('hide-weekends');
|
||||
}
|
||||
|
||||
// Weekday names
|
||||
const allWeekdays = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];
|
||||
const workdays = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri'];
|
||||
|
||||
const weekdaysToShow = hideWeekends ? workdays : allWeekdays;
|
||||
|
||||
weekdaysToShow.forEach(day => {
|
||||
const dayEl = document.createElement('div');
|
||||
dayEl.className = 'calendar-weekday';
|
||||
dayEl.textContent = day;
|
||||
header.appendChild(dayEl);
|
||||
});
|
||||
|
||||
container.appendChild(header);
|
||||
|
||||
// Create month grid
|
||||
const grid = document.createElement('div');
|
||||
grid.className = 'calendar-month-grid';
|
||||
if (hideWeekends) {
|
||||
grid.classList.add('hide-weekends');
|
||||
}
|
||||
|
||||
// Generate sample days (first week of January 2024)
|
||||
// January 1, 2024 is a Monday
|
||||
const sampleDays = [
|
||||
{ date: 1, isWeekend: false }, // Mon
|
||||
{ date: 2, isWeekend: false }, // Tue
|
||||
{ date: 3, isWeekend: false }, // Wed
|
||||
{ date: 4, isWeekend: false }, // Thu
|
||||
{ date: 5, isWeekend: false }, // Fri
|
||||
{ date: 6, isWeekend: true }, // Sat
|
||||
{ date: 7, isWeekend: true }, // Sun
|
||||
{ date: 8, isWeekend: false }, // Mon
|
||||
{ date: 9, isWeekend: false }, // Tue
|
||||
{ date: 10, isWeekend: false }, // Wed
|
||||
{ date: 11, isWeekend: false }, // Thu
|
||||
{ date: 12, isWeekend: false }, // Fri
|
||||
{ date: 13, isWeekend: true }, // Sat
|
||||
{ date: 14, isWeekend: true }, // Sun
|
||||
];
|
||||
|
||||
sampleDays.forEach(day => {
|
||||
// Skip weekend days if hideWeekends is true
|
||||
if (hideWeekends && day.isWeekend) {
|
||||
return;
|
||||
}
|
||||
|
||||
const dayCell = document.createElement('div');
|
||||
dayCell.className = 'calendar-day-cell';
|
||||
|
||||
const dayNumber = document.createElement('div');
|
||||
dayNumber.className = 'calendar-day-number';
|
||||
dayNumber.textContent = day.date;
|
||||
|
||||
dayCell.appendChild(dayNumber);
|
||||
grid.appendChild(dayCell);
|
||||
});
|
||||
|
||||
container.appendChild(grid);
|
||||
|
||||
// Test results
|
||||
const headerCells = header.children.length;
|
||||
const dayCells = grid.children.length;
|
||||
|
||||
let testResult = '';
|
||||
let testPassed = false;
|
||||
|
||||
if (hideWeekends) {
|
||||
testPassed = headerCells === 5 && dayCells <= 10; // Should have 5 headers and only work days
|
||||
testResult = `Expected: 5 headers, work days only. Got: ${headerCells} headers, ${dayCells} day cells. ${testPassed ? 'PASS' : 'FAIL'}`;
|
||||
} else {
|
||||
testPassed = headerCells === 7 && dayCells === 14; // Should have 7 headers and all days
|
||||
testResult = `Expected: 7 headers, 14 day cells. Got: ${headerCells} headers, ${dayCells} day cells. ${testPassed ? 'PASS' : 'FAIL'}`;
|
||||
}
|
||||
|
||||
resultDiv.innerHTML = `<div class="test-result ${testPassed ? 'test-pass' : 'test-fail'}">${testResult}</div>`;
|
||||
}
|
||||
|
||||
// Auto-run tests on page load
|
||||
window.onload = function() {
|
||||
renderCalendar('test1', false);
|
||||
renderCalendar('test2', true);
|
||||
};
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
30
run-weekend-tests.js
Normal file
30
run-weekend-tests.js
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
#!/usr/bin/env node
|
||||
|
||||
/**
|
||||
* Script to run weekend hiding tests specifically
|
||||
*/
|
||||
|
||||
const { execSync } = require('child_process');
|
||||
const path = require('path');
|
||||
|
||||
console.log('🧪 Running Weekend Hiding Tests...\n');
|
||||
|
||||
try {
|
||||
// Run the specific test files for weekend hiding
|
||||
const testCommand = 'npx jest src/components/calendar/views/__tests__/month-view.test.ts src/components/calendar/views/__tests__/year-view.test.ts --verbose';
|
||||
|
||||
console.log('Running command:', testCommand);
|
||||
console.log('─'.repeat(50));
|
||||
|
||||
const result = execSync(testCommand, {
|
||||
stdio: 'inherit',
|
||||
cwd: process.cwd()
|
||||
});
|
||||
|
||||
console.log('\n✅ All weekend hiding tests passed!');
|
||||
|
||||
} catch (error) {
|
||||
console.error('\n❌ Some tests failed:');
|
||||
console.error(error.message);
|
||||
process.exit(1);
|
||||
}
|
||||
61
src/__mocks__/dom-helpers.ts
Normal file
61
src/__mocks__/dom-helpers.ts
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
/**
|
||||
* DOM helpers for testing Obsidian components
|
||||
*/
|
||||
|
||||
// Extend HTMLElement to include Obsidian-specific methods
|
||||
declare global {
|
||||
interface HTMLElement {
|
||||
empty(): void;
|
||||
addClass(className: string): void;
|
||||
removeClass(className: string): void;
|
||||
createDiv(className?: string): HTMLElement;
|
||||
createEl(tagName: string, options?: { cls?: string; attr?: Record<string, string>; text?: string }): HTMLElement;
|
||||
}
|
||||
}
|
||||
|
||||
// Add Obsidian-specific methods to HTMLElement prototype
|
||||
if (typeof HTMLElement !== 'undefined') {
|
||||
HTMLElement.prototype.empty = function() {
|
||||
this.innerHTML = '';
|
||||
};
|
||||
|
||||
HTMLElement.prototype.addClass = function(className: string) {
|
||||
this.classList.add(className);
|
||||
};
|
||||
|
||||
HTMLElement.prototype.removeClass = function(className: string) {
|
||||
this.classList.remove(className);
|
||||
};
|
||||
|
||||
HTMLElement.prototype.createDiv = function(className?: string) {
|
||||
const div = document.createElement('div');
|
||||
if (className) {
|
||||
div.className = className;
|
||||
}
|
||||
this.appendChild(div);
|
||||
return div;
|
||||
};
|
||||
|
||||
HTMLElement.prototype.createEl = function(tagName: string, options?: { cls?: string; attr?: Record<string, string>; text?: string }) {
|
||||
const el = document.createElement(tagName);
|
||||
|
||||
if (options?.cls) {
|
||||
el.className = options.cls;
|
||||
}
|
||||
|
||||
if (options?.attr) {
|
||||
Object.entries(options.attr).forEach(([key, value]) => {
|
||||
el.setAttribute(key, value);
|
||||
});
|
||||
}
|
||||
|
||||
if (options?.text) {
|
||||
el.textContent = options.text;
|
||||
}
|
||||
|
||||
this.appendChild(el);
|
||||
return el;
|
||||
};
|
||||
}
|
||||
|
||||
export {};
|
||||
|
|
@ -305,6 +305,10 @@ function momentFn(input?: any) {
|
|||
return ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
|
||||
};
|
||||
|
||||
(momentFn as any).weekdaysMin = function (localeData?: boolean) {
|
||||
return ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"];
|
||||
};
|
||||
|
||||
export const moment = momentFn as any;
|
||||
|
||||
// Mock Component class
|
||||
|
|
|
|||
|
|
@ -113,6 +113,7 @@ export interface KanbanColumnConfig {
|
|||
export interface CalendarSpecificConfig {
|
||||
viewType: "calendar"; // Discriminator
|
||||
firstDayOfWeek?: number; // 0=Sun, 1=Mon, ..., 6=Sat; undefined=locale default
|
||||
hideWeekends?: boolean; // Whether to hide weekend columns/cells in calendar views
|
||||
}
|
||||
|
||||
export interface GanttSpecificConfig {
|
||||
|
|
@ -124,6 +125,7 @@ export interface GanttSpecificConfig {
|
|||
export interface ForecastSpecificConfig {
|
||||
viewType: "forecast"; // Discriminator
|
||||
firstDayOfWeek?: number; // 0=Sun, 1=Mon, ..., 6=Sat; undefined=locale default
|
||||
hideWeekends?: boolean; // Whether to hide weekend columns/cells in forecast calendar
|
||||
}
|
||||
|
||||
export interface TwoColumnSpecificConfig {
|
||||
|
|
@ -736,6 +738,11 @@ export const DEFAULT_SETTINGS: TaskProgressBarSettings = {
|
|||
hideCompletedAndAbandonedTasks: true,
|
||||
filterRules: {},
|
||||
filterBlanks: false,
|
||||
specificConfig: {
|
||||
viewType: "forecast",
|
||||
firstDayOfWeek: undefined, // Use locale default initially
|
||||
hideWeekends: false, // Show weekends by default
|
||||
} as ForecastSpecificConfig,
|
||||
},
|
||||
{
|
||||
id: "projects",
|
||||
|
|
@ -789,6 +796,7 @@ export const DEFAULT_SETTINGS: TaskProgressBarSettings = {
|
|||
specificConfig: {
|
||||
viewType: "calendar",
|
||||
firstDayOfWeek: undefined, // Use locale default initially
|
||||
hideWeekends: false, // Show weekends by default
|
||||
} as CalendarSpecificConfig,
|
||||
},
|
||||
{
|
||||
|
|
|
|||
|
|
@ -323,6 +323,30 @@ export class ViewConfigModal extends Modal {
|
|||
this.checkForChanges();
|
||||
});
|
||||
});
|
||||
|
||||
// Add weekend hiding toggle for calendar view
|
||||
new Setting(contentEl)
|
||||
.setName(t("Hide weekends"))
|
||||
.setDesc(t("Hide weekend columns (Saturday and Sunday) in calendar views."))
|
||||
.addToggle((toggle) => {
|
||||
const currentValue = (this.viewConfig.specificConfig as CalendarSpecificConfig)?.hideWeekends ?? false;
|
||||
toggle.setValue(currentValue);
|
||||
toggle.onChange((value) => {
|
||||
if (
|
||||
!this.viewConfig.specificConfig ||
|
||||
this.viewConfig.specificConfig.viewType !== "calendar"
|
||||
) {
|
||||
this.viewConfig.specificConfig = {
|
||||
viewType: "calendar",
|
||||
firstDayOfWeek: undefined,
|
||||
hideWeekends: value,
|
||||
};
|
||||
} else {
|
||||
(this.viewConfig.specificConfig as CalendarSpecificConfig).hideWeekends = value;
|
||||
}
|
||||
this.checkForChanges();
|
||||
});
|
||||
});
|
||||
} else if (isKanbanView) {
|
||||
new Setting(contentEl)
|
||||
.setName(t("Group by"))
|
||||
|
|
@ -761,6 +785,30 @@ export class ViewConfigModal extends Modal {
|
|||
this.checkForChanges();
|
||||
});
|
||||
});
|
||||
|
||||
// Add weekend hiding toggle for forecast view
|
||||
new Setting(contentEl)
|
||||
.setName(t("Hide weekends"))
|
||||
.setDesc(t("Hide weekend columns (Saturday and Sunday) in forecast calendar."))
|
||||
.addToggle((toggle) => {
|
||||
const currentValue = (this.viewConfig.specificConfig as ForecastSpecificConfig)?.hideWeekends ?? false;
|
||||
toggle.setValue(currentValue);
|
||||
toggle.onChange((value) => {
|
||||
if (
|
||||
!this.viewConfig.specificConfig ||
|
||||
this.viewConfig.specificConfig.viewType !== "forecast"
|
||||
) {
|
||||
this.viewConfig.specificConfig = {
|
||||
viewType: "forecast",
|
||||
firstDayOfWeek: undefined,
|
||||
hideWeekends: value,
|
||||
};
|
||||
} else {
|
||||
(this.viewConfig.specificConfig as ForecastSpecificConfig).hideWeekends = value;
|
||||
}
|
||||
this.checkForChanges();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Two Column View specific config
|
||||
|
|
|
|||
236
src/components/calendar/views/__tests__/month-view.test.ts
Normal file
236
src/components/calendar/views/__tests__/month-view.test.ts
Normal file
|
|
@ -0,0 +1,236 @@
|
|||
import { App, moment } from "obsidian";
|
||||
import { MonthView } from "../month-view";
|
||||
import { CalendarEvent } from "../../index";
|
||||
import TaskProgressBarPlugin from "../../../../index";
|
||||
import "../../../../__mocks__/dom-helpers";
|
||||
|
||||
// Mock dependencies
|
||||
jest.mock("obsidian", () => ({
|
||||
App: jest.fn(),
|
||||
Component: class MockComponent {
|
||||
addChild = jest.fn();
|
||||
registerDomEvent = jest.fn();
|
||||
onunload = jest.fn();
|
||||
},
|
||||
moment: require("moment"),
|
||||
debounce: (fn: Function) => fn,
|
||||
}));
|
||||
|
||||
jest.mock("../../../../index", () => ({
|
||||
default: jest.fn(),
|
||||
}));
|
||||
|
||||
jest.mock("../../rendering/event-renderer", () => ({
|
||||
renderCalendarEvent: jest.fn(() => ({
|
||||
eventEl: document.createElement("div"),
|
||||
component: { onunload: jest.fn() },
|
||||
})),
|
||||
}));
|
||||
|
||||
jest.mock("../../../../common/setting-definition", () => ({
|
||||
getViewSettingOrDefault: jest.fn(),
|
||||
CalendarSpecificConfig: {},
|
||||
}));
|
||||
|
||||
jest.mock("sortablejs", () => ({
|
||||
default: {
|
||||
create: jest.fn(() => ({
|
||||
destroy: jest.fn(),
|
||||
})),
|
||||
},
|
||||
}));
|
||||
|
||||
describe("MonthView Weekend Hiding", () => {
|
||||
let mockApp: App;
|
||||
let mockPlugin: TaskProgressBarPlugin;
|
||||
let containerEl: HTMLElement;
|
||||
let monthView: MonthView;
|
||||
|
||||
beforeEach(() => {
|
||||
// Setup DOM environment
|
||||
document.body.innerHTML = "";
|
||||
containerEl = document.createElement("div");
|
||||
document.body.appendChild(containerEl);
|
||||
|
||||
// Mock app and plugin
|
||||
mockApp = {} as App;
|
||||
mockPlugin = {
|
||||
settings: {
|
||||
viewConfiguration: [
|
||||
{
|
||||
id: "calendar",
|
||||
specificConfig: {
|
||||
firstDayOfWeek: 1, // Monday
|
||||
hideWeekends: true,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
} as any;
|
||||
|
||||
// Create test events
|
||||
const testEvents: CalendarEvent[] = [
|
||||
{
|
||||
id: "test-1",
|
||||
title: "Test Event 1",
|
||||
start: moment("2024-01-15").toDate(), // Monday
|
||||
metadata: {
|
||||
dueDate: moment("2024-01-15").valueOf(),
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "test-2",
|
||||
title: "Test Event 2",
|
||||
start: moment("2024-01-20").toDate(), // Saturday (weekend)
|
||||
metadata: {
|
||||
dueDate: moment("2024-01-20").valueOf(),
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
// Create MonthView instance
|
||||
monthView = new MonthView(
|
||||
mockApp,
|
||||
mockPlugin,
|
||||
containerEl,
|
||||
"calendar",
|
||||
moment("2024-01-15"), // January 2024
|
||||
testEvents,
|
||||
{
|
||||
onDayClick: jest.fn(),
|
||||
onEventClick: jest.fn(),
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
monthView.onunload();
|
||||
document.body.innerHTML = "";
|
||||
});
|
||||
|
||||
test("should render only 5 weekday headers when hideWeekends is true", () => {
|
||||
// Render the month view
|
||||
monthView.render();
|
||||
|
||||
// Check weekday headers
|
||||
const weekdayHeaders = containerEl.querySelectorAll(".calendar-weekday");
|
||||
expect(weekdayHeaders.length).toBe(5);
|
||||
|
||||
// Verify the headers are work days only (Mon-Fri)
|
||||
const headerTexts = Array.from(weekdayHeaders).map(
|
||||
(el) => el.textContent
|
||||
);
|
||||
expect(headerTexts).not.toContain("Sat");
|
||||
expect(headerTexts).not.toContain("Sun");
|
||||
expect(headerTexts).toContain("Mon");
|
||||
expect(headerTexts).toContain("Tue");
|
||||
expect(headerTexts).toContain("Wed");
|
||||
expect(headerTexts).toContain("Thu");
|
||||
expect(headerTexts).toContain("Fri");
|
||||
});
|
||||
|
||||
test("should have hide-weekends CSS class when hideWeekends is true", () => {
|
||||
monthView.render();
|
||||
|
||||
expect(containerEl.classList.contains("hide-weekends")).toBe(true);
|
||||
});
|
||||
|
||||
test("should not create day cells for weekend dates", () => {
|
||||
monthView.render();
|
||||
|
||||
// Get all day cells
|
||||
const dayCells = containerEl.querySelectorAll(".calendar-day-cell");
|
||||
|
||||
// Check that no weekend dates are created
|
||||
Array.from(dayCells).forEach((cell) => {
|
||||
const dateStr = cell.getAttribute("data-date");
|
||||
if (dateStr) {
|
||||
const date = moment(dateStr);
|
||||
const dayOfWeek = date.day();
|
||||
// Should not be Saturday (6) or Sunday (0)
|
||||
expect(dayOfWeek).not.toBe(0);
|
||||
expect(dayOfWeek).not.toBe(6);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
test("should render only work days in a typical week", () => {
|
||||
monthView.render();
|
||||
|
||||
// Get all day cells for a specific week
|
||||
const dayCells = containerEl.querySelectorAll(".calendar-day-cell");
|
||||
const datesInView = Array.from(dayCells).map((cell) =>
|
||||
cell.getAttribute("data-date")
|
||||
);
|
||||
|
||||
// Find a complete work week (e.g., Jan 15-19, 2024 is Mon-Fri)
|
||||
const workWeekDates = [
|
||||
"2024-01-15", // Monday
|
||||
"2024-01-16", // Tuesday
|
||||
"2024-01-17", // Wednesday
|
||||
"2024-01-18", // Thursday
|
||||
"2024-01-19", // Friday
|
||||
];
|
||||
|
||||
// All work days should be present
|
||||
workWeekDates.forEach((date) => {
|
||||
expect(datesInView).toContain(date);
|
||||
});
|
||||
|
||||
// Weekend days should not be present
|
||||
const weekendDates = [
|
||||
"2024-01-13", // Saturday
|
||||
"2024-01-14", // Sunday
|
||||
"2024-01-20", // Saturday
|
||||
"2024-01-21", // Sunday
|
||||
];
|
||||
|
||||
weekendDates.forEach((date) => {
|
||||
expect(datesInView).not.toContain(date);
|
||||
});
|
||||
});
|
||||
|
||||
test("should have correct grid layout with 5 columns when weekends hidden", () => {
|
||||
monthView.render();
|
||||
|
||||
// Check that the container has the hide-weekends class
|
||||
expect(containerEl.classList.contains("hide-weekends")).toBe(true);
|
||||
|
||||
// The CSS should set grid-template-columns to repeat(5, 1fr)
|
||||
// We can't directly test CSS, but we can verify the class is applied
|
||||
const monthGrid = containerEl.querySelector(".calendar-month-grid");
|
||||
expect(monthGrid).toBeTruthy();
|
||||
|
||||
const weekdayHeader = containerEl.querySelector(
|
||||
".calendar-weekday-header"
|
||||
);
|
||||
expect(weekdayHeader).toBeTruthy();
|
||||
});
|
||||
|
||||
test("should render 7 days when hideWeekends is false", () => {
|
||||
// Update plugin settings to show weekends
|
||||
mockPlugin.settings.viewConfiguration[0].specificConfig.hideWeekends = false;
|
||||
|
||||
// Create new instance with weekends enabled
|
||||
const monthViewWithWeekends = new MonthView(
|
||||
mockApp,
|
||||
mockPlugin,
|
||||
containerEl,
|
||||
"calendar",
|
||||
moment("2024-01-15"),
|
||||
[],
|
||||
{}
|
||||
);
|
||||
|
||||
monthViewWithWeekends.render();
|
||||
|
||||
// Should have 7 weekday headers
|
||||
const weekdayHeaders = containerEl.querySelectorAll(".calendar-weekday");
|
||||
expect(weekdayHeaders.length).toBe(7);
|
||||
|
||||
// Should not have hide-weekends class
|
||||
expect(containerEl.classList.contains("hide-weekends")).toBe(false);
|
||||
|
||||
monthViewWithWeekends.onunload();
|
||||
});
|
||||
});
|
||||
256
src/components/calendar/views/__tests__/year-view.test.ts
Normal file
256
src/components/calendar/views/__tests__/year-view.test.ts
Normal file
|
|
@ -0,0 +1,256 @@
|
|||
import { App, moment } from "obsidian";
|
||||
import { YearView } from "../year-view";
|
||||
import { CalendarEvent } from "../../index";
|
||||
import TaskProgressBarPlugin from "../../../../index";
|
||||
import "../../../../__mocks__/dom-helpers";
|
||||
|
||||
// Mock dependencies
|
||||
jest.mock("obsidian", () => ({
|
||||
App: jest.fn(),
|
||||
Component: class MockComponent {
|
||||
addChild = jest.fn();
|
||||
registerDomEvent = jest.fn();
|
||||
onunload = jest.fn();
|
||||
},
|
||||
moment: require("moment"),
|
||||
debounce: (fn: Function) => fn,
|
||||
}));
|
||||
|
||||
jest.mock("../../../../index", () => ({
|
||||
default: jest.fn(),
|
||||
}));
|
||||
|
||||
jest.mock("../../../../common/setting-definition", () => ({
|
||||
getViewSettingOrDefault: jest.fn(() => ({
|
||||
specificConfig: {
|
||||
firstDayOfWeek: 1, // Monday
|
||||
hideWeekends: true,
|
||||
},
|
||||
})),
|
||||
CalendarSpecificConfig: {},
|
||||
}));
|
||||
|
||||
describe("YearView Weekend Hiding", () => {
|
||||
let mockApp: App;
|
||||
let mockPlugin: TaskProgressBarPlugin;
|
||||
let containerEl: HTMLElement;
|
||||
let yearView: YearView;
|
||||
|
||||
beforeEach(() => {
|
||||
// Setup DOM environment
|
||||
document.body.innerHTML = "";
|
||||
containerEl = document.createElement("div");
|
||||
document.body.appendChild(containerEl);
|
||||
|
||||
// Mock app and plugin
|
||||
mockApp = {} as App;
|
||||
mockPlugin = {
|
||||
settings: {
|
||||
viewConfiguration: [
|
||||
{
|
||||
id: "calendar",
|
||||
specificConfig: {
|
||||
firstDayOfWeek: 1, // Monday
|
||||
hideWeekends: true,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
} as any;
|
||||
|
||||
// Create test events
|
||||
const testEvents: CalendarEvent[] = [
|
||||
{
|
||||
id: "test-1",
|
||||
title: "Test Event 1",
|
||||
start: moment("2024-01-15").toDate(), // Monday
|
||||
metadata: {
|
||||
dueDate: moment("2024-01-15").valueOf(),
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "test-2",
|
||||
title: "Test Event 2",
|
||||
start: moment("2024-06-15").toDate(), // Saturday (weekend)
|
||||
metadata: {
|
||||
dueDate: moment("2024-06-15").valueOf(),
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
// Create YearView instance
|
||||
yearView = new YearView(
|
||||
mockApp,
|
||||
mockPlugin,
|
||||
containerEl,
|
||||
moment("2024-01-01"), // Year 2024
|
||||
testEvents,
|
||||
{
|
||||
onDayClick: jest.fn(),
|
||||
onMonthClick: jest.fn(),
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
yearView.onunload();
|
||||
document.body.innerHTML = "";
|
||||
});
|
||||
|
||||
test("should render only 5 weekday headers in mini-months when hideWeekends is true", () => {
|
||||
// Render the year view
|
||||
yearView.render();
|
||||
|
||||
// Check mini-month weekday headers
|
||||
const miniWeekdayHeaders = containerEl.querySelectorAll(".mini-weekday");
|
||||
|
||||
// Each mini-month should have 5 weekday headers (one for each month)
|
||||
// Since we have 12 months, we should have 12 * 5 = 60 mini-weekday headers
|
||||
expect(miniWeekdayHeaders.length).toBe(60);
|
||||
|
||||
// Verify no weekend day names are present
|
||||
const headerTexts = Array.from(miniWeekdayHeaders).map(
|
||||
(el) => el.textContent
|
||||
);
|
||||
|
||||
// Should not contain Saturday or Sunday abbreviations
|
||||
expect(headerTexts.filter(text => text === "Sa" || text === "Su")).toHaveLength(0);
|
||||
|
||||
// Should contain work day abbreviations
|
||||
expect(headerTexts.filter(text => text === "Mo")).toHaveLength(12); // 12 months
|
||||
expect(headerTexts.filter(text => text === "Tu")).toHaveLength(12);
|
||||
expect(headerTexts.filter(text => text === "We")).toHaveLength(12);
|
||||
expect(headerTexts.filter(text => text === "Th")).toHaveLength(12);
|
||||
expect(headerTexts.filter(text => text === "Fr")).toHaveLength(12);
|
||||
});
|
||||
|
||||
test("should have hide-weekends CSS class when hideWeekends is true", () => {
|
||||
yearView.render();
|
||||
|
||||
expect(containerEl.classList.contains("hide-weekends")).toBe(true);
|
||||
});
|
||||
|
||||
test("should not create day cells for weekend dates in mini-months", () => {
|
||||
yearView.render();
|
||||
|
||||
// Get all mini-day cells from all mini-months
|
||||
const miniDayCells = containerEl.querySelectorAll(".mini-day-cell");
|
||||
|
||||
// Check that no weekend dates are created
|
||||
Array.from(miniDayCells).forEach((cell) => {
|
||||
const dateStr = cell.getAttribute("data-date");
|
||||
if (dateStr) {
|
||||
const date = moment(dateStr);
|
||||
const dayOfWeek = date.day();
|
||||
// Should not be Saturday (6) or Sunday (0)
|
||||
expect(dayOfWeek).not.toBe(0);
|
||||
expect(dayOfWeek).not.toBe(6);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
test("should render 12 mini-months", () => {
|
||||
yearView.render();
|
||||
|
||||
// Should have 12 mini-month containers
|
||||
const miniMonths = containerEl.querySelectorAll(".calendar-mini-month");
|
||||
expect(miniMonths.length).toBe(12);
|
||||
|
||||
// Each should have a header with month name
|
||||
const monthHeaders = containerEl.querySelectorAll(".mini-month-header");
|
||||
expect(monthHeaders.length).toBe(12);
|
||||
|
||||
// Verify month names are present
|
||||
const monthNames = Array.from(monthHeaders).map(
|
||||
(el) => el.textContent
|
||||
);
|
||||
expect(monthNames).toContain("January");
|
||||
expect(monthNames).toContain("December");
|
||||
});
|
||||
|
||||
test("should render only work days in January 2024 mini-month", () => {
|
||||
yearView.render();
|
||||
|
||||
// Get the first mini-month (January)
|
||||
const miniMonths = containerEl.querySelectorAll(".calendar-mini-month");
|
||||
const januaryMonth = miniMonths[0];
|
||||
|
||||
// Get day cells from January mini-month
|
||||
const januaryDayCells = januaryMonth.querySelectorAll(".mini-day-cell");
|
||||
const datesInJanuary = Array.from(januaryDayCells).map((cell) =>
|
||||
cell.getAttribute("data-date")
|
||||
);
|
||||
|
||||
// Check that work days are present and weekend days are not
|
||||
// January 1, 2024 is a Monday
|
||||
const workDays = [
|
||||
"2024-01-01", // Monday
|
||||
"2024-01-02", // Tuesday
|
||||
"2024-01-03", // Wednesday
|
||||
"2024-01-04", // Thursday
|
||||
"2024-01-05", // Friday
|
||||
];
|
||||
|
||||
const weekendDays = [
|
||||
"2024-01-06", // Saturday
|
||||
"2024-01-07", // Sunday
|
||||
];
|
||||
|
||||
// Work days should be present
|
||||
workDays.forEach((date) => {
|
||||
expect(datesInJanuary).toContain(date);
|
||||
});
|
||||
|
||||
// Weekend days should not be present
|
||||
weekendDays.forEach((date) => {
|
||||
expect(datesInJanuary).not.toContain(date);
|
||||
});
|
||||
});
|
||||
|
||||
test("should have correct grid layout with 5 columns for mini-months when weekends hidden", () => {
|
||||
yearView.render();
|
||||
|
||||
// Check that the container has the hide-weekends class
|
||||
expect(containerEl.classList.contains("hide-weekends")).toBe(true);
|
||||
|
||||
// All mini-month grids should be present
|
||||
const miniMonthGrids = containerEl.querySelectorAll(".mini-month-grid");
|
||||
expect(miniMonthGrids.length).toBe(12);
|
||||
|
||||
// Each mini-month should have weekday headers
|
||||
const miniWeekdayHeaders = containerEl.querySelectorAll(".mini-weekday-header");
|
||||
expect(miniWeekdayHeaders.length).toBe(12);
|
||||
});
|
||||
|
||||
test("should render 7 days when hideWeekends is false", () => {
|
||||
// Mock getViewSettingOrDefault to return hideWeekends: false
|
||||
const { getViewSettingOrDefault } = require("../../../../common/setting-definition");
|
||||
getViewSettingOrDefault.mockReturnValue({
|
||||
specificConfig: {
|
||||
firstDayOfWeek: 1,
|
||||
hideWeekends: false,
|
||||
},
|
||||
});
|
||||
|
||||
// Create new instance with weekends enabled
|
||||
const yearViewWithWeekends = new YearView(
|
||||
mockApp,
|
||||
mockPlugin,
|
||||
containerEl,
|
||||
moment("2024-01-01"),
|
||||
[],
|
||||
{}
|
||||
);
|
||||
|
||||
yearViewWithWeekends.render();
|
||||
|
||||
// Should have 7 * 12 = 84 weekday headers (7 per month, 12 months)
|
||||
const miniWeekdayHeaders = containerEl.querySelectorAll(".mini-weekday");
|
||||
expect(miniWeekdayHeaders.length).toBe(84);
|
||||
|
||||
// Should not have hide-weekends class
|
||||
expect(containerEl.classList.contains("hide-weekends")).toBe(false);
|
||||
|
||||
yearViewWithWeekends.onunload();
|
||||
});
|
||||
});
|
||||
|
|
@ -46,11 +46,12 @@ export class MonthView extends CalendarViewComponent {
|
|||
}
|
||||
|
||||
render(): void {
|
||||
// Get view settings, including the first day of the week override
|
||||
// Get view settings, including the first day of the week override and weekend hiding
|
||||
const viewConfig = this.plugin.settings.viewConfiguration.find(
|
||||
(v) => v.id === this.currentViewId
|
||||
)?.specificConfig as CalendarSpecificConfig; // Assuming 'calendar' view for settings lookup, adjust if needed
|
||||
const firstDayOfWeekSetting = viewConfig?.firstDayOfWeek;
|
||||
const hideWeekends = viewConfig?.hideWeekends ?? false;
|
||||
// Default to Sunday (0) if the setting is undefined, following 0=Sun, 1=Mon, ..., 6=Sat
|
||||
const effectiveFirstDay =
|
||||
firstDayOfWeekSetting === undefined ? 0 : firstDayOfWeekSetting;
|
||||
|
|
@ -63,17 +64,50 @@ export class MonthView extends CalendarViewComponent {
|
|||
// Calculate grid end based on the week containing the end of the month, adjusted for the effective first day
|
||||
let gridEnd = endOfMonth.clone().weekday(effectiveFirstDay + 6); // moment handles wrapping correctly
|
||||
|
||||
// Ensure grid covers at least 6 weeks (42 days) for consistent layout
|
||||
// This logic should still work fine with custom start/end days
|
||||
if (gridEnd.diff(gridStart, "days") + 1 < 42) {
|
||||
// Add full weeks until at least 42 days are covered
|
||||
const daysToAdd = 42 - (gridEnd.diff(gridStart, "days") + 1);
|
||||
gridEnd.add(daysToAdd, "days");
|
||||
// Adjust grid coverage based on whether weekends are hidden
|
||||
if (hideWeekends) {
|
||||
// When weekends are hidden, we need fewer days to fill the grid
|
||||
// Calculate how many work days we need (approximately 6 weeks * 5 work days = 30 days)
|
||||
let workDaysCount = 0;
|
||||
let tempIter = gridStart.clone();
|
||||
|
||||
// Count existing work days in the current range
|
||||
while (tempIter.isSameOrBefore(gridEnd, "day")) {
|
||||
const isWeekend = tempIter.day() === 0 || tempIter.day() === 6;
|
||||
if (!isWeekend) {
|
||||
workDaysCount++;
|
||||
}
|
||||
tempIter.add(1, "day");
|
||||
}
|
||||
|
||||
// Ensure we have at least 30 work days (6 weeks * 5 days) for consistent layout
|
||||
while (workDaysCount < 30) {
|
||||
gridEnd.add(1, "day");
|
||||
const isWeekend = gridEnd.day() === 0 || gridEnd.day() === 6;
|
||||
if (!isWeekend) {
|
||||
workDaysCount++;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Original logic for when weekends are shown
|
||||
// Ensure grid covers at least 6 weeks (42 days) for consistent layout
|
||||
if (gridEnd.diff(gridStart, "days") + 1 < 42) {
|
||||
// Add full weeks until at least 42 days are covered
|
||||
const daysToAdd = 42 - (gridEnd.diff(gridStart, "days") + 1);
|
||||
gridEnd.add(daysToAdd, "days");
|
||||
}
|
||||
}
|
||||
|
||||
this.containerEl.empty();
|
||||
this.containerEl.addClass("view-month"); // Add class for styling
|
||||
|
||||
// Add hide-weekends class if weekend hiding is enabled
|
||||
if (hideWeekends) {
|
||||
this.containerEl.addClass("hide-weekends");
|
||||
} else {
|
||||
this.containerEl.removeClass("hide-weekends");
|
||||
}
|
||||
|
||||
// 2. Add weekday headers, rotated according to effective first day
|
||||
const headerRow = this.containerEl.createDiv("calendar-weekday-header");
|
||||
const weekdays = moment.weekdaysShort(true); // Gets locale-aware short weekdays
|
||||
|
|
@ -81,7 +115,17 @@ export class MonthView extends CalendarViewComponent {
|
|||
...weekdays.slice(effectiveFirstDay),
|
||||
...weekdays.slice(0, effectiveFirstDay),
|
||||
];
|
||||
rotatedWeekdays.forEach((day) => {
|
||||
|
||||
// Filter out weekends if hideWeekends is enabled
|
||||
const filteredWeekdays = hideWeekends
|
||||
? rotatedWeekdays.filter((_, index) => {
|
||||
// Calculate the actual day of week for this header position
|
||||
const dayOfWeek = (effectiveFirstDay + index) % 7;
|
||||
return dayOfWeek !== 0 && dayOfWeek !== 6; // Exclude Sunday (0) and Saturday (6)
|
||||
})
|
||||
: rotatedWeekdays;
|
||||
|
||||
filteredWeekdays.forEach((day) => {
|
||||
const weekdayEl = headerRow.createDiv("calendar-weekday");
|
||||
weekdayEl.textContent = day;
|
||||
});
|
||||
|
|
@ -92,6 +136,14 @@ export class MonthView extends CalendarViewComponent {
|
|||
let currentDayIter = gridStart.clone();
|
||||
|
||||
while (currentDayIter.isSameOrBefore(gridEnd, "day")) {
|
||||
const isWeekend = currentDayIter.day() === 0 || currentDayIter.day() === 6; // Sunday or Saturday
|
||||
|
||||
// Skip weekend days if hideWeekends is enabled
|
||||
if (hideWeekends && isWeekend) {
|
||||
currentDayIter.add(1, "day");
|
||||
continue;
|
||||
}
|
||||
|
||||
const cell = gridContainer.createEl("div", {
|
||||
cls: "calendar-day-cell",
|
||||
attr: {
|
||||
|
|
@ -113,9 +165,9 @@ export class MonthView extends CalendarViewComponent {
|
|||
if (currentDayIter.isSame(moment(), "day")) {
|
||||
cell.addClass("is-today");
|
||||
}
|
||||
// Weekend check might need adjustment depending on visual definition of weekend with custom start day
|
||||
if (currentDayIter.day() === 0 || currentDayIter.day() === 6) {
|
||||
// Sunday or Saturday
|
||||
// Note: We don't add is-weekend class when hideWeekends is enabled
|
||||
// because weekend cells are not created at all
|
||||
if (!hideWeekends && isWeekend) {
|
||||
cell.addClass("is-weekend");
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ export class WeekView extends CalendarViewComponent {
|
|||
}
|
||||
|
||||
render(): void {
|
||||
// Get view settings, including the first day of the week override
|
||||
// Get view settings, including the first day of the week override and weekend hiding
|
||||
const viewConfig = getViewSettingOrDefault(
|
||||
this.plugin,
|
||||
this.currentViewId
|
||||
|
|
@ -47,6 +47,7 @@ export class WeekView extends CalendarViewComponent {
|
|||
const firstDayOfWeekSetting = (
|
||||
viewConfig.specificConfig as CalendarSpecificConfig
|
||||
).firstDayOfWeek;
|
||||
const hideWeekends = (viewConfig.specificConfig as CalendarSpecificConfig)?.hideWeekends ?? false;
|
||||
// Default to Sunday (0) if the setting is undefined, following 0=Sun, 1=Mon, ..., 6=Sat
|
||||
const effectiveFirstDay =
|
||||
firstDayOfWeekSetting === undefined ? 0 : firstDayOfWeekSetting;
|
||||
|
|
@ -58,6 +59,13 @@ export class WeekView extends CalendarViewComponent {
|
|||
this.containerEl.empty();
|
||||
this.containerEl.addClass("view-week");
|
||||
|
||||
// Add hide-weekends class if weekend hiding is enabled
|
||||
if (hideWeekends) {
|
||||
this.containerEl.addClass("hide-weekends");
|
||||
} else {
|
||||
this.containerEl.removeClass("hide-weekends");
|
||||
}
|
||||
|
||||
// 1. Render Header Row (Days of the week + Dates)
|
||||
const headerRow = this.containerEl.createDiv("calendar-week-header");
|
||||
const dayHeaderCells: { [key: string]: HTMLElement } = {};
|
||||
|
|
@ -69,14 +77,32 @@ export class WeekView extends CalendarViewComponent {
|
|||
...weekdays.slice(effectiveFirstDay),
|
||||
...weekdays.slice(0, effectiveFirstDay),
|
||||
];
|
||||
|
||||
// Filter out weekends if hideWeekends is enabled
|
||||
const filteredWeekdays = hideWeekends
|
||||
? rotatedWeekdays.filter((_, index) => {
|
||||
// Calculate the actual day of week for this header position
|
||||
const dayOfWeek = (effectiveFirstDay + index) % 7;
|
||||
return dayOfWeek !== 0 && dayOfWeek !== 6; // Exclude Sunday (0) and Saturday (6)
|
||||
})
|
||||
: rotatedWeekdays;
|
||||
|
||||
let dayIndex = 0;
|
||||
|
||||
while (currentDayIter.isSameOrBefore(endOfWeek, "day")) {
|
||||
const isWeekend = currentDayIter.day() === 0 || currentDayIter.day() === 6; // Sunday or Saturday
|
||||
|
||||
// Skip weekend days if hideWeekends is enabled
|
||||
if (hideWeekends && isWeekend) {
|
||||
currentDayIter.add(1, "day");
|
||||
continue; // Don't increment dayIndex for skipped days
|
||||
}
|
||||
|
||||
const dateStr = currentDayIter.format("YYYY-MM-DD");
|
||||
const headerCell = headerRow.createDiv("calendar-header-cell");
|
||||
dayHeaderCells[dateStr] = headerCell; // Store header cell if needed
|
||||
const weekdayEl = headerCell.createDiv("calendar-weekday");
|
||||
weekdayEl.textContent = rotatedWeekdays[dayIndex % 7]; // Use rotated weekday name
|
||||
weekdayEl.textContent = filteredWeekdays[dayIndex]; // Use filtered weekday names
|
||||
const dayNumEl = headerCell.createDiv("calendar-day-number");
|
||||
dayNumEl.textContent = currentDayIter.format("D"); // Date number
|
||||
|
||||
|
|
@ -96,6 +122,14 @@ export class WeekView extends CalendarViewComponent {
|
|||
currentDayIter = startOfWeek.clone();
|
||||
|
||||
while (currentDayIter.isSameOrBefore(endOfWeek, "day")) {
|
||||
const isWeekend = currentDayIter.day() === 0 || currentDayIter.day() === 6; // Sunday or Saturday
|
||||
|
||||
// Skip weekend days if hideWeekends is enabled
|
||||
if (hideWeekends && isWeekend) {
|
||||
currentDayIter.add(1, "day");
|
||||
continue;
|
||||
}
|
||||
|
||||
const dateStr = currentDayIter.format("YYYY-MM-DD");
|
||||
const dayCell = weekGrid.createEl("div", {
|
||||
cls: "calendar-day-column",
|
||||
|
|
@ -110,7 +144,7 @@ export class WeekView extends CalendarViewComponent {
|
|||
if (currentDayIter.isSame(moment(), "day")) {
|
||||
dayCell.addClass("is-today"); // Apply to the main day cell
|
||||
}
|
||||
if (currentDayIter.day() === 0 || currentDayIter.day() === 6) {
|
||||
if (isWeekend) {
|
||||
// This weekend check is based on Sun/Sat, might need adjustment if start day changes weekend definition visually
|
||||
dayCell.addClass("is-weekend"); // Apply to the main day cell
|
||||
}
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@ export class YearView extends CalendarViewComponent {
|
|||
const year = this.currentDate.year();
|
||||
this.containerEl.empty();
|
||||
this.containerEl.addClass("view-year");
|
||||
|
||||
console.log(
|
||||
`YearView: Rendering year ${year}. Total events received: ${this.events.length}`
|
||||
); // Log total events
|
||||
|
|
@ -68,6 +69,14 @@ export class YearView extends CalendarViewComponent {
|
|||
const firstDayOfWeekSetting = (
|
||||
viewConfig.specificConfig as CalendarSpecificConfig
|
||||
).firstDayOfWeek;
|
||||
const hideWeekends = (viewConfig.specificConfig as CalendarSpecificConfig)?.hideWeekends ?? false;
|
||||
|
||||
// Add hide-weekends class if weekend hiding is enabled
|
||||
if (hideWeekends) {
|
||||
this.containerEl.addClass("hide-weekends");
|
||||
} else {
|
||||
this.containerEl.removeClass("hide-weekends");
|
||||
}
|
||||
// Default to Sunday (0) if the setting is undefined, following 0=Sun, 1=Mon, ..., 6=Sat
|
||||
const effectiveFirstDay =
|
||||
firstDayOfWeekSetting === undefined ? 0 : firstDayOfWeekSetting;
|
||||
|
|
@ -111,7 +120,8 @@ export class YearView extends CalendarViewComponent {
|
|||
monthBody,
|
||||
monthMoment,
|
||||
daysWithEvents,
|
||||
effectiveFirstDay
|
||||
effectiveFirstDay,
|
||||
hideWeekends
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -169,7 +179,8 @@ export class YearView extends CalendarViewComponent {
|
|||
container: HTMLElement,
|
||||
monthMoment: moment.Moment,
|
||||
daysWithEvents: Set<number>,
|
||||
effectiveFirstDay: number // Pass the effective first day
|
||||
effectiveFirstDay: number, // Pass the effective first day
|
||||
hideWeekends: boolean // Pass the weekend hiding setting
|
||||
) {
|
||||
container.empty(); // Clear placeholder
|
||||
container.addClass("mini-month-grid");
|
||||
|
|
@ -181,25 +192,67 @@ export class YearView extends CalendarViewComponent {
|
|||
...weekdays.slice(effectiveFirstDay),
|
||||
...weekdays.slice(0, effectiveFirstDay),
|
||||
];
|
||||
rotatedWeekdays.forEach((day) => {
|
||||
|
||||
// Filter out weekends if hideWeekends is enabled
|
||||
const filteredWeekdays = hideWeekends
|
||||
? rotatedWeekdays.filter((_, index) => {
|
||||
// Calculate the actual day of week for this header position
|
||||
const dayOfWeek = (effectiveFirstDay + index) % 7;
|
||||
return dayOfWeek !== 0 && dayOfWeek !== 6; // Exclude Sunday (0) and Saturday (6)
|
||||
})
|
||||
: rotatedWeekdays;
|
||||
|
||||
filteredWeekdays.forEach((day) => {
|
||||
headerRow.createDiv("mini-weekday").textContent = day;
|
||||
});
|
||||
|
||||
// Calculate grid boundaries using effective first day
|
||||
const monthStart = monthMoment.clone().startOf("month");
|
||||
const daysToSubtractStart =
|
||||
(monthStart.weekday() - effectiveFirstDay + 7) % 7;
|
||||
const gridStart = monthStart
|
||||
.clone()
|
||||
.subtract(daysToSubtractStart, "days");
|
||||
|
||||
const monthEnd = monthMoment.clone().endOf("month");
|
||||
const daysToAddEnd =
|
||||
(effectiveFirstDay + 6 - monthEnd.weekday() + 7) % 7;
|
||||
const gridEnd = monthEnd.clone().add(daysToAddEnd, "days");
|
||||
|
||||
let gridStart: moment.Moment;
|
||||
let gridEnd: moment.Moment;
|
||||
|
||||
if (hideWeekends) {
|
||||
// When weekends are hidden, adjust grid to start and end on work days
|
||||
// Find the first work day of the week containing the start of month
|
||||
gridStart = monthStart.clone();
|
||||
const daysToSubtractStart = (monthStart.weekday() - effectiveFirstDay + 7) % 7;
|
||||
gridStart.subtract(daysToSubtractStart, "days");
|
||||
|
||||
// Ensure gridStart is not a weekend
|
||||
while (gridStart.day() === 0 || gridStart.day() === 6) {
|
||||
gridStart.add(1, "day");
|
||||
}
|
||||
|
||||
// Find the last work day of the week containing the end of month
|
||||
gridEnd = monthEnd.clone();
|
||||
const daysToAddEnd = (effectiveFirstDay + 4 - monthEnd.weekday() + 7) % 7; // 4 = Friday in work week
|
||||
gridEnd.add(daysToAddEnd, "days");
|
||||
|
||||
// Ensure gridEnd is not a weekend
|
||||
while (gridEnd.day() === 0 || gridEnd.day() === 6) {
|
||||
gridEnd.subtract(1, "day");
|
||||
}
|
||||
} else {
|
||||
// Original logic for when weekends are shown
|
||||
const daysToSubtractStart = (monthStart.weekday() - effectiveFirstDay + 7) % 7;
|
||||
gridStart = monthStart.clone().subtract(daysToSubtractStart, "days");
|
||||
|
||||
const daysToAddEnd = (effectiveFirstDay + 6 - monthEnd.weekday() + 7) % 7;
|
||||
gridEnd = monthEnd.clone().add(daysToAddEnd, "days");
|
||||
}
|
||||
|
||||
let currentDayIter = gridStart.clone();
|
||||
while (currentDayIter.isSameOrBefore(gridEnd, "day")) {
|
||||
const isWeekend = currentDayIter.day() === 0 || currentDayIter.day() === 6; // Sunday or Saturday
|
||||
|
||||
// Skip weekend days if hideWeekends is enabled
|
||||
if (hideWeekends && isWeekend) {
|
||||
currentDayIter.add(1, "day");
|
||||
continue;
|
||||
}
|
||||
|
||||
const cell = container.createEl("div", {
|
||||
cls: "mini-day-cell",
|
||||
attr: {
|
||||
|
|
|
|||
|
|
@ -60,6 +60,11 @@ export class CalendarComponent extends Component {
|
|||
cls: "mini-calendar-container",
|
||||
});
|
||||
|
||||
// Add hide-weekends class if weekend hiding is enabled
|
||||
if (!this.options.showWeekends) {
|
||||
this.containerEl.addClass("hide-weekends");
|
||||
}
|
||||
|
||||
// Create header with navigation
|
||||
this.createCalendarHeader();
|
||||
|
||||
|
|
@ -142,14 +147,19 @@ export class CalendarComponent extends Component {
|
|||
}
|
||||
}
|
||||
|
||||
// Filter out weekend headers if showWeekends is false
|
||||
const filteredDayNames = this.options.showWeekends
|
||||
? sortedDayNames
|
||||
: sortedDayNames.filter(day => day !== "Sat" && day !== "Sun");
|
||||
|
||||
// Add day header cells
|
||||
sortedDayNames.forEach((day) => {
|
||||
filteredDayNames.forEach((day) => {
|
||||
const dayHeaderEl = this.calendarGridEl.createDiv({
|
||||
cls: "calendar-day-header",
|
||||
text: day,
|
||||
});
|
||||
|
||||
// Highlight weekend headers
|
||||
// Highlight weekend headers (only if they're shown)
|
||||
if (
|
||||
(day === "Sat" || day === "Sun") &&
|
||||
!this.options.showWeekends
|
||||
|
|
@ -263,6 +273,12 @@ export class CalendarComponent extends Component {
|
|||
isFuture: boolean,
|
||||
isThisMonth: boolean = false
|
||||
) {
|
||||
// Skip weekend days if showWeekends is false
|
||||
const isWeekend = date.getDay() === 0 || date.getDay() === 6; // Sunday or Saturday
|
||||
if (!this.options.showWeekends && isWeekend) {
|
||||
return; // Skip creating this day
|
||||
}
|
||||
|
||||
// Filter tasks for this day
|
||||
const dayTasks = this.getTasksForDate(date);
|
||||
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import {
|
|||
setIcon,
|
||||
} from "obsidian";
|
||||
import { Task } from "../../types/task";
|
||||
import { CalendarComponent } from "./calendar";
|
||||
import { CalendarComponent, CalendarOptions } from "./calendar";
|
||||
import { TaskListItemComponent } from "./listItem";
|
||||
import { t } from "../../translations/helper";
|
||||
import "../../styles/forecast.css";
|
||||
|
|
@ -269,11 +269,20 @@ export class ForecastComponent extends Component {
|
|||
});
|
||||
|
||||
// Create and initialize calendar component
|
||||
const forecastConfig = this.plugin.settings.viewConfiguration.find(
|
||||
(view) => view.id === "forecast"
|
||||
)?.specificConfig as ForecastSpecificConfig;
|
||||
|
||||
// Convert ForecastSpecificConfig to CalendarOptions
|
||||
const calendarOptions: Partial<CalendarOptions> = {
|
||||
firstDayOfWeek: forecastConfig?.firstDayOfWeek ?? 0,
|
||||
showWeekends: !(forecastConfig?.hideWeekends ?? false), // Invert hideWeekends to showWeekends
|
||||
showTaskCounts: true,
|
||||
};
|
||||
|
||||
this.calendarComponent = new CalendarComponent(
|
||||
this.calendarContainerEl,
|
||||
this.plugin.settings.viewConfiguration.find(
|
||||
(view) => view.id === "forecast"
|
||||
)?.specificConfig as ForecastSpecificConfig
|
||||
calendarOptions
|
||||
);
|
||||
this.addChild(this.calendarComponent);
|
||||
this.calendarComponent.load();
|
||||
|
|
|
|||
|
|
@ -88,6 +88,14 @@
|
|||
color: var(--text-accent);
|
||||
}
|
||||
|
||||
/* Adjust grid layout when weekends are hidden in mini calendar */
|
||||
.task-genius-view .mini-calendar-container.hide-weekends .calendar-grid {
|
||||
grid-template-columns: repeat(5, 1fr); /* 5 columns instead of 7 when weekends hidden */
|
||||
}
|
||||
|
||||
/* Note: Weekend elements are not created when hideWeekends is enabled,
|
||||
so no hiding rules are needed. The grid layout adjustments above are sufficient. */
|
||||
|
||||
.task-genius-view .mini-calendar-container .calendar-day {
|
||||
aspect-ratio: 1;
|
||||
border-radius: 4px;
|
||||
|
|
|
|||
|
|
@ -409,6 +409,30 @@
|
|||
background-color: var(--background-secondary);
|
||||
}
|
||||
|
||||
/* Adjust grid layouts when weekends are hidden */
|
||||
.full-calendar-container .calendar-view-container.hide-weekends .calendar-weekday-header {
|
||||
grid-template-columns: repeat(5, 1fr) !important; /* 5 columns instead of 7 when weekends hidden */
|
||||
}
|
||||
|
||||
.full-calendar-container .calendar-view-container.hide-weekends .calendar-month-grid {
|
||||
grid-template-columns: repeat(5, 1fr) !important; /* 5 columns instead of 7 when weekends hidden */
|
||||
}
|
||||
|
||||
.full-calendar-container .calendar-view-container.hide-weekends .calendar-week-header {
|
||||
grid-template-columns: repeat(5, 1fr) !important; /* 5 columns instead of 7 when weekends hidden */
|
||||
}
|
||||
|
||||
.full-calendar-container .calendar-view-container.hide-weekends .calendar-week-grid {
|
||||
grid-template-columns: repeat(5, 1fr) !important; /* 5 columns instead of 7 when weekends hidden */
|
||||
}
|
||||
|
||||
.full-calendar-container .calendar-view-container.hide-weekends .mini-month-grid {
|
||||
grid-template-columns: repeat(5, 1fr) !important; /* 5 columns instead of 7 when weekends hidden */
|
||||
}
|
||||
|
||||
/* Note: Weekend elements are not created when hideWeekends is enabled,
|
||||
so no hiding rules are needed. The grid layout adjustments above are sufficient. */
|
||||
|
||||
.full-calendar-container .calendar-day-events-container {
|
||||
/* Renamed class */
|
||||
/* This container might not be strictly necessary if .calendar-day-column uses flex */
|
||||
|
|
|
|||
21
src/test-setup.ts
Normal file
21
src/test-setup.ts
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
/**
|
||||
* Test setup file for Jest
|
||||
*/
|
||||
|
||||
import './__mocks__/dom-helpers';
|
||||
|
||||
// jsdom is already set up by jest-environment-jsdom
|
||||
// Just need to ensure our DOM helpers are loaded
|
||||
|
||||
// Mock console methods to reduce noise in tests
|
||||
global.console = {
|
||||
...console,
|
||||
log: jest.fn(),
|
||||
warn: jest.fn(),
|
||||
error: jest.fn(),
|
||||
};
|
||||
|
||||
// Setup performance mock
|
||||
global.performance = {
|
||||
now: jest.fn(() => Date.now()),
|
||||
} as any;
|
||||
20
styles.css
20
styles.css
File diff suppressed because one or more lines are too long
Loading…
Reference in a new issue