WARNING: THIS SITE IS A MIRROR OF GITHUB.COM / IT CANNOT LOGIN OR REGISTER ACCOUNTS / THE CONTENTS ARE PROVIDED AS-IS / THIS SITE ASSUMES NO RESPONSIBILITY FOR ANY DISPLAYED CONTENT OR LINKS / IF YOU FOUND SOMETHING MAY NOT GOOD FOR EVERYONE, CONTACT ADMIN AT ilovescratch@foxmail.com
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 53 additions & 5 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
name: CI
name: CI and Chromatic

on:
push:
branches: [master, main]
branches: [main, master]
pull_request:
branches: [master, main]
branches: [main, master]

jobs:
build:
ci:
runs-on: ubuntu-latest
steps:
- name: Checkout
Expand All @@ -21,7 +21,7 @@ jobs:
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: 20
node-version-file: ".nvmrc"
cache: "pnpm"

- name: Install dependencies
Expand All @@ -35,3 +35,51 @@ jobs:

- name: Run tests
run: pnpm run test:ci

- name: Build project
run: pnpm run build

chromatic:
runs-on: ubuntu-latest
needs: ci
if: github.event_name == 'pull_request' || github.ref == 'refs/heads/main' || github.ref == 'refs/heads/master'
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
# Chromatic needs to know the git history to make sure it only runs changed components
fetch-depth: 0

- uses: pnpm/action-setup@v4
name: Install pnpm
with:
run_install: false

- name: Setup Node
uses: actions/setup-node@v4
with:
node-version-file: ".nvmrc"
cache: "pnpm"

- name: Install dependencies
run: pnpm install --frozen-lockfile

- name: Publish to Chromatic
uses: chromaui/action@v1
with:
# Chromatic project token. This value should be set in GitHub Secrets
projectToken: ${{ secrets.CHROMATIC_PROJECT_TOKEN }}
# Build the Storybook static files using the existing script
buildScriptName: build-storybook
# The working directory that contains the package.json with the build-storybook script
workingDir: ./
# Only run this action on changed components
onlyChanged: true
# Automatically accept all changes on the main branch
autoAcceptChanges: ${{ github.ref == 'refs/heads/main' || github.ref == 'refs/heads/master' }}
# Exit with 0 on visual changes
exitZeroOnChanges: true
# Custom output directory
outputDir: storybook-static
# Skip build step since we're using buildScriptName
skip: false
21 changes: 7 additions & 14 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,32 +25,25 @@ jobs:
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: 20
node-version-file: ".nvmrc"
cache: "pnpm"

- name: Install dependencies
run: pnpm install --frozen-lockfile

- name: Run tests
run: pnpm run test:ci

- name: Type check
run: pnpm run type-check

- name: Lint
run: pnpm run lint
- name: Check formatting and linting
run: pnpm run check

- name: Run tests
run: pnpm run test:ci

- name: Build package
run: pnpm run build

- name: Create GitHub Release
uses: softprops/action-gh-release@v2
with:
generate_release_notes: true
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

- name: Publish to npm
run: pnpm publish --access public --provenance
run: pnpm publish --access public --provenance --no-git-checks
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
142 changes: 139 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
</p>

<p>
A customizable events calendar component library. Checkout <a href="https://v84yk.csb.app/">Demo 1</a> and <a href="https://codesandbox.io/s/color-calendar-bnwdu">Demo 2.</a>
Add a colorful, interactive events calendar to your site in seconds. Try it: <a href="https://master--68dc1b2449e62022d61d079f.chromatic.com">Storybook</a> | <a href="https://codesandbox.io/s/color-calendar-bnwdu">CodeSandbox</a>
</p>

<!-- # Color Calendar
Expand Down Expand Up @@ -53,7 +53,8 @@
## 🚀 Features

- Zero dependencies
- Add events to calendar
- Add events to calendar with **individual event colors**
- **Configurable event bullet modes** - show multiple bullets or single bullet
- Perform some action on calendar date change
- Month and year picker built-in
- Themes available
Expand Down Expand Up @@ -158,6 +159,66 @@ new Calendar({

[Example](https://codesandbox.io/s/color-calendar-bnwdu)

#### Function-Based ID

```javascript
// Using a function that returns an HTMLElement
new Calendar({
id: () => document.getElementById("my-calendar"),
calendarSize: "large"
});

// Dynamic element creation
new Calendar({
id: () => {
let container = document.getElementById("calendar-container");
if (!container) {
container = document.createElement("div");
container.id = "calendar-container";
container.style.padding = "20px";
document.body.appendChild(container);
}
return container;
},
calendarSize: "large"
});
```

#### Event Colors and Bullet Modes

```javascript
new Calendar({
id: "#calendar",
eventBulletMode: "multiple", // or "single"
eventsData: [
{
start: '2024-09-15T00:00:00',
end: '2024-09-20T23:59:59',
name: 'Red Event',
color: '#ff0000'
},
{
start: '2024-09-15T00:00:00',
end: '2024-09-15T23:59:59',
name: 'Blue Event',
color: '#0000ff'
},
{
start: '2024-09-24T00:00:00',
end: '2024-10-05T23:59:59',
name: 'Cross-Month Event',
color: '#00ff00'
}
]
});
```

**Key Features:**
- **Individual Event Colors**: Each event can have its own color
- **Cross-Month Support**: Events spanning multiple months display correctly
- **White Bullets on Selection**: Selected dates show white bullets for better contrast
- **Configurable Bullet Modes**: Choose between multiple bullets or single bullet per day

<a id="usage-react"></a>

### React
Expand All @@ -176,11 +237,36 @@ new Calendar({

### `id`

Type: `String`
Type: `String | Function`
Default: `#color-calendar`

Selector referencing HTMLElement where the calendar instance will bind to.

**String**: CSS selector string (e.g., `"#my-calendar"`, `".calendar-container"`)

**Function**: Function that returns an HTMLElement or null
```javascript
// Function-based id example
const calendar = new ColorCalendar({
id: () => document.getElementById("my-calendar"),
// ... other options
});

// Dynamic element creation
const calendar = new ColorCalendar({
id: () => {
let container = document.getElementById("calendar-container");
if (!container) {
container = document.createElement("div");
container.id = "calendar-container";
document.body.appendChild(container);
}
return container;
},
// ... other options
});
```

### `calendarSize`

Type: `String`
Expand Down Expand Up @@ -366,6 +452,38 @@ Set custom display values for Weekdays.
["Lun", "Mar", "Mer", "Jeu", "Ven", "Sam", "Dim"];
```

### `eventBulletMode`

Type: `String`
Default: `multiple`
Options: `multiple` | `single`

Controls how event bullets are displayed when multiple events exist on the same day.

- `multiple`: Shows one bullet per event, positioned side by side (maximum 5 bullets to prevent overflow)
- `single`: Shows only one bullet per day, using the first event's color

```javascript
new Calendar({
id: "#calendar",
eventBulletMode: "single", // or "multiple"
eventsData: [
{
start: '2024-09-15T00:00:00',
end: '2024-09-15T23:59:59',
name: 'Red Event',
color: '#ff0000'
},
{
start: '2024-09-15T00:00:00',
end: '2024-09-15T23:59:59',
name: 'Blue Event',
color: '#0000ff'
}
]
});
```

<a id="events"></a>

## 🖱 Events
Expand Down Expand Up @@ -531,10 +649,17 @@ Set month display type.
{
start: string, // ISO 8601 date and time format
end: string, // ISO 8601 date and time format
color?: string, // Optional color for event bullet (hex, rgb, hsl)
[key: string]: any,
}
```

**Properties:**
- `start` (required): Event start date in ISO 8601 format
- `end` (required): Event end date in ISO 8601 format
- `color` (optional): Color for the event bullet. Accepts any valid CSS color value (hex, rgb, hsl, etc.). Falls back to `primaryColor` if not specified.
- Additional properties: Any other properties can be added and will be preserved

<a id="type-weekday-display-type"></a>

### `WeekdayDisplayType`
Expand Down Expand Up @@ -572,6 +697,17 @@ January February March ...

`"month-align-left"`

<a id="type-event-bullet-mode"></a>

### `EventBulletMode`

`"multiple"` | `"single"`

Controls how event bullets are displayed when multiple events exist on the same day.

- `"multiple"`: Shows one bullet per event, positioned side by side
- `"single"`: Shows only one bullet per day, using the first event's color

<a id="themes"></a>

## 🎨 Themes
Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "color-calendar",
"version": "1.0.7",
"description": "A customizable events calendar widget library",
"version": "2.0.0",
"description": "A zero-dependency, customizable events calendar widget with themes and event colors.",
"main": "dist/bundle.cjs.js",
"module": "dist/bundle.esm.js",
"browser": "dist/bundle.js",
Expand Down
62 changes: 62 additions & 0 deletions src/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -421,3 +421,65 @@ describe("custom calendar options when instantiated", () => {

test.todo("dateChanged should be executed");
});

describe("function-based id parameter", () => {
test("should work with function that returns HTMLElement", () => {
const mockElement = document.createElement("div");
mockElement.id = "test-calendar";
document.body.appendChild(mockElement);

const idFunction = () => mockElement;
const myCalendar = new Calendar({ id: idFunction });

expect(myCalendar.calendar).toBe(mockElement);
expect(myCalendar.calendar).not.toBeNull();
});

test("should work with function that returns null", () => {
const idFunction = () => null;

expect(() => {
new Calendar({ id: idFunction });
}).toThrow("[COLOR-CALENDAR] Element with selector function not found");
});

test("should work with function that returns different element each time", () => {
const element1 = document.createElement("div");
element1.id = "calendar-1";
const element2 = document.createElement("div");
element2.id = "calendar-2";
document.body.appendChild(element1);
document.body.appendChild(element2);

let callCount = 0;
const idFunction = () => {
callCount++;
return callCount === 1 ? element1 : element2;
};

const myCalendar = new Calendar({ id: idFunction });
expect(myCalendar.calendar).toBe(element1);
expect(callCount).toBe(1);
});

test("should maintain backward compatibility with string id", () => {
const mockElement = document.createElement("div");
mockElement.id = "string-calendar";
document.body.appendChild(mockElement);

const myCalendar = new Calendar({ id: "#string-calendar" });

expect(myCalendar.calendar).toBe(mockElement);
expect(myCalendar.calendar).not.toBeNull();
});

test("should handle function that throws error", () => {
const idFunction = () => {
throw new Error("Function error");
};

expect(() => {
new Calendar({ id: idFunction });
}).toThrow("Function error");
});
});
Loading