Skip to content

Commit 208b3ff

Browse files
committed
feat: allow selecting fonts in design-builder
1 parent 37cc9eb commit 208b3ff

File tree

10 files changed

+295
-10
lines changed

10 files changed

+295
-10
lines changed

library/Styleguide/Customize/Customize.php

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -92,9 +92,11 @@ public function getCustomizeMarkup(): ?string
9292
return null;
9393
}
9494

95+
$overrideStateService = new \Municipio\Styleguide\Customize\OverrideState\OverrideState($this->wpService);
96+
97+
$overrideState = json_encode($overrideStateService->getOverrideState());
98+
$tokenData = json_encode((new \Municipio\Styleguide\Customize\TokenData\TokenData($this->wpService, $overrideStateService))->getTokenData());
9599
$componentData = json_encode((new \Municipio\Styleguide\Customize\ComponentData\ComponentData($this->wpService))->getComponentData());
96-
$tokenData = json_encode((new \Municipio\Styleguide\Customize\TokenData\TokenData($this->wpService))->getTokenData());
97-
$overrideState = json_encode((new \Municipio\Styleguide\Customize\OverrideState\OverrideState($this->wpService))->getOverrideState());
98100

99101
$componentData = htmlspecialchars($componentData, ENT_QUOTES, 'UTF-8');
100102
$tokenData = htmlspecialchars($tokenData, ENT_QUOTES, 'UTF-8');

library/Styleguide/Customize/OverrideState/OverrideState.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
use WpService\Contracts\ApplyFilters;
66
use WpService\Contracts\GetThemeMod;
77

