Skip to content

Commit 337b860

Browse files
committed
feature #2972 [TogglePassword] Deprecate the package (Kocal)
This PR was merged into the 2.x branch. Discussion ---------- [TogglePassword] Deprecate the package | Q | A | ------------- | --- | Bug fix? | no | New feature? | yes <!-- please update src/**/CHANGELOG.md files --> | Docs? | no <!-- required for new features --> | Issues | Fix #2815 (partially) <!-- prefix each issue number with "Fix #", no need to create an issue if none exist, explain below instead --> | License | MIT <!-- Replace this notice by a description of your feature/bugfix. This will help reviewers and should be a good start for the documentation. Additionally (see https://symfony.com/releases): - Always add tests and ensure they pass. - For new features, provide some code snippets to help understand usage. - Features and deprecations must be submitted against branch main. - Update/add documentation as required (we can help!) - Changelog entry should follow https://symfony.com/doc/current/contributing/code/conventions.html#writing-a-changelog-entry - Never break backward compatibility (see https://symfony.com/bc). --> Following #2815 Commits ------- 7d44542 [TogglePassword] Deprecate the package
2 parents 306ad93 + 7d44542 commit 337b860

File tree

10 files changed

+248
-2
lines changed

10 files changed

+248
-2
lines changed

.github/workflows/unit-tests.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ jobs:
6565
6666
# Exclude deprecated packages when testing against lowest dependencies
6767
if [ "${{ matrix.dependency-version }}" = "lowest" ]; then
68-
EXCLUDED_PACKAGES="$EXCLUDED_PACKAGES|LazyImage"
68+
EXCLUDED_PACKAGES="$EXCLUDED_PACKAGES|LazyImage|TogglePassword"
6969
fi
7070
7171
PACKAGES=$(find src/ -mindepth 2 -type f -name composer.json -not -path "*/vendor/*" -printf '%h\n' | sed 's/^src\///' | grep -Ev "$EXCLUDED_PACKAGES" | sort | tr '\n' ' ')

src/TogglePassword/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
# CHANGELOG
22

3+
## 2.29.0
4+
5+
- Deprecate the package
6+
37
## 2.13.2
48

59
- Revert "Change JavaScript package to `type: module`"

src/TogglePassword/README.md

Lines changed: 226 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,231 @@
11
# Symfony UX TogglePassword
22

