Skip to content

Commit dbbf94f

Browse files
committed
[Toolkit] Refactor ToolkitKit enum to ToolkitKitId, leverage description/uxIcon/installation steps in Kit VO
1 parent 43e95a3 commit dbbf94f

File tree

23 files changed

+236
-251
lines changed

23 files changed

+236
-251
lines changed

src/Toolkit/kits/shadcn/INSTALL.md

+102
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
# Getting started
2+
3+
This kit provides ready-to-use and fully-customizable UI Twig components based on [Shadcn UI](https://ui.shadcn.com/) components's **design**.
4+
5+
Please note that not every Shadcn UI component is available in this kit, but we are working on it!
6+
7+
## Requirements
8+
9+
This kit requires TailwindCSS to work:
10+
- If you use Symfony AssetMapper, you can install TailwindCSS with the [TailwindBundle](https://symfony.com/bundles/TailwindBundle/current/index.html),
11+
- If you use Webpack Encore, you can follow the [TailwindCSS installation guide for Symfony](https://tailwindcss.com/docs/installation/framework-guides/symfony)
12+
13+
## Installation
14+
15+
In your `assets/styles/app.css`, after the TailwindCSS imports, add the following code:
16+
17+
```css
18+
@custom-variant dark (&:is(.dark *));
19+
20+
:root {
21+
--radius: 0.625rem;
22+
--background: oklch(1 0 0);
23+
--foreground: oklch(0.145 0 0);
24+
--card: oklch(1 0 0);
25+
--card-foreground: oklch(0.145 0 0);
26+
--popover: oklch(1 0 0);
27+
--popover-foreground: oklch(0.145 0 0);
28+
--primary: oklch(0.205 0 0);
29+
--primary-foreground: oklch(0.985 0 0);
30+
--secondary: oklch(0.97 0 0);
31+
--secondary-foreground: oklch(0.205 0 0);
32+
--muted: oklch(0.97 0 0);
33+
--muted-foreground: oklch(0.556 0 0);
34+
--accent: oklch(0.97 0 0);
35+
--accent-foreground: oklch(0.205 0 0);
36+
--destructive: oklch(0.577 0.245 27.325);
37+
--border: oklch(0.922 0 0);
38+
--input: oklch(0.922 0 0);
39+
--ring: oklch(0.708 0 0);
40+
--chart-1: oklch(0.646 0.222 41.116);
41+
--chart-2: oklch(0.6 0.118 184.704);
42+
--chart-3: oklch(0.398 0.07 227.392);
43+
--chart-4: oklch(0.828 0.189 84.429);
44+
--chart-5: oklch(0.769 0.188 70.08);
45+
--sidebar: oklch(0.985 0 0);
46+
--sidebar-foreground: oklch(0.145 0 0);
47+
--sidebar-primary: oklch(0.205 0 0);
48+
--sidebar-primary-foreground: oklch(0.985 0 0);
49+
--sidebar-accent: oklch(0.97 0 0);
50+
--sidebar-accent-foreground: oklch(0.205 0 0);
51+
--sidebar-border: oklch(0.922 0 0);
52+
--sidebar-ring: oklch(0.708 0 0);
53+
}
54+
55+
.dark {
56+
--background: oklch(0.145 0 0);
57+
--foreground: oklch(0.985 0 0);
58+
--card: oklch(0.205 0 0);
59+
--card-foreground: oklch(0.985 0 0);
60+
--popover: oklch(0.269 0 0);
61+
--popover-foreground: oklch(0.985 0 0);
62+
--primary: oklch(0.922 0 0);
63+
--primary-foreground: oklch(0.205 0 0);
64+
--secondary: oklch(0.269 0 0);
65+
--secondary-foreground: oklch(0.985 0 0);
66+
--muted: oklch(0.269 0 0);
67+
--muted-foreground: oklch(0.708 0 0);
68+
--accent: oklch(0.371 0 0);
69+
--accent-foreground: oklch(0.985 0 0);
70+
--destructive: oklch(0.704 0.191 22.216);
71+
--border: oklch(1 0 0 / 10%);
72+
--input: oklch(1 0 0 / 15%);
73+
--ring: oklch(0.556 0 0);
74+
--chart-1: oklch(0.488 0.243 264.376);
75+
--chart-2: oklch(0.696 0.17 162.48);
76+
--chart-3: oklch(0.769 0.188 70.08);
77+
--chart-4: oklch(0.627 0.265 303.9);
78+
--chart-5: oklch(0.645 0.246 16.439);
79+
--sidebar: oklch(0.205 0 0);
80+
--sidebar-foreground: oklch(0.985 0 0);
81+
--sidebar-primary: oklch(0.488 0.243 264.376);
82+
--sidebar-primary-foreground: oklch(0.985 0 0);
83+
--sidebar-accent: oklch(0.269 0 0);
84+
--sidebar-accent-foreground: oklch(0.985 0 0);
85+
--sidebar-border: oklch(1 0 0 / 10%);
86+
--sidebar-ring: oklch(0.439 0 0);
87+
}
88+
89+
@layer base {
90+
* {
91+
border-color: var(--border);
92+
outline-color: var(--ring);
93+
}
94+
95+
body {
96+
background-color: var(--background);
97+
color: var(--foreground);
98+
}
99+
}
100+
```
101+
102+
And voilà! You are now ready to use Shadcn components in your Symfony project.

src/Toolkit/kits/shadcn/manifest.json

+4-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
{
2-
"name": "Shadcn",
2+
"name": "Shadcn UI",
3+
"description": "Component based on the Shadcn UI library, one of the most popular design systems in JavaScript world.",
34
"license": "MIT",
45
"homepage": "https://ux.symfony.com/components",
5-
"authors": ["Shadcn", "Symfony Community"]
6+
"authors": ["Shadcn", "Symfony Community"],
7+
"ux-icon": "simple-icons:shadcnui"
68
}

src/Toolkit/src/Assert.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
*/
2323
public static function kitName(string $name): void
2424
{
25-
if (1 !== preg_match('/^[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?$/', $name)) {
25+
if (1 !== preg_match('/^[a-zA-Z0-9](?:[a-zA-Z0-9-_ ]{0,61}[a-zA-Z0-9])?$/', $name)) {
2626
throw new \InvalidArgumentException(\sprintf('Invalid kit name "%s".', $name));
2727
}
2828
}

src/Toolkit/src/Kit/Kit.php

+3
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,9 @@ public function __construct(
3636
public readonly string $homepage,
3737
public readonly array $authors,
3838
public readonly string $license,
39+
public readonly ?string $description = null,
40+
public readonly ?string $uxIcon = null,
41+
public ?string $installAsMarkdown = null,
3942
private array $components = [],
4043
) {
4144
Assert::kitName($this->name);

src/Toolkit/src/Kit/KitFactory.php

+25-7
Original file line numberDiff line numberDiff line change
@@ -54,11 +54,13 @@ public function createKitFromAbsolutePath(string $absolutePath): Kit
5454
$manifest = json_decode($this->filesystem->readFile($manifestPath), true, flags: \JSON_THROW_ON_ERROR);
5555

5656
$kit = new Kit(
57-
$absolutePath,
58-
$manifest['name'] ?? throw new \InvalidArgumentException('Manifest file is missing "name" key.'),
59-
$manifest['homepage'] ?? throw new \InvalidArgumentException('Manifest file is missing "homepage" key.'),
60-
$manifest['authors'] ?? throw new \InvalidArgumentException('Manifest file is missing "authors" key.'),
61-
$manifest['license'] ?? throw new \InvalidArgumentException('Manifest file is missing "license" key.'),
57+
path: $absolutePath,
58+
name: $manifest['name'] ?? throw new \InvalidArgumentException('Manifest file is missing "name" key.'),
59+
homepage: $manifest['homepage'] ?? throw new \InvalidArgumentException('Manifest file is missing "homepage" key.'),
60+
authors: $manifest['authors'] ?? throw new \InvalidArgumentException('Manifest file is missing "authors" key.'),
61+
license: $manifest['license'] ?? throw new \InvalidArgumentException('Manifest file is missing "license" key.'),
62+
description: $manifest['description'] ?? null,
63+
uxIcon: $manifest['ux-icon'] ?? null,
6264
);
6365

6466
$this->synchronizeKit($kit);
@@ -69,6 +71,7 @@ public function createKitFromAbsolutePath(string $absolutePath): Kit
6971
private function synchronizeKit(Kit $kit): void
7072
{
7173
$this->synchronizeKitComponents($kit);
74+
$this->synchronizeKitDocumentation($kit);
7275
}
7376

7477
private function synchronizeKitComponents(Kit $kit): void
@@ -86,15 +89,13 @@ private function synchronizeKitComponents(Kit $kit): void
8689
$relativePathNameToKit = $file->getRelativePathname();
8790
$relativePathName = str_replace($componentsPath.\DIRECTORY_SEPARATOR, '', $relativePathNameToKit);
8891
$componentName = $this->extractComponentName($relativePathName);
89-
$docPath = Path::join($kit->path, 'docs', 'components', $componentName.'.md');
9092
$component = new Component(
9193
name: $componentName,
9294
files: [new File(
9395
type: FileType::Twig,
9496
relativePathNameToKit: $relativePathNameToKit,
9597
relativePathName: $relativePathName,
9698
)],
97-
doc: $this->filesystem->exists($docPath) ? new Doc($this->filesystem->readFile($docPath)) : null,
9899
);
99100

100101
$kit->addComponent($component);
@@ -107,4 +108,21 @@ private static function extractComponentName(string $pathnameRelativeToKit): str
107108
{
108109
return str_replace(['.html.twig', '/'], ['', ':'], $pathnameRelativeToKit);
109110
}
111+
112+
private function synchronizeKitDocumentation(Kit $kit): void
113+
{
114+
// Read INSTALL.md if exists
115+
$fileInstall = Path::join($kit->path, 'INSTALL.md');
116+
if ($this->filesystem->exists($fileInstall)) {
117+
$kit->installAsMarkdown = $this->filesystem->readFile($fileInstall);
118+
}
119+
120+
// Iterate over Component and find their documentation
121+
foreach ($kit->getComponents() as $component) {
122+
$docPath = Path::join($kit->path, 'docs', 'components', $component->name.'.md');
123+
if ($this->filesystem->exists($docPath)) {
124+
$component->doc = new Doc($this->filesystem->readFile($docPath));
125+
}
126+
}
127+
}
110128
}

src/Toolkit/tests/AssertTest.php

+3-3
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,8 @@ public static function provideValidKitNames(): \Generator
3333
yield ['1-my-kit'];
3434
yield ['my-kit-1'];
3535
yield ['my-kit-1-with-dashes'];
36-
yield ['Shadcn-UI'];
37-
yield ['Shadcn-UI-1'];
36+
yield ['Shadcn UI'];
37+
yield ['Shadcn UI-1'];
3838
// Single character
3939
yield ['a'];
4040
yield ['1'];
@@ -47,6 +47,7 @@ public static function provideValidKitNames(): \Generator
4747
yield ['a-b-c'];
4848
yield ['a1-b2-c3'];
4949
yield ['A1-B2-C3'];
50+
yield ['my_kit'];
5051
}
5152

5253
/**
@@ -71,7 +72,6 @@ public static function provideInvalidKitNames(): \Generator
7172
// Ending with hyphen
7273
yield ['my-kit-'];
7374
// Invalid characters
74-
yield ['my_kit'];
7575
yield ['my.kit'];
7676
yield ['my@kit'];
7777
// Too long (64 chars)

src/Toolkit/tests/Command/LintKitCommandTest.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ public function testShouldBeAbleToLint(): void
2424
$this->consoleCommand('ux:toolkit:lint-kit shadcn')
2525
->execute()
2626
->assertSuccessful()
27-
->assertOutputContains('The kit "Shadcn" is valid, it has 46 components')
27+
->assertOutputContains('The kit "Shadcn UI" is valid, it has 46 components')
2828
;
2929
}
3030
}

src/Toolkit/tests/Kit/KitFactoryTest.php

-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@
1414
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
1515
use Symfony\UX\Toolkit\Dependency\ComponentDependency;
1616
use Symfony\UX\Toolkit\Dependency\PhpPackageDependency;
17-
use Symfony\UX\Toolkit\Dependency\Version;
1817
use Symfony\UX\Toolkit\Kit\KitFactory;
1918

2019
final class KitFactoryTest extends KernelTestCase

src/Toolkit/tests/Registry/GitHubRegistryTest.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ public function testCanGetKitFromGithub(): void
6464
$kit = $githubRegistry->getKit('github.com/user/repo');
6565

6666
$this->assertTrue($isHttpClientCalled);
67-
$this->assertSame('Shadcn', $kit->name);
67+
$this->assertSame('Shadcn UI', $kit->name);
6868
$this->assertNotEmpty($kit->getComponents());
6969
$this->assertFileExists($kit->path);
7070
$this->assertFileExists(Path::join($kit->path, 'templates/components/Button.html.twig'));

src/Toolkit/tests/Registry/LocalRegistryTest.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,6 @@ public function testCanGetKit(): void
2828
$kit = $localRegistry->getKit('shadcn');
2929

3030
$this->assertInstanceOf(Kit::class, $kit);
31-
$this->assertSame('Shadcn', $kit->name);
31+
$this->assertSame('Shadcn UI', $kit->name);
3232
}
3333
}

ux.symfony.com/src/Controller/SitemapController.php

+4-6
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111

1212
namespace App\Controller;
1313

14-
use App\Enum\ToolkitKit;
1514
use App\Service\LiveDemoRepository;
1615
use App\Service\Toolkit\ToolkitService;
1716
use App\Service\UxPackageRepository;
@@ -67,12 +66,11 @@ private function getSitemapUrls(): iterable
6766
}
6867

6968
// Toolkit kits
70-
foreach ($this->toolkitService->getKits() as $kitName => $kit) {
71-
yield $this->generateAbsoluteUrl('app_toolkit_kit', ['kit' => $kitName]);
69+
foreach ($this->toolkitService->getKits() as $kitId => $kit) {
70+
yield $this->generateAbsoluteUrl('app_toolkit_kit', ['kitId' => $kitId]);
7271

73-
$toolkitKit = ToolkitKit::from($kitName);
74-
foreach ($this->toolkitService->getDocumentableComponents($toolkitKit) as $component) {
75-
yield $this->generateAbsoluteUrl('app_toolkit_component', ['kit' => $kitName, 'componentName' => $component->name]);
72+
foreach ($this->toolkitService->getDocumentableComponents($kit) as $component) {
73+
yield $this->generateAbsoluteUrl('app_toolkit_component', ['kitId' => $kitId, 'componentName' => $component->name]);
7674
}
7775
}
7876
}

ux.symfony.com/src/Controller/Toolkit/ComponentsController.php

+11-9
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111

1212
namespace App\Controller\Toolkit;
1313

14-
use App\Enum\ToolkitKit;
14+
use App\Enum\ToolkitKitId;
1515
use App\Service\Toolkit\ToolkitService;
1616
use App\Service\UxPackageRepository;
1717
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
@@ -39,7 +39,7 @@ public function __construct(
3939
}
4040

4141
#[Route('/toolkit/kits/{kit}/components/')]
42-
public function listComponents(ToolkitKit $kit): Response
42+
public function listComponents(ToolkitKitId $kit): Response
4343
{
4444
// TODO: implementing listing in the future :D
4545

@@ -48,27 +48,29 @@ public function listComponents(ToolkitKit $kit): Response
4848
], Response::HTTP_FOUND);
4949
}
5050

51-
#[Route('/toolkit/kits/{kit}/components/{componentName}', name: 'app_toolkit_component')]
52-
public function showComponent(ToolkitKit $kit, string $componentName): Response
51+
#[Route('/toolkit/kits/{kitId}/components/{componentName}', name: 'app_toolkit_component')]
52+
public function showComponent(ToolkitKitId $kitId, string $componentName): Response
5353
{
54-
if (null === $component = $this->toolkitService->getComponent($kit, $componentName)) {
54+
$kit = $this->toolkitService->getKit($kitId);
55+
if (null === $component = $kit->getComponent($componentName)) {
5556
throw $this->createNotFoundException(\sprintf('Component "%s" not found', $componentName));
5657
}
5758

5859
$package = $this->uxPackageRepository->find('toolkit');
5960

6061
return $this->render('toolkit/component.html.twig', [
6162
'package' => $package,
62-
'kit' => $kit,
6363
'components' => $this->toolkitService->getDocumentableComponents($kit),
64+
'kit' => $kit,
65+
'kit_id' => $kitId,
6466
'component' => $component,
6567
]);
6668
}
6769

6870
#[Route('/toolkit/component_preview', name: 'app_toolkit_component_preview')]
6971
public function previewComponent(
7072
Request $request,
71-
#[MapQueryParameter] ToolkitKit $toolkitKit,
73+
#[MapQueryParameter] ToolkitKitId $kitId,
7274
#[MapQueryParameter] string $code,
7375
#[MapQueryParameter] string $height,
7476
UriSigner $uriSigner,
@@ -84,7 +86,7 @@ public function previewComponent(
8486

8587
$profiler?->disable();
8688

87-
$kit = $this->toolkitService->getKit($toolkitKit);
89+
$kit = $this->toolkitService->getKit($kitId);
8890

8991
$twig->setLoader(new ChainLoader([
9092
new FilesystemLoader($kit->path.\DIRECTORY_SEPARATOR.'templates'.\DIRECTORY_SEPARATOR.'components'),
@@ -120,7 +122,7 @@ public function findAnonymousComponentTemplate(string $name): ?string
120122
<meta charset="utf-8">
121123
<title>Preview</title>
122124
<meta name="viewport" content="width=device-width, initial-scale=1">
123-
{{ importmap('toolkit-{$toolkitKit->value}') }}
125+
{{ importmap('toolkit-{$kitId->value}') }}
124126
</head>
125127
<body class="flex min-h-[{$height}] w-full justify-center p-5 items-center">{$code}</body>
126128
</html>

ux.symfony.com/src/Controller/Toolkit/KitsController.php

+7-11
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111

1212
namespace App\Controller\Toolkit;
1313

14-
use App\Enum\ToolkitKit;
14+
use App\Enum\ToolkitKitId;
1515
use App\Service\Toolkit\ToolkitService;
1616
use App\Service\UxPackageRepository;
1717
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
@@ -20,27 +20,23 @@
2020

2121
class KitsController extends AbstractController
2222
{
23-
public function __construct(
24-
private ToolkitService $toolkitService,
25-
private UxPackageRepository $uxPackageRepository,
26-
) {
27-
}
28-
2923
#[Route('/toolkit/kits')]
3024
public function listKits(): Response
3125
{
3226
return $this->redirectToRoute('app_toolkit', ['_fragment' => 'kits']);
3327
}
3428

35-
#[Route('/toolkit/kits/{kit}', name: 'app_toolkit_kit')]
36-
public function showKit(ToolkitKit $kit): Response
29+
#[Route('/toolkit/kits/{kitId}', name: 'app_toolkit_kit')]
30+
public function showKit(ToolkitKitId $kitId, ToolkitService $toolkitService, UxPackageRepository $uxPackageRepository): Response
3731
{
38-
$package = $this->uxPackageRepository->find('toolkit');
32+
$kit = $toolkitService->getKit($kitId);
33+
$package = $uxPackageRepository->find('toolkit');
3934

4035
return $this->render('toolkit/kit.html.twig', [
4136
'package' => $package,
4237
'kit' => $kit,
43-
'components' => $this->toolkitService->getDocumentableComponents($kit),
38+
'kit_id' => $kitId,
39+
'components' => $toolkitService->getDocumentableComponents($kit),
4440
]);
4541
}
4642
}

0 commit comments

Comments
 (0)