8-
class OverrideState
8+
class OverrideState implements OverrideStateInterface
99
{
1010
public function __construct(
1111
private GetThemeMod&ApplyFilters $wpService,
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<?php
2+
3+
namespace Municipio\Styleguide\Customize\OverrideState;
4+
5+
use WpService\Contracts\ApplyFilters;
6+
use WpService\Contracts\GetThemeMod;
7+
8+
interface OverrideStateInterface
9+
{
10+
/**
11+
* Get the current override state from the database, and apply filters to it before returning. The override state is stored as a JSON string in the theme mods, and should be decoded into an array before being returned.
12+
* The returned array should have the following structure:
13+
* @return array{
14+
* token: array,
15+
* component: array,
16+
* }
17+
*/
18+
public function getOverrideState(): array;
19+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
<?php
2+
3+
namespace Municipio\Styleguide\Customize\TokenData\Decorators;
4+
5+
use Modularity\HooksRegistrar\Hookable;
6+
use Municipio\Styleguide\Customize\OverrideState\OverrideStateInterface;
7+
use WpService\Contracts\AddFilter;
8+
9+
/**
10+
* Ensure that the font chosen and stored for OverrideState is added to the options list, otherwise it will be lost when the user opens the select again and saves without changing the value.
11+
*/
12+
class AddOverrideFontFamilies implements Hookable
13+
{
14+
public function __construct(
15+
private AddFilter $wpService,
16+
private OverrideStateInterface $overrideState,
17+
) {}
18+
19+
public function addHooks(): void
20+
{
21+
$this->wpService->addFilter('Municipio/Styleguide/Customize/TokenData/FontFamilies', [$this, 'addOverrideFontFamilies']);
22+
}
23+
24+
public function addOverrideFontFamilies(array $options): array
25+
{
26+
$state = $this->overrideState->getOverrideState();
27+
28+
foreach (['--font-family-base', '--font-family-heading'] as $variable) {
29+
if (!empty($state['token'][$variable]) && is_string($state['token'][$variable])) {
30+
$options[] = [
31+
'value' => $state['token'][$variable],
32+
'label' => $state['token'][$variable],
33+
];
34+
}
35+
}
36+
37+
return $options;
38+
}
39+
}
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
<?php
2+
3+
namespace Municipio\Styleguide\Customize\TokenData\Decorators;
4+
5+
use Modularity\HooksRegistrar\Hookable;
6+
use Municipio\Styleguide\Customize\OverrideState\OverrideStateInterface;
7+
use PHPUnit\Framework\Attributes\TestDox;
8+
use PHPUnit\Framework\TestCase;
9+
use WpService\Contracts\AddFilter;
10+
11+
class AddOverrideFontFamiliesTest extends TestCase
12+
{
13+
#[TestDox('adds font family from override state to options')]
14+
public function testAddOverrideFontFamilies(): void
15+
{
16+
$overrideStateService = self::createOverrideStateService([
17+
'token' => [
18+
'--font-family-base' => 'Custom Font Base',
19+
'--font-family-heading' => 'Custom Font Heading',
20+
],
21+
]);
22+
23+
$decorator = new AddOverrideFontFamilies(self::createWpService(), $overrideStateService);
24+
25+
$options = [
26+
['value' => 'Default Font', 'label' => 'Default Font'],
27+
];
28+
29+
$result = $decorator->addOverrideFontFamilies($options);
30+
31+
static::assertCount(3, $result);
32+
static::assertEquals('Default Font', $result[0]['value']);
33+
static::assertEquals('Custom Font Base', $result[1]['value']);
34+
static::assertEquals('Custom Font Heading', $result[2]['value']);
35+
}
36+
37+
private static function createWpService(): AddFilter
38+
{
39+
return new class implements AddFilter {
40+
public function addFilter(string $hookName, callable $callback, int $priority = 10, int $acceptedArgs = 1): true
41+
{
42+
return true;
43+
}
44+
};
45+
}
46+
47+
private static function createOverrideStateService(array $overrideState): OverrideStateInterface
48+
{
49+
return new class($overrideState) implements OverrideStateInterface {
50+
private array $overrideState;
51+
52+
public function __construct(array $overrideState)
53+
{
54+
$this->overrideState = $overrideState;
55+
}
56+
57+
public function getOverrideState(): array
58+
{
59+
return $this->overrideState;
60+
}
61+
};
62+
}
63+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<?php
2+
3+
namespace Municipio\Styleguide\Customize\TokenData\Decorators;
4+
5+
interface DecoratorInterface
6+
{
7+
public function decorate(array $tokenData): array;
8+
}
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
<?php
2+
3+
namespace Municipio\Styleguide\Customize\TokenData\Decorators;
4+
5+
use WpService\Contracts\ApplyFilters;
6+
7+
class FontFamilies implements DecoratorInterface
8+
{
9+
public function __construct(
10+
private ApplyFilters $wpService,
11+
) {}
12+
13+
public function decorate(array $tokenData): array
14+
{
15+
$tagetVariables = ['--font-family-base', '--font-family-heading'];
16+
17+
foreach ($tokenData['categories'] as &$category) {
18+
foreach ($category['settings'] as &$setting) {
19+
if (in_array($setting['variable'], $tagetVariables, true)) {
20+
$setting = $this->modifySetting($setting);
21+
}
22+
}
23+
}
24+
25+
return $tokenData;
26+
}
27+
28+
private function modifySetting(array $setting): array
29+
{
30+
$setting = [
31+
...$setting,
32+
'type' => 'select',
33+
'options' => $this->getFontOptions($setting),
34+
];
35+
36+
return $setting;
37+
}
38+
39+
private function getFontOptions(array $setting): array
40+
{
41+
$options = [];
42+
43+
if (!empty($setting['default']) && is_string($setting['default'])) {
44+
$options[] = [
45+
'value' => $setting['default'],
46+
'label' => $setting['default'],
47+
];
48+
}
49+
50+
return $this->wpService->applyFilters('Municipio/Styleguide/Customize/TokenData/FontFamilies', array_merge($options, [
51+
['value' => 'Arial, sans-serif', 'label' => 'Arial'],
52+
['value' => 'Helvetica, sans-serif', 'label' => 'Helvetica'],
53+
['value' => 'sans-serif', 'label' => 'sans-serif'],
54+
]));
55+
}
56+
}
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
<?php
2+
3+
namespace Municipio\Styleguide\Customize\TokenData\Decorators;
4+
5+
use PHPUnit\Framework\Attributes\TestDox;
6+
use PHPUnit\Framework\TestCase;
7+
use WpService\Contracts\ApplyFilters;
8+
9+
class FontFamiliesTest extends TestCase
10+
{
11+
#[TestDox('turns --font-family-base into a select with options')]
12+
public function testDecorate(): void
13+
{
14+
$tokenData = [
15+
'categories' => [[
16+
'id' => 'typography',
17+
'settings' => [[
18+
'variable' => '--font-family-base',
19+
'type' => 'font',
20+
'default' => 'Default Font',
21+
]],
22+
]],
23+
];
24+
25+
$decorator = new FontFamilies(static::createWpService());
26+
$decoratedData = $decorator->decorate($tokenData);
27+
28+
static::assertSame('select', $decoratedData['categories'][0]['settings'][0]['type']);
29+
static::assertContains(
30+
[
31+
'value' => 'Arial, sans-serif',
32+
'label' => 'Arial',
33+
],
34+
$decoratedData['categories'][0]['settings'][0]['options'],
35+
);
36+
}
37+
38+
#[TestDox('adds default font value to options')]
39+
public function testDecorateWithDefaultFont(): void
40+
{
41+
$tokenData = [
42+
'categories' => [[
43+
'id' => 'typography',
44+
'settings' => [[
45+
'variable' => '--font-family-base',
46+
'type' => 'font',
47+
'default' => '"Roboto", sans-serif',
48+
]],
49+
]],
50+
];
51+
52+
$decorator = new FontFamilies(static::createWpService());
53+
$decoratedData = $decorator->decorate($tokenData);
54+
55+
static::assertContains(
56+
[
57+
'value' => '"Roboto", sans-serif',
58+
'label' => '"Roboto", sans-serif',
59+
],
60+
$decoratedData['categories'][0]['settings'][0]['options'],
61+
);
62+
}
63+
64+
private static function createWpService(): ApplyFilters
65+
{
66+
return new class implements ApplyFilters {
67+
public function applyFilters(string $hookName, mixed $value, mixed ...$args): mixed
68+
{
69+
return $value;
70+
}
71+
};
72+
}
73+
}

library/Styleguide/Customize/TokenData/TokenData.php

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,17 @@
33
namespace Municipio\Styleguide\Customize\TokenData;
44

55
use Composer\InstalledVersions;
6+
use Municipio\Styleguide\Customize\OverrideState\OverrideStateInterface;
7+
use WpService\Contracts\AddFilter;
68
use WpService\Contracts\ApplyFilters;
79

810
class TokenData
911
{
1012
private const STYLEGUIDE_PACKAGE = 'helsingborg-stad/styleguide';
1113

1214
public function __construct(
13-
private ApplyFilters $wpService,
15+
private ApplyFilters&AddFilter $wpService,
16+
private OverrideStateInterface $overrideStateService,
1417
) {}
1518

1619
public function getTokenData(): array
@@ -22,6 +25,10 @@ public function getTokenData(): array
2225
$contents = file_get_contents($this->getFilePath());
2326
$contents = $contents === false ? [] : json_decode($contents, true);
2427

28+
// Apply local decorators
29+
(new Decorators\AddOverrideFontFamilies($this->wpService, $this->overrideStateService))->addHooks();
30+
$contents = (new Decorators\FontFamilies($this->wpService))->decorate($contents);
31+
2532
return $this->wpService->applyFilters('Municipio/Styleguide/Customize/TokenData', $contents);
2633
}
2734

library/Styleguide/Customize/TokenData/TokenDataTest.php

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,10 @@
22

33
namespace Municipio\Styleguide\Customize\TokenData;
44

5+
use Municipio\Styleguide\Customize\OverrideState\OverrideStateInterface;
56
use PHPUnit\Framework\Attributes\TestDox;
67
use PHPUnit\Framework\TestCase;
8+
use WpService\Contracts\AddFilter;
79
use WpService\Contracts\ApplyFilters;
810
use WpService\Implementations\FakeWpService;
911

@@ -12,18 +14,34 @@ class TokenDataTest extends TestCase
1214
#[TestDox('returns file contents as array')]
1315
public function testGetTokenData(): void
1416
{
15-
$tokenData = new TokenData(static::createWpService());
17+
$tokenData = new TokenData(static::createWpService(), static::createOverrideStateService());
1618
$result = $tokenData->getTokenData();
1719

1820
$this->assertIsArray($result);
1921
}
2022

21-
private static function createWpService(): ApplyFilters
23+
private static function createWpService(): ApplyFilters&AddFilter
2224
{
23-
return new FakeWpService([
24-
'applyFilters' => function (string $filterName, $value) {
25+
return new class implements ApplyFilters, AddFilter {
26+
public function addFilter(string $hookName, callable $callback, int $priority = 10, int $acceptedArgs = 1): true
27+
{
28+
return true;
29+
}
30+
31+
public function applyFilters(string $hookName, mixed $value, mixed ...$args): mixed
32+
{
2533
return $value;
26-
},
27-
]);
34+
}
35+
};
36+
}
37+
38+
private static function createOverrideStateService(): OverrideStateInterface
39+
{
40+
return new class implements OverrideStateInterface {
41+
public function getOverrideState(): array
42+
{
43+
return [];
44+
}
45+
};
2846
}
2947
}

0 commit comments

Comments
 (0)