3+
> [!WARNING]
4+
> **Deprecated**: This package has been **deprecated** in 2.x and will be removed in the next major version.
5+
6+
To keep the same functionality in your Symfony application, follow these migration steps:
7+
8+
1. Remove the `symfony/ux-toggle-password` package from your project:
9+
```bash
10+
composer remove symfony/ux-toggle-password
11+
```
12+
13+
2. Create the following files in your project:
14+
15+
> [!NOTE]
16+
> These files are provided as a reference.
17+
> You can customize them to fit your needs, and even simplify the implementation if you don't need all the features.
18+
19+
- `src/Form/Extension/TogglePasswordTypeExtension.php`
20+
```php
21+
<?php
22+
23+
namespace App\Form\Extension;
24+
25+
use Symfony\Component\Form\AbstractTypeExtension;
26+
use Symfony\Component\Form\Extension\Core\Type\PasswordType;
27+
use Symfony\Component\Form\FormInterface;
28+
use Symfony\Component\Form\FormView;
29+
use Symfony\Component\OptionsResolver\Options;
30+
use Symfony\Component\OptionsResolver\OptionsResolver;
31+
use Symfony\Component\Translation\TranslatableMessage;
32+
use Symfony\Contracts\Translation\TranslatorInterface;
33+
34+
final class TogglePasswordTypeExtension extends AbstractTypeExtension
35+
{
36+
public function __construct(private readonly ?TranslatorInterface $translator)
37+
{
38+
}
39+
40+
public static function getExtendedTypes(): iterable
41+
{
42+
return [PasswordType::class];
43+
}
44+
45+
public function configureOptions(OptionsResolver $resolver): void
46+
{
47+
$resolver->setDefaults([
48+
'toggle' => false,
49+
'hidden_label' => 'Hide',
50+
'visible_label' => 'Show',
51+
'hidden_icon' => 'Default',
52+
'visible_icon' => 'Default',
53+
'button_classes' => ['toggle-password-button'],
54+
'toggle_container_classes' => ['toggle-password-container'],
55+
'toggle_translation_domain' => null,
56+
'use_toggle_form_theme' => true,
57+
]);
58+
59+
$resolver->setNormalizer(
60+
'toggle_translation_domain',
61+
static fn (Options $options, $labelTranslationDomain) => $labelTranslationDomain ?? $options['translation_domain'],
62+
);
63+
64+
$resolver->setAllowedTypes('toggle', ['bool']);
65+
$resolver->setAllowedTypes('hidden_label', ['string', TranslatableMessage::class, 'null']);
66+
$resolver->setAllowedTypes('visible_label', ['string', TranslatableMessage::class, 'null']);
67+
$resolver->setAllowedTypes('hidden_icon', ['string', 'null']);
68+
$resolver->setAllowedTypes('visible_icon', ['string', 'null']);
69+
$resolver->setAllowedTypes('button_classes', ['string[]']);
70+
$resolver->setAllowedTypes('toggle_container_classes', ['string[]']);
71+
$resolver->setAllowedTypes('toggle_translation_domain', ['string', 'bool', 'null']);
72+
$resolver->setAllowedTypes('use_toggle_form_theme', ['bool']);
73+
}
74+
75+
public function buildView(FormView $view, FormInterface $form, array $options): void
76+
{
77+
$view->vars['toggle'] = $options['toggle'];
78+
79+
if (!$options['toggle']) {
80+
return;
81+
}
82+
83+
if ($options['use_toggle_form_theme']) {
84+
array_splice($view->vars['block_prefixes'], -1, 0, 'toggle_password');
85+
}
86+
87+
$controllerName = 'toggle-password';
88+
$view->vars['attr']['data-controller'] = trim(\sprintf('%s %s', $view->vars['attr']['data-controller'] ?? '', $controllerName));
89+
90+
if (false !== $options['toggle_translation_domain']) {
91+
$controllerValues['hidden-label'] = $this->translateLabel($options['hidden_label'], $options['toggle_translation_domain']);
92+
$controllerValues['visible-label'] = $this->translateLabel($options['visible_label'], $options['toggle_translation_domain']);
93+
} else {
94+
$controllerValues['hidden-label'] = $options['hidden_label'];
95+
$controllerValues['visible-label'] = $options['visible_label'];
96+
}
97+
98+
$controllerValues['hidden-icon'] = $options['hidden_icon'];
99+
$controllerValues['visible-icon'] = $options['visible_icon'];
100+
$controllerValues['button-classes'] = json_encode($options['button_classes'], \JSON_THROW_ON_ERROR);
101+
102+
foreach ($controllerValues as $name => $value) {
103+
$view->vars['attr'][\sprintf('data-%s-%s-value', $controllerName, $name)] = $value;
104+
}
105+
106+
$view->vars['toggle_container_classes'] = $options['toggle_container_classes'];
107+
}
108+
109+
private function translateLabel(string|TranslatableMessage|null $label, ?string $translationDomain): ?string
110+
{
111+
if (null === $this->translator || null === $label) {
112+
return $label;
113+
}
114+
115+
if ($label instanceof TranslatableMessage) {
116+
return $label->trans($this->translator);
117+
}
118+
119+
return $this->translator->trans($label, domain: $translationDomain);
120+
}
121+
}
122+
```
123+
- `template/form_theme.html.twig`, and follow the [How to work with form themes](https://symfony.com/doc/current/form/form_themes.html) documentation to register it.
124+
```twig
125+
{%- block toggle_password_widget -%}
126+
<div class="{{ toggle_container_classes|join(' ') }}">{{ block('password_widget') }}</div>
127+
{%- endblock toggle_password_widget -%}
128+
```
129+
- `assets/controllers/toggle_password_controller.js`
130+
```javascript
131+
import { Controller } from '@hotwired/stimulus';
132+
import '../styles/toggle_password.css';
133+
134+
export default class extends Controller {
135+
static values = {
136+
visibleLabel: { type: String, default: 'Show' },
137+
visibleIcon: { type: String, default: 'Default' },
138+
hiddenLabel: { type: String, default: 'Hide' },
139+
hiddenIcon: { type: String, default: 'Default' },
140+
buttonClasses: Array,
141+
};
142+
143+
isDisplayed = false;
144+
visibleIcon = `<svg xmlns="http://www.w3.org/2000/svg" class="toggle-password-icon" viewBox="0 0 20 20" fill="currentColor">
145+
<path d="M10 12a2 2 0 100-4 2 2 0 000 4z" />
146+
<path fill-rule="evenodd" d="M.458 10C1.732 5.943 5.522 3 10 3s8.268 2.943 9.542 7c-1.274 4.057-5.064 7-9.542 7S1.732 14.057.458 10zM14 10a4 4 0 11-8 0 4 4 0 018 0z" clip-rule="evenodd" />
147+
</svg>`;
148+
hiddenIcon = `<svg xmlns="http://www.w3.org/2000/svg" class="toggle-password-icon" viewBox="0 0 20 20" fill="currentColor">
149+
<path fill-rule="evenodd" d="M3.707 2.293a1 1 0 00-1.414 1.414l14 14a1 1 0 001.414-1.414l-1.473-1.473A10.014 10.014 0 0019.542 10C18.268 5.943 14.478 3 10 3a9.958 9.958 0 00-4.512 1.074l-1.78-1.781zm4.261 4.26l1.514 1.515a2.003 2.003 0 012.45 2.45l1.514 1.514a4 4 0 00-5.478-5.478z" clip-rule="evenodd" />
150+
<path d="M12.454 16.697L9.75 13.992a4 4 0 01-3.742-3.741L2.335 6.578A9.98 9.98 0 00.458 10c1.274 4.057 5.065 7 9.542 7 .847 0 1.669-.105 2.454-.303z" />
151+
</svg>`;
152+
153+
connect() {
154+
if (this.visibleIconValue !== 'Default') {
155+
this.visibleIcon = this.visibleIconValue;
156+
}
157+
158+
if (this.hiddenIconValue !== 'Default') {
159+
this.hiddenIcon = this.hiddenIconValue;
160+
}
161+
162+
const button = this.createButton();
163+
164+
this.element.insertAdjacentElement('afterend', button);
165+
this.dispatchEvent('connect', { element: this.element, button });
166+
}
167+
168+
/**
169+
* @returns {HTMLButtonElement}
170+
*/
171+
createButton() {
172+
const button = document.createElement('button');
173+
button.type = 'button';
174+
button.classList.add(...this.buttonClassesValue);
175+
button.setAttribute('tabindex', '-1');
176+
button.addEventListener('click', this.toggle.bind(this));
177+
button.innerHTML = `${this.visibleIcon} ${this.visibleLabelValue}`;
178+
return button;
179+
}
180+
181+
/**
182+
* Toggle input type between "text" or "password" and update label accordingly
183+
*/
184+
toggle(event) {
185+
this.isDisplayed = !this.isDisplayed;
186+
const toggleButtonElement = event.currentTarget;
187+
toggleButtonElement.innerHTML = this.isDisplayed
188+
? `${this.hiddenIcon} ${this.hiddenLabelValue}`
189+
: `${this.visibleIcon} ${this.visibleLabelValue}`;
190+
this.element.setAttribute('type', this.isDisplayed ? 'text' : 'password');
191+
this.dispatchEvent(this.isDisplayed ? 'show' : 'hide', { element: this.element, button: toggleButtonElement });
192+
}
193+
194+
dispatchEvent(name, payload) {
195+
this.dispatch(name, { detail: payload, prefix: 'toggle-password' });
196+
}
197+
}
198+
```
199+
- `assets/styles/toggle_password.css`
200+
```css
201+
.toggle-password-container {
202+
position: relative;
203+
}
204+
.toggle-password-icon {
205+
height: 1rem;
206+
width: 1rem;
207+
}
208+
.toggle-password-button {
209+
align-items: center;
210+
background-color: transparent;
211+
border: none;
212+
column-gap: 0.25rem;
213+
display: flex;
214+
flex-direction: row;
215+
font-size: 0.875rem;
216+
justify-items: center;
217+
height: 1rem;
218+
line-height: 1.25rem;
219+
position: absolute;
220+
right: 0.5rem;
221+
top: -1.25rem;
222+
}
223+
```
224+
225+
You're done!
226+
227+
---
228+
3229
Symfony UX TogglePassword is a Symfony bundle providing visibility toggle for password inputs
4230
in Symfony Forms. It is part of [the Symfony UX initiative](https://ux.symfony.com/).
5231

src/TogglePassword/doc/index.rst

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,13 @@
11
Symfony UX TogglePassword
22
=========================
33

4+
.. warning::
5+
6+
**Deprecated: This package has been deprecated in 2.x and will be removed in the next major version.**
7+
8+
To keep the same functionality in your Symfony application, please follow the migration steps
9+
from the `Symfony UX TogglePassword README.md`_.
10+
411
Symfony UX TogglePassword is a Symfony bundle providing visibility toggle for password inputs
512
in Symfony Forms. It is part of `the Symfony UX initiative`_.
613

@@ -306,3 +313,4 @@ https://symfony.com/doc/current/contributing/code/bc.html
306313
.. _StimulusBundle configured in your app: https://symfony.com/bundles/StimulusBundle/current/index.html
307314
.. _Heroicons: https://heroicons.com/
308315
.. _`@symfony/ux-toggle-password npm package`: https://www.npmjs.com/package/@symfony/ux-toggle-password
316+
.. _`Symfony UX TogglePassword README.md`: https://github.com/symfony/ux/tree/2.x/src/TogglePassword/README.md

src/TogglePassword/phpunit.xml.dist

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
<php>
1212
<ini name="error_reporting" value="-1"/>
1313
<env name="SHELL_VERBOSITY" value="-1"/>
14-
<env name="SYMFONY_DEPRECATIONS_HELPER" value="max[self]=0&amp;max[direct]=0"/>
14+
<env name="SYMFONY_DEPRECATIONS_HELPER" value="ignoreFile=./tests/baseline-ignore&amp;max[self]=0&amp;max[direct]=0"/>
1515
</php>
1616

1717
<testsuites>

src/TogglePassword/src/DependencyInjection/TogglePasswordExtension.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
2020
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
2121
use Symfony\UX\TogglePassword\Form\TogglePasswordTypeExtension;
2222

23+
trigger_deprecation('symfony/ux-toggle-password', '2.29.0', 'The package is deprecated and will be removed in 3.0. Follow the migration steps in https://github.com/symfony/ux/tree/2.x/src/TogglePassword to keep using TogglePassword in your Symfony application.');
24+
2325
/**
2426
* @author Félix Eymonot <[email protected]>
2527
*/

src/TogglePassword/src/Form/TogglePasswordTypeExtension.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
2020
use Symfony\Component\Translation\TranslatableMessage;
2121
use Symfony\Contracts\Translation\TranslatorInterface;
2222

23+
trigger_deprecation('symfony/ux-toggle-password', '2.29.0', 'The package is deprecated and will be removed in 3.0. Follow the migration steps in https://github.com/symfony/ux/tree/2.x/src/TogglePassword to keep using TogglePassword in your Symfony application.');
24+
2325
/**
2426
* @author Félix Eymonot <[email protected]>
2527
*/

src/TogglePassword/src/TogglePasswordBundle.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313

1414
use Symfony\Component\HttpKernel\Bundle\Bundle;
1515

16+
trigger_deprecation('symfony/ux-toggle-password', '2.29.0', 'The package is deprecated and will be removed in 3.0. Follow the migration steps in https://github.com/symfony/ux/tree/2.x/src/TogglePassword to keep using TogglePassword in your Symfony application.');
17+
1618
/**
1719
* @author Félix Eymonot <[email protected]>
1820
*/
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
%Since symfony/ux-toggle-password 2\.29\.0: The package is deprecated and will be removed in 3\.0\.%

ux.symfony.com/tests/baseline-ignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
%Since symfony/ux-typed 2\.27\.0: The package is deprecated and will be removed in 3\.0\.%
22
%Since symfony/ux-lazy-image 2\.27\.0: The package is deprecated and will be removed in 3\.0\.%
33
%Since symfony/ux-swup 2\.27\.0: The package is deprecated and will be removed in 3\.0\.%
4+
%Since symfony/ux-toggle-password 2\.29\.0: The package is deprecated and will be removed in 3\.0\.%

0 commit comments

Comments
 (0)