Skip to content

Commit 86a47ea

Browse files
committed
Define classes interactively
1 parent 835d42b commit 86a47ea

File tree

6 files changed

+268
-5
lines changed

6 files changed

+268
-5
lines changed

src/Maker/MakeStimulusController.php

Lines changed: 47 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -57,12 +57,13 @@ public function interact(InputInterface $input, ConsoleStyle $io, Command $comma
5757
$command->addArgument('extension', InputArgument::OPTIONAL);
5858
$command->addArgument('targets', InputArgument::OPTIONAL);
5959
$command->addArgument('values', InputArgument::OPTIONAL);
60+
$command->addArgument('classes', InputArgument::OPTIONAL);
6061

6162
if ($input->getOption('typescript')) {
6263
$input->setArgument('extension', 'ts');
6364
} else {
6465
$chosenExtension = $io->choice(
65-
'Language (<fg=yellow>JavaScript</> or <fg=yellow>TypeScript</>)',
66+
'Language (<fg=yellow>JavaScript</> or <fg=yellow>TypeScript</>)',
6667
[
6768
'js' => 'JavaScript',
6869
'ts' => 'TypeScript',
@@ -107,16 +108,35 @@ public function interact(InputInterface $input, ConsoleStyle $io, Command $comma
107108

108109
$input->setArgument('values', $values);
109110
}
111+
112+
if ($io->confirm('Do you want to add classes?', false)) {
113+
$classes = [];
114+
$isFirstClass = true;
115+
116+
while (true) {
117+
$newClass = $this->askForNextClass($io, $classes, $isFirstClass);
118+
if (null === $newClass) {
119+
break;
120+
}
121+
122+
$isFirstClass = false;
123+
$classes[] = $newClass;
124+
}
125+
126+
$input->setArgument('classes', $classes);
127+
}
110128
}
111129

112130
public function generate(InputInterface $input, ConsoleStyle $io, Generator $generator): void
113131
{
114132
$controllerName = Str::asSnakeCase($input->getArgument('name'));
115133
$chosenExtension = $input->getArgument('extension');
116-
$targets = $input->getArgument('targets');
117-
$values = $input->getArgument('values');
134+
$targets = $targetArgs = $input->getArgument('targets') ?? [];
135+
$values = $valuesArg = $input->getArgument('values') ?? [];
136+
$classes = $classesArgs = $input->getArgument('classes') ?? [];
118137

119138
$targets = empty($targets) ? $targets : \sprintf("['%s']", implode("', '", $targets));
139+
$classes = $classes ? \sprintf("['%s']", implode("', '", $classes)) : null;
120140

121141
$fileName = \sprintf('%s_controller.%s', $controllerName, $chosenExtension);
122142
$filePath = \sprintf('assets/controllers/%s', $fileName);
@@ -127,6 +147,7 @@ public function generate(InputInterface $input, ConsoleStyle $io, Generator $gen
127147
[
128148
'targets' => $targets,
129149
'values' => $values,
150+
'classes' => $classes,
130151
]
131152
);
132153

@@ -224,6 +245,29 @@ private function askForNextValue(ConsoleStyle $io, array $values, bool $isFirstV
224245
return ['name' => $valueName, 'type' => $type];
225246
}
226247

248+
/** @param string[] $classes */
249+
private function askForNextClass(ConsoleStyle $io, array $classes, bool $isFirstClass): ?string
250+
{
251+
$questionText = 'New class name (press <return> to stop adding classes)';
252+
253+
if (!$isFirstClass) {
254+
$questionText = 'Add another class? Enter the class name (or press <return> to stop adding classes)';
255+
}
256+
257+
$className = $io->ask($questionText, validator: function (?string $name) use ($classes) {
258+
if (str_contains($name, ' ')) {
259+
throw new \InvalidArgumentException('Class name cannot contain spaces.');
260+
}
261+
if (\in_array($name, $classes, true)) {
262+
throw new \InvalidArgumentException(\sprintf('The "%s" class already exists.', $name));
263+
}
264+
265+
return $name;
266+
});
267+
268+
return $className ?: null;
269+
}
270+
227271
private function printAvailableTypes(ConsoleStyle $io): void
228272
{
229273
foreach ($this->getValuesTypes() as $type) {

templates/stimulus/Controller.tpl.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
<?php endforeach; ?>
1616
}
1717
<?php } ?>
18+
<?= $classes ? " static classes = $classes\n" : '' ?>
1819

1920
initialize() {
2021
// Called once when the controller is first instantiated (per element)

tests/Maker/MakeStimulusControllerTest.php

Lines changed: 95 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ public function getTestDetails(): \Generator
3636
$this->assertFileExists($generatedFilePath);
3737
}),
3838
];
39-
39+
4040
yield 'it_generates_stimulus_controller_with_targets' => [$this->createMakerTest()
4141
->run(function (MakerTestRunner $runner) {
4242
$runner->runMaker(
@@ -87,6 +87,99 @@ public function getTestDetails(): \Generator
8787
}),
8888
];
8989

90+
yield 'it_generates_stimulus_controller_with_values' => [$this->createMakerTest()
91+
->run(function (MakerTestRunner $runner) {
92+
$runner->runMaker(
93+
[
94+
'with_values', // controller name
95+
'js', // controller language
96+
'no', // no targets
97+
'yes', // values
98+
'min', // first value
99+
'Number', // first value type
100+
'email', // second values
101+
'String', // second value type
102+
'', // empty input to stop adding values
103+
]);
104+
105+
$generatedFilePath = $runner->getPath('assets/controllers/with_values_controller.js');
106+
107+
$this->assertFileExists($generatedFilePath);
108+
109+
$generatedFileContents = file_get_contents($generatedFilePath);
110+
$expectedContents = file_get_contents(__DIR__.'/../fixtures/make-stimulus-controller/with_values.js');
111+
112+
$this->assertSame(
113+
$expectedContents,
114+
$generatedFileContents
115+
);
116+
}),
117+
];
118+
119+
yield 'it_generates_stimulus_controller_with_classes' => [$this->createMakerTest()
120+
->run(function (MakerTestRunner $runner) {
121+
$runner->runMaker(
122+
[
123+
'with_classes', // controller name
124+
'js', // use default extension (js)
125+
'no', // do not add targets
126+
'no', // do not add values
127+
'yes', // add classes
128+
'foo', // first class
129+
'bar', // second class
130+
'', // empty input to stop adding classes
131+
]);
132+
133+
$generatedFilePath = $runner->getPath('assets/controllers/with_classes_controller.js');
134+
135+
$this->assertFileExists($generatedFilePath);
136+
137+
$generatedFileContents = file_get_contents($generatedFilePath);
138+
$expectedContents = file_get_contents(__DIR__.'/../fixtures/make-stimulus-controller/with_classes.js');
139+
140+
$this->assertSame(
141+
$expectedContents,
142+
$generatedFileContents
143+
);
144+
}),
145+
];
146+
147+
yield 'it_generates_stimulus_controller_with_targets_values_and_classes' => [$this->createMakerTest()
148+
->run(function (MakerTestRunner $runner) {
149+
$runner->runMaker(
150+
[
151+
'with_targets_values_classes',
152+
'js',
153+
'yes', // add targets
154+
'aaa',
155+
'bbb',
156+
'', // end
157+
'yes', // add values
158+
'ccc',
159+
'Number',
160+
'ddd',
161+
'String',
162+
'', // end
163+
'yes', // add classes
164+
'eee',
165+
'fff',
166+
'', // end
167+
]);
168+
169+
$generatedFilePath = $runner->getPath('assets/controllers/with_targets_values_classes_controller.js');
170+
171+
$this->assertFileExists($generatedFilePath);
172+
173+
$generatedFileContents = file_get_contents($generatedFilePath);
174+
$expectedContents = file_get_contents(__DIR__.'/../fixtures/make-stimulus-controller/with_targets_values_classes.js');
175+
176+
$this->assertSame(
177+
$expectedContents,
178+
$generatedFileContents
179+
);
180+
}),
181+
];
182+
90183
yield 'it_generates_typescript_stimulus_controller_interactively' => [$this->createMakerTest()
91184
->run(function (MakerTestRunner $runner) {
92185
$runner->runMaker(
@@ -101,7 +194,7 @@ public function getTestDetails(): \Generator
101194
$this->assertFileDoesNotExist($runner->getPath('assets/controllers/typescript_controller.js'));
102195
}),
103196
];
104-
197+
105198
yield 'it_generates_typescript_stimulus_controller_when_option_is_set' => [$this->createMakerTest()
106199
->run(function (MakerTestRunner $runner) {
107200
$runner->runMaker(
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import { Controller } from '@hotwired/stimulus';
2+
3+
/*
4+
* The following line makes this controller "lazy": it won't be downloaded until needed
5+
* See https://symfony.com/bundles/StimulusBundle/current/index.html#lazy-stimulus-controllers
6+
*/
7+
8+
/* stimulusFetch: 'lazy' */
9+
export default class extends Controller {
10+
static classes = ['foo', 'bar']
11+
12+
initialize() {
13+
// Called once when the controller is first instantiated (per element)
14+
15+
// Here you can initialize variables, create scoped callables for event
16+
// listeners, instantiate external libraries, etc.
17+
// this._fooBar = this.fooBar.bind(this)
18+
}
19+
20+
connect() {
21+
// Called every time the controller is connected to the DOM
22+
// (on page load, when it's added to the DOM, moved in the DOM, etc.)
23+
24+
// Here you can add event listeners on the element or target elements,
25+
// add or remove classes, attributes, dispatch custom events, etc.
26+
// this.fooTarget.addEventListener('click', this._fooBar)
27+
}
28+
29+
// Add custom controller actions here
30+
// fooBar() { this.fooTarget.classList.toggle(this.bazClass) }
31+
32+
disconnect() {
33+
// Called anytime its element is disconnected from the DOM
34+
// (on page change, when it's removed from or moved in the DOM, etc.)
35+
36+
// Here you should remove all event listeners added in "connect()"
37+
// this.fooTarget.removeEventListener('click', this._fooBar)
38+
}
39+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import { Controller } from '@hotwired/stimulus';
2+
3+
/*
4+
* The following line makes this controller "lazy": it won't be downloaded until needed
5+
* See https://symfony.com/bundles/StimulusBundle/current/index.html#lazy-stimulus-controllers
6+
*/
7+
8+
/* stimulusFetch: 'lazy' */
9+
export default class extends Controller {
10+
static targets = ['aaa', 'bbb']
11+
static values = {
12+
ccc: Number,
13+
ddd: String,
14+
}
15+
static classes = ['eee', 'fff']
16+
17+
initialize() {
18+
// Called once when the controller is first instantiated (per element)
19+
20+
// Here you can initialize variables, create scoped callables for event
21+
// listeners, instantiate external libraries, etc.
22+
// this._fooBar = this.fooBar.bind(this)
23+
}
24+
25+
connect() {
26+
// Called every time the controller is connected to the DOM
27+
// (on page load, when it's added to the DOM, moved in the DOM, etc.)
28+
29+
// Here you can add event listeners on the element or target elements,
30+
// add or remove classes, attributes, dispatch custom events, etc.
31+
// this.fooTarget.addEventListener('click', this._fooBar)
32+
}
33+
34+
// Add custom controller actions here
35+
// fooBar() { this.fooTarget.classList.toggle(this.bazClass) }
36+
37+
disconnect() {
38+
// Called anytime its element is disconnected from the DOM
39+
// (on page change, when it's removed from or moved in the DOM, etc.)
40+
41+
// Here you should remove all event listeners added in "connect()"
42+
// this.fooTarget.removeEventListener('click', this._fooBar)
43+
}
44+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import { Controller } from '@hotwired/stimulus';
2+
3+
/*
4+
* The following line makes this controller "lazy": it won't be downloaded until needed
5+
* See https://symfony.com/bundles/StimulusBundle/current/index.html#lazy-stimulus-controllers
6+
*/
7+
8+
/* stimulusFetch: 'lazy' */
9+
export default class extends Controller {
10+
static values = {
11+
min: Number,
12+
email: String,
13+
}
14+
15+
initialize() {
16+
// Called once when the controller is first instantiated (per element)
17+
18+
// Here you can initialize variables, create scoped callables for event
19+
// listeners, instantiate external libraries, etc.
20+
// this._fooBar = this.fooBar.bind(this)
21+
}
22+
23+
connect() {
24+
// Called every time the controller is connected to the DOM
25+
// (on page load, when it's added to the DOM, moved in the DOM, etc.)
26+
27+
// Here you can add event listeners on the element or target elements,
28+
// add or remove classes, attributes, dispatch custom events, etc.
29+
// this.fooTarget.addEventListener('click', this._fooBar)
30+
}
31+
32+
// Add custom controller actions here
33+
// fooBar() { this.fooTarget.classList.toggle(this.bazClass) }
34+
35+
disconnect() {
36+
// Called anytime its element is disconnected from the DOM
37+
// (on page change, when it's removed from or moved in the DOM, etc.)
38+
39+
// Here you should remove all event listeners added in "connect()"
40+
// this.fooTarget.removeEventListener('click', this._fooBar)
41+
}
42+
}

0 commit comments

Comments
 (0)