Skip to content

[CalendarLink] Add component#3460

Open
zairigimad wants to merge 7 commits intosymfony:3.xfrom
zairigimad:feat/component-calendar-link
Open

[CalendarLink] Add component#3460
zairigimad wants to merge 7 commits intosymfony:3.xfrom
zairigimad:feat/component-calendar-link

Conversation

@zairigimad
Copy link
Copy Markdown
Contributor

@zairigimad zairigimad commented Apr 12, 2026

Q A
Bug fix? no
New feature? yes
Deprecations? no
Documentation? no
Issues Fix #3308 ...
License MIT

This new component address the #3308 idea , it provide twig functions as proposed by (@Kocal) with an easy way to configure calendar links , it supports Google Calendar, Microsoft Outlook and Ics.

I did not include a dropdown in the component since we can use any other dropdown from any Kit or custom Dropdown.

Capture d’écran 2026-04-12 à 16 31 23

@carsonbot carsonbot added Deprecation Feature New Feature Status: Needs Review Needs to be reviewed labels Apr 12, 2026
@zairigimad zairigimad marked this pull request as draft April 12, 2026 15:58
@zairigimad zairigimad force-pushed the feat/component-calendar-link branch 6 times, most recently from b7c7159 to 4da188d Compare April 12, 2026 16:20
@zairigimad zairigimad marked this pull request as ready for review April 13, 2026 14:25
@Kocal
Copy link
Copy Markdown
Member

Kocal commented Apr 14, 2026

Hi, Symfony UX 3.0 has been released, and the 2.x branch is no longer maintained.

Could you please retarget this PR to the 3.x branch instead? That way we can review and merge it in the currently supported version.

Thanks!

@Kocal Kocal added Status: Needs Work Additional work is needed and removed Status: Needs Review Needs to be reviewed labels Apr 14, 2026
@zairigimad zairigimad force-pushed the feat/component-calendar-link branch from bb5ab31 to 13a1fc5 Compare April 15, 2026 15:42
@zairigimad zairigimad requested a review from Kocal as a code owner April 15, 2026 15:42
@carsonbot carsonbot added Status: Needs Review Needs to be reviewed and removed Status: Needs Work Additional work is needed labels Apr 15, 2026
@zairigimad zairigimad changed the base branch from 2.x to 3.x April 15, 2026 15:42
@github-actions
Copy link
Copy Markdown
Contributor

📊 Packages dist files size difference

Thanks for the PR! Here is the difference in size of the packages dist files between the base branch and the PR.
Please review the changes and make sure they are expected.

FileBefore (Size / Gzip)After (Size / Gzip)
LazyImage
controller.d.ts 395 B / 257 B Removed
controller.js 904 B / 457 B Removed
Map
abstract_map_controller.d.ts 7.6 kB / 1.49 kB 7.29 kB-4% 📉 / 1.46 kB-2% 📉
abstract_map_controller.js 4.92 kB / 1.4 kB 4.65 kB-5% 📉 / 1.26 kB-10% 📉
Map (Bridge Google)
map_controller.d.ts 10.56 kB / 1.92 kB 10.26 kB-3% 📉 / 1.9 kB-1% 📉
map_controller.js 12.9 kB / 3.19 kB 11.25 kB-13% 📉 / 2.84 kB-11% 📉
Map (Bridge Leaflet)
map_controller.d.ts 9.92 kB / 1.84 kB 9.61 kB-3% 📉 / 1.81 kB-2% 📉
map_controller.js 12.45 kB / 3.36 kB 11.38 kB-9% 📉 / 3.17 kB-6% 📉
Svelte
components.d.ts 200 B / 150 B Removed
components.js 46 B / 69 B Removed
loader.d.ts 435 B / 217 B Removed
loader.js 553 B / 313 B Removed
register_controller.d.ts 384 B / 235 B Removed
register_controller.js 531 B / 303 B Removed
render_controller.d.ts 629 B / 353 B Removed
render_controller.js 1.05 kB / 493 B Removed
Swup
controller.d.ts 1012 B / 360 B Removed
controller.js 1.71 kB / 653 B Removed
TogglePassword
controller.d.ts 896 B / 355 B Removed
controller.js 2.64 kB / 1.07 kB Removed
style.min.css 312 B / 218 B Removed
Typed
controller.d.ts 1.9 kB / 501 B Removed
controller.js 1.8 kB / 638 B Removed

@Kocal Kocal removed the Deprecation label Apr 17, 2026
@Kocal Kocal changed the title [CalendarLink] Init CalendarLink component [CalendarLink] Add component Apr 17, 2026
Copy link
Copy Markdown
Member

@Kocal Kocal left a comment

Choose a reason for hiding this comment

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

Thanks for working on this component!

Please ensure to read the whole review before starting anything, my mind changed during the review and I'm sure we don't need a dedicated route (and everything related) for downloading a .ics file.

Thanks you!

Comment thread src/CalendarLink/doc/index.rst Outdated
Comment thread src/CalendarLink/doc/index.rst Outdated
Comment thread src/CalendarLink/src/Controller/IcsDownloadController.php Outdated
Comment thread src/CalendarLink/doc/index.rst Outdated
Comment thread src/CalendarLink/src/Ics/IcsBuilder.php Outdated
Comment thread src/CalendarLink/src/UXCalendarLinkBundle.php Outdated
Comment on lines +66 to +76
$builder->setParameter('ux_calendar_link.ics.data_uri_threshold', $config['ics']['data_uri_threshold']);
$builder->setParameter('ux_calendar_link.ics.ttl', $config['ics']['ttl']);
$builder->setParameter('ux_calendar_link.ics.download_route.enabled', $config['ics']['download_route']['enabled']);
$builder->setParameter('ux_calendar_link.ics.download_route.path', $config['ics']['download_route']['path']);

if ($config['ics']['download_route']['enabled']) {
$builder->setAlias('ux_calendar_link.ics.event_store', 'ux_calendar_link.ics.event_store.cache');
$builder->removeDefinition('ux_calendar_link.ics.event_store.in_memory');
} else {
$builder->removeDefinition('ux_calendar_link.ics.event_store.cache');
}
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This won't be useful anymore

Comment thread src/CalendarLink/.symfony.bundle.yaml Outdated
Comment thread src/CalendarLink/CHANGELOG.md Outdated
Comment thread src/CalendarLink/CHANGELOG.md Outdated
@carsonbot carsonbot added Status: Needs Work Additional work is needed and removed Status: Needs Review Needs to be reviewed labels Apr 17, 2026
Comment thread src/CalendarLink/tests/bootstrap.php Outdated
Comment thread src/CalendarLink/composer.json Outdated
Comment thread src/CalendarLink/composer.json Outdated
@zairigimad
Copy link
Copy Markdown
Contributor Author

Thank you @Kocal for this great review and suggestions, indeed there are bunch of things to simplify here , I will take care of this

@Kocal
Copy link
Copy Markdown
Member

Kocal commented Apr 17, 2026

The package must also be added in the subtree split configuration file, at the repo's root

@carsonbot carsonbot added Status: Needs Review Needs to be reviewed and removed Status: Needs Work Additional work is needed labels Apr 18, 2026
@zairigimad zairigimad force-pushed the feat/component-calendar-link branch from 405061c to 8348232 Compare April 18, 2026 14:51
@zairigimad
Copy link
Copy Markdown
Contributor Author

The package must also be added in the subtree split configuration file, at the repo's root

I did a large cleanup here and the CI is green.

Copy link
Copy Markdown
Member

@Kocal Kocal left a comment

Choose a reason for hiding this comment

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

New round

Comment thread src/CalendarLink/config/services.php Outdated
Comment on lines +26 to +30
$services = $container->services()
->defaults()
->autowire()
->autoconfigure()
->private();
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

We do not configure services defaults in bundles:

Suggested change
$services = $container->services()
->defaults()
->autowire()
->autoconfigure()
->private();
$services = $container->services();

Comment thread src/CalendarLink/doc/index.rst
Comment thread src/CalendarLink/doc/index.rst Outdated
Comment on lines +4 to +7
**Symfony UX Calendar Link** generates "Add to calendar" links for Google
Calendar, Outlook.com, Office 365 and iCalendar (``.ics``) — the format
consumed by Apple Calendar, Outlook desktop, Thunderbird and every native
calendar client.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Suggested change
**Symfony UX Calendar Link** generates "Add to calendar" links for Google
Calendar, Outlook.com, Office 365 and iCalendar (``.ics``) the format
consumed by Apple Calendar, Outlook desktop, Thunderbird and every native
calendar client.
Symfony UX CalendarLink is a Symfony bundle that allows generator of "Add to calendar" links for Google
Calendar, Outlook.com, Office 365 and iCalendar (``.ics``), the format
consumed by Apple Calendar, Outlook desktop, Thunderbird and every native
calendar client.

Comment thread src/CalendarLink/doc/index.rst Outdated
Comment on lines +9 to +12
It addresses the long-standing request in
`symfony/ux#3308 <https://github.com/symfony/ux/issues/3308>`_ for a
first-party "Add to calendar" component in the Symfony UX initiative.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

To remove, that's not useful

Comment thread src/CalendarLink/doc/index.rst Outdated
Comment on lines +16 to +28
.. code-block:: terminal

$ composer require symfony/ux-calendar-link

If you're not using Symfony Flex, enable the bundle manually:

::

// config/bundles.php
return [
// ...
Symfony\UX\CalendarLink\UXCalendarLinkBundle::class => ['all' => true],
];
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Suggested change
.. code-block:: terminal
$ composer require symfony/ux-calendar-link
If you're not using Symfony Flex, enable the bundle manually:
::
// config/bundles.php
return [
// ...
Symfony\UX\CalendarLink\UXCalendarLinkBundle::class => ['all' => true],
];
Install the bundle using Composer and Symfony Flex:
.. code-block:: terminal
$ composer require symfony/ux-turbo

Comment thread src/CalendarLink/doc/index.rst Outdated
Comment on lines +99 to +115
A note on Apple Calendar and EventKit
-------------------------------------

Apple's EventKit (``EKEvent`` / ``EKEventStore``) is a **native iOS/macOS
API**, not a web URL scheme. There is no proprietary "Add to Apple Calendar"
deeplink. The correct way to target Apple Calendar from a web page is to
serve an RFC 5545 ``.ics`` file — Safari on iOS and macOS recognize the
``text/calendar`` MIME type and offers to add the event directly. The ``ics``
provider covers Apple Calendar, Outlook desktop, Thunderbird and every other
native calendar client in a single link.

ICS delivery
------------

The ``ics`` provider always returns a
``data:text/calendar;charset=utf-8;base64,...`` URL containing the full
VCALENDAR payload.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I don't think these paragraphs are useful

Comment thread src/CalendarLink/doc/index.rst Outdated
Reminders and recurrence
------------------------

::
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Add some text please

Comment thread src/CalendarLink/doc/index.rst Outdated
Comment on lines +136 to +138
accepting the usual ``interval``, ``count`` and ``until`` named arguments:

::
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Having :: alone is nonsense:

Suggested change
accepting the usual ``interval``, ``count`` and ``until`` named arguments:
::
accepting the usual ``interval``, ``count`` and ``until`` named arguments::

Comment thread src/CalendarLink/doc/index.rst Outdated
Comment on lines +140 to +142
CalendarRecurrence::daily(interval: 2); // every other day
CalendarRecurrence::monthly(count: 6); // six monthly occurrences
CalendarRecurrence::yearly(until: new \DateTimeImmutable('2030-01-01'));
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Suggested change
CalendarRecurrence::daily(interval: 2); // every other day
CalendarRecurrence::monthly(count: 6); // six monthly occurrences
CalendarRecurrence::yearly(until: new \DateTimeImmutable('2030-01-01'));
// every other day
CalendarRecurrence::daily(interval: 2);
// six monthly occurrences
CalendarRecurrence::monthly(count: 6);
// ...
CalendarRecurrence::yearly(until: new \DateTimeImmutable('2030-01-01'));

Comment thread src/CalendarLink/doc/index.rst Outdated
All-day events
--------------

::
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Add some text please

@carsonbot carsonbot added Status: Needs Work Additional work is needed Status: Needs Review Needs to be reviewed and removed Status: Needs Review Needs to be reviewed Status: Needs Work Additional work is needed labels Apr 21, 2026
@zairigimad zairigimad requested a review from Kocal April 21, 2026 14:29
@zairigimad zairigimad force-pushed the feat/component-calendar-link branch from 74889b4 to c83ae65 Compare April 21, 2026 14:30
@zairigimad zairigimad force-pushed the feat/component-calendar-link branch from c83ae65 to 3920086 Compare April 21, 2026 14:38
Copy link
Copy Markdown
Member

@Kocal Kocal left a comment

Choose a reason for hiding this comment

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

Thanks for taking care about my previous comments, here some new ones.

Overall it looks fine, but I still want to take some time to try it inside a real project.

Thanks!

Usage
-----

Start by creating a CalendarEvent object in your controller. Once populated with your event details, pass it to Twig to render the calendar links::
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Suggested change
Start by creating a CalendarEvent object in your controller. Once populated with your event details, pass it to Twig to render the calendar links::
Start by creating a ``CalendarEvent`` object in your controller. Once populated with your event details, pass it to Twig to render the calendar links::

start: new \DateTimeImmutable('2026-05-14 09:00'),
end: new \DateTimeImmutable('2026-05-15 18:00'),
location: 'Cité Universitaire Paris',
description: 'Annual Symfony conference',
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Suggested change
description: 'Annual Symfony conference',
description: 'Annual Symfony conference in France',

(since we also have SF Con, SF Day, and SF Live Berlin 😉)

Comment on lines +54 to +55
* ``ux_calendar_link(event, provider)`` — generates a link for one provider
* ``ux_calendar_links(event)`` — generates links for every registered provider
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Suggested change
* ``ux_calendar_link(event, provider)`` generates a link for one provider
* ``ux_calendar_links(event)`` generates links for every registered provider
* ``ux_calendar_link(event, provider)``: generates a link for one provider
* ``ux_calendar_links(event)``: generates links for every registered provider

PS: you can add this section to your AGENTS.md / skills :D

### Em-dash

Never use em-dashes (the `—` character). Use parentheses, commas, colons, or restructure the sentence instead.

| Bad | Good |
|-----|------|
| EN: "The tool — which was released last year — is now stable." | EN: "The tool (released last year) is now stable." |
| EN: "This has one major benefit — speed." | EN: "This has one major benefit: speed." |
| FR: "L'outil — sorti l'an dernier — est maintenant stable." | FR: "L'outil, sorti l'an dernier, est maintenant stable." |

Comment on lines +105 to +107
``CalendarRecurrence`` exposes one static factory per RFC 5545 frequency —
``minutely()``, ``daily()``, ``weekly()``, ``monthly()``, ``yearly()`` — each
accepting the usual ``interval``, ``count`` and ``until`` named arguments:
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Suggested change
``CalendarRecurrence`` exposes one static factory per RFC 5545 frequency
``minutely()``, ``daily()``, ``weekly()``, ``monthly()``, ``yearly()`` each
accepting the usual ``interval``, ``count`` and ``until`` named arguments:
``CalendarRecurrence`` exposes one static factory per RFC 5545 frequency:
``minutely()``, ``daily()``, ``weekly()``, ``monthly()``, ``yearly()``, each
accepting the usual ``interval``, ``count`` and ``until`` named arguments:

Comment on lines +68 to +78
private function uid(CalendarEvent $event): string
{
$hash = hash('xxh128', implode('|', [
$event->title,
$event->start->format(\DateTimeInterface::ATOM),
$event->end->format(\DateTimeInterface::ATOM),
$event->location ?? '',
]));

return $hash.'@ux-calendar-link.symfony';
}
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I don't really like this method, as two similar events with different description/url/... will have the same generated hash, which is wrong.

And as documented by https://icalendar.org/New-Properties-for-iCalendar-RFC-7986/5-3-uid-property.html, it's recommended to use a UUID to generate the UID field.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Comment on lines +39 to +43
$this->assertStringContainsString("BEGIN:VCALENDAR\r\n", $ics);
$this->assertStringContainsString("DTSTART:20260514T090000Z\r\n", $ics);
$this->assertStringContainsString("DTEND:20260514T100000Z\r\n", $ics);
$this->assertStringContainsString("SUMMARY:Demo\r\n", $ics);
$this->assertStringContainsString("END:VCALENDAR\r\n", $ics);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Can't we assert the whole string instead?

Comment on lines +3 to +4
EXPERIMENTAL This component is currently experimental and is likely to
change, or even change drastically.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Suggested change
EXPERIMENTAL This component is currently experimental and is likely to
change, or even change drastically.
**EXPERIMENTAL** This component is currently experimental and is
likely to change, or even change drastically.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

(please mimic the other README.md files)

Comment on lines +14 to +36
## Basic usage

```php
use Symfony\UX\CalendarLink\CalendarEvent;

$event = new CalendarEvent(
title: 'Symfony Live Paris',
start: new \DateTimeImmutable('2026-05-14 09:00'),
end: new \DateTimeImmutable('2026-05-15 18:00'),
location: 'Disneyland Paris',
description: 'Annual Symfony conference',
);

return $this->render('event/show.html.twig', ['event' => $event]);
```

```twig
<a href="{{ ux_calendar_link(event, 'google').url }}">Add to Google Calendar</a>

{% for link in ux_calendar_links(event) %}
<a href="{{ link.url }}">{{ link.label }}</a>
{% endfor %}
```
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

To remove

Comment on lines +11 to +12
This repository is a READ-ONLY sub-tree split. See https://github.com/symfony/ux
to create issues or submit pull requests.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Suggested change
This repository is a READ-ONLY sub-tree split. See https://github.com/symfony/ux
to create issues or submit pull requests.
**This repository is a READ-ONLY sub-tree split**. See
https://github.com/symfony/ux to create issues or submit pull requests.

change, or even change drastically.

Symfony UX Calendar Link generates "Add to calendar" links for Google
Calendar, Outlook.com, Office 365 and iCalendar (`.ics`) — the format
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Suggested change
Calendar, Outlook.com, Office 365 and iCalendar (`.ics`) the format
Calendar, Outlook.com, Office 365 and iCalendar (`.ics`), the format

@carsonbot carsonbot added Status: Needs Work Additional work is needed and removed Status: Needs Review Needs to be reviewed labels Apr 26, 2026
Comment on lines +1 to +27
<?xml version="1.0" encoding="UTF-8"?>

<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/9.5/phpunit.xsd"
backupGlobals="false"
colors="true"
bootstrap="tests/bootstrap.php"
failOnRisky="true"
failOnWarning="true"
>
<php>
<ini name="error_reporting" value="-1"/>
<server name="KERNEL_CLASS" value="Symfony\UX\CalendarLink\Tests\Fixtures\TestKernel"/>
</php>

<testsuites>
<testsuite name="symfony/ux-calendar-link Test Suite">
<directory>./tests/</directory>
</testsuite>
</testsuites>

<coverage>
<include>
<directory>./src</directory>
</include>
</coverage>
</phpunit>
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This is what PHPUnit configuration file from other UX packages looks like:

Suggested change
<?xml version="1.0" encoding="UTF-8"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/9.5/phpunit.xsd"
backupGlobals="false"
colors="true"
bootstrap="tests/bootstrap.php"
failOnRisky="true"
failOnWarning="true"
>
<php>
<ini name="error_reporting" value="-1"/>
<server name="KERNEL_CLASS" value="Symfony\UX\CalendarLink\Tests\Fixtures\TestKernel"/>
</php>
<testsuites>
<testsuite name="symfony/ux-calendar-link Test Suite">
<directory>./tests/</directory>
</testsuite>
</testsuites>
<coverage>
<include>
<directory>./src</directory>
</include>
</coverage>
</phpunit>
<?xml version="1.0" encoding="UTF-8"?>
<!-- https://phpunit.de/manual/current/en/appendixes.configuration.html -->
<phpunit
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="vendor/phpunit/phpunit/phpunit.xsd"
colors="true"
bootstrap="tests/bootstrap.php"
failOnDeprecation="true"
failOnRisky="true"
failOnWarning="true"
cacheDirectory=".phpunit.cache"
>
<php>
<ini name="error_reporting" value="-1"/>
<env name="SHELL_VERBOSITY" value="-1"/>
<env name="KERNEL_CLASS" value="Symfony\UX\CalendarLink\Tests\Fixtures\TestKernel" />
</php>
<testsuites>
<testsuite name="Symfony UX CalendarLink Test Suite">
<directory>./tests</directory>
</testsuite>
</testsuites>
<source
ignoreSuppressionOfDeprecations="true"
ignoreIndirectDeprecations="true"
restrictNotices="true"
restrictWarnings="true"
>
<include>
<directory>src</directory>
</include>
<deprecationTrigger>
<function>trigger_deprecation</function>
</deprecationTrigger>
</source>
</phpunit>

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Feature New Feature Status: Needs Work Additional work is needed

Projects

None yet

Development

Successfully merging this pull request may close these issues.

"Add to calendar" component

3 participants