tollwerk / tw-componentlibrary
Component library features for your TYPO3 project
Installs: 7 199
Dependents: 0
Suggesters: 0
Security: 0
Stars: 6
Watchers: 2
Forks: 1
Open Issues: 2
Type:typo3-cms-extension
Requires
- ext-json: *
- alom/graphviz: ^1.1
- typo3/cms-core: ^10.0
Suggests
- ext-tidy: Enable source code formatting
Replaces
- tollwerk/tw-componentlibrary: v1.2.1
- typo3-ter/tw_componentlibrary: v1.2.1
README
Component library features for your TYPO3 project
About
This TYPO3 extension
- encourages and supports the development of self-contained, re-usable function and design modules ("components") along with your TYPO3 project and
- exposes these components via a JSON API so that component or pattern library, testing and styleguide tools like Fractal can extract and render your components individually and independently of your TYPO3 frontend.
Usage
Installation
Install the extension into your composer mode TYPO3 installation:
cd /path/to/site/root
composer require tollwerk/tw-componentlibrary
Alternatively, download the latest source package and extract its contents to typo3conf/ext/tw_componentlibrary
.
Enable the extension via the TYPO3 extension manager.
Component types
The extension distiguishes 5 main types of components:
- TypoScript components: Require a TypoScript path with an object definition to render (e.g.
lib.menu
, defined asHMENU
). - Fluid template components: Require a Fluid template file (e.g. an Extbase / Fluid partial or standalone Fluid template) and an optional set of rendering parameters / variables.
- Extbase plugin components: Require an Extbase controller, a controller action to call and possibly a list of parameters to pass to the controller action.
- Content components: Convenient way to render existing TYPO3 content elements as components. Works with both default and custom content elements.
- Form components: Hook into the TYPO3 Form Framework and treat standard and custom form elements as individual components.
The extension doesn't impose any requirements towards your TypoScript, Fluid templates or directory layout except that every component must be individually addressable. That is, you cannot expose e.g. just a part of a rendered Fluid template as a component. In that case, you'd have to outsource the desired part as a partial file of its own.
Declaring components
You can declare components either manually (described below) or using the command line component kickstarter .
- Create and install an empty TYPO3 extension that is going to hold your component definitions. Alternatively, pick an existing extension you've got write access to. It's possible to have multiple of these component provider extensions. If you're using and maintaining custom extensions anyway, I recommend using these for providing components on an per-extension basis.
- Create a
Components
directory inside the provider extension's root directory. In case you're running TYPO3 in composer mode, make sure this directory is properly mapped to theComponent
namespace. You might have to add something like this to your maincomposer.json
file (replace the vendor name, extension key and paths with appropriate local values):{ "autoload": { "psr-4": { "Vendor\\ExtKey\\Component\\": "web/typo3conf/ext/ext_key/Components/" } } }
- Especially if you're going to have a lot of components it's advisable to organise them in a hierarchical structure. Create a suitable directory layout below your
Components
folder to reflect this hierarchy. Use UpperCamelCase directory names without spaces, underscores and special characters — external systems can use the word boundaries for transforming these e.g. into a navigation tree. Your directory layout could look something like this:ext_key `-- Components |-- Composite | `-- Form `-- Generic |-- Form `-- Typography
- Start creating component declarations by creating PHP class files at appropriate locations in your directory layout. Each file must declare exactly one class extending one of the main component type base classes (see below). The file and class names must be identical, should be in UpperCamelCase and must end with the suffix
…Component
(respectively…Component.php
for the file name). The part before…Component
will be used as the component by external tools. In addition to the base version of a component you may provide variants of that component by adding an underscore and appendage to the file and class name. System like Fractal can use this for a grouped display of component variants:ext_key `-- Components `-- Generic `-- Form |-- ButtonComponent.php |-- Button_IconLeftComponent.php |-- Button_IconRightComponent.php |-- Button_LinkComponent.php |-- Button_LinkIconLeftComponent.php `-- Button_LinkIconRightComponent.php
- Use the
configure()
method of your component declaration to specify the individual component properties. While each component type brings a small set of specific properties (see below), the majority of instructions is common to all component types.
TypoScript component
Use the setTypoScriptKey()
method to specify the TypoScript object that renders the component output. The key will be evaluated for the page ID specified by the $page
property (see common properties).
<?php namespace Vendor\ExtKey\Component; use Tollwerk\TwComponentlibrary\Component\TypoScriptComponent; /** * Example TypoScript component */ class ExampleTypoScriptComponent extends TypoScriptComponent { /** * Configure the component */ protected function configure() { $this->setTypoScriptKey('lib.example'); } }
Fluid template component
Use the setTemplate()
method to specify the Fluid template file that renders the component output. Specify rendering parameters using the setParameter()
method (works just like $this->view->assign('param', 'value')
in Extbase controller actions).
<?php namespace Vendor\ExtKey\Component; use Tollwerk\TwComponentlibrary\Component\FluidTemplateComponent; /** * Example Fluid template component */ class ExampleFluidTemplateComponent extends FluidTemplateComponent { /** * Configure the component */ protected function configure() { $this->setTemplate('EXT:ext_kex/Resources/Private/Partials/Component.html'); $this->setParameter('param', 'value'); } }
For convenience reasons, Fluid template components can read in external JSON files for setting parameters. JSON parameter files must be named after their associated component files and lie in the same directory:
|-- ButtonComponent.json `-- ButtonComponent.php
The key / value pairs inside the JSON file will be used as input for the setParameter()
method.
Extbase plugin component
To configure an Extbase plugin component, use the setExtbaseConfiguration()
method to specify the plugin name, the controller class name and the controller action to be called. The output will be rendered using the Fluid template associated with the controller action. You can specify action arguments via setControllerActionArgument()
and simulate controller controller settings via setControllerSettings()
.
<?php namespace Vendor\ExtKey\Component; use Tollwerk\TwComponentlibrary\Component\ExtbaseComponent; /** * Example Extbase plugin component */ class ExampleExtbaseComponent extends ExtbaseComponent { /** * Configure the component */ protected function configure() { $this->setExtbaseConfiguration('PluginName', MyCustomController::class, 'action'); $this->setControllerActionArgument('param', [1, 2, 3]); $overrideExistingSettings = true; $this->setControllerSettings(['category' => 1], $overrideExistingSettings); } }
Content component
Use the setContentRecordId()
method to specify the UID of an existing content element (tt_content
table) that you want to render as a component (please make sure the content element is accessible / isn't hidden by any means).
<?php namespace Vendor\ExtKey\Component; use Tollwerk\TwComponentlibrary\Component\ContentComponent; /** * Example content component */ class ExampleContentComponent extends ContentComponent { /** * Configure the component */ protected function configure() { $this->setContentRecordId(123); } }
Form component
Inside your component definition, you must use the createElement()
method to instantiate a renderable form element (TYPO3\CMS\Form\Domain\Model\Renderable\AbstractRenderable
). This is basically the same object that you get via the Form Framework's Page::createElement()
method during API form composition. You can further configure the form element using its native methods (as you would do in a form factory class).
To simulate a form validation error for your element, simply call addElementError($message)
(the element must have been instantiated prior to adding errors). You can registrer multiple errors.
It is advised but not mandatory to register the form field's Fluid template using setTemplate()
for a nicer display in your component library.
<?php namespace Vendor\ExtKey\Component; use Tollwerk\TwComponentlibrary\Component\FormComponent; /** * Example form component */ class ExampleTextComponent extends FormComponent { /** * Configure the component */ protected function configure() { $this->setTemplate('EXT:example/Resources/Private/Partials/Form/Text.html'); $element = $this->createElement('Text', 'name'); $element->setProperty( 'fluidAdditionalAttributes', [ 'placeholder' => 'John Doe', ] ); $this->addError('Please enter a name'); } }
Common properties
There's a bunch of component properties and methods that are common to all component types. Some of them are controlled via TypoScript constants, others by overriding component class properties or calling shared configuration methods.
TypoScript constants
Use the TypoScript constants to globally configure the HTML documents wrapped around your components when rendered by external systems. You can add base files, web fonts and libraries this way (global.css
, jQuery, etc.). All resources can be referenced absolutely (starting with http://
or https://
), relatively (/fileadmin/css/...
) or using a TYPO3 extension prefix (EXT:ext_key/Resources/...
).
TypoScript-Namensraum: plugin.tx_twcomponentlibrary.settings
Component properties
There are a couple of protected object properties you can override in your component classes to alter the default behaviour.
- ¹
\TYPO3\CMS\Extbase\Mvc\Web\Request
- ²
\Tollwerk\TwComponentlibrary\Component\Preview\TemplateInterface
- ³
\Tollwerk\TwComponentlibrary\Component\Preview\FluidTemplate
Example usage:
<?php namespace Vendor\ExtKey\Component; use Tollwerk\TwComponentlibrary\Component\FluidTemplateComponent; /** * Example Fluid template component */ class ExampleFluidTemplateComponent extends FluidTemplateComponent { /** * Component status * * @var int */ protected $status = self::STATUS_READY; /** * Label * * @var string */ protected $label = 'Button with icon'; /** * Configure the component */ protected function configure() { $this->setTemplate('EXT:ext_kex/Resources/Private/Partials/Button/Icon.html'); } }
Configuration methods
Use the following methods to further configure your components.
<?php namespace Tollwerk\TwComponentlibrary\Component; use \Tollwerk\TwComponentlibrary\Component\ComponentInterface; /** * Abstract component */ abstract class AbstractComponent implements ComponentInterface { /** * Add a notice * * Fractal displays the notice in the "Notes" tab (only available for the default component variant) * * @param string $notice Notice */ protected function addNotice($notice) {} /** * Set a custom preview template * * Overrides the default preview template facilitating the `stylesheets`, `headerScript` and `footerScript` TypoScript constants * * @param TemplateInterface|string|null $preview Preview template */ protected function setPreview($preview) {} }
Preview templates
By default, the builtin FluidTemplate
is used for rendering components for external systems. You can use your custom template as long as you implement the TemplateInterface
. The default FluidTemplate
supports a couple of configuration methods:
<?php namespace Tollwerk\TwComponentlibrary\Component\Preview; use TYPO3\CMS\Core\Utility\GeneralUtility; /** * Basic preview template * * @package Tollwerk\TwComponentlibrary * @subpackage Tollwerk\TwComponentlibrary\Component */ class FluidTemplate implements TemplateInterface { /** * Add a CSS stylesheet * * Will be added in the `<head>` section of the preview template * * @param string $url CSS stylesheet URL */ public function addStylesheet($url){} /** * Add a header JavaScript * * Will be added in the `<head>` section of the preview template * * @param string $url Header JavaScript URL */ public function addHeaderScript($url){} /** * Add a header inclusion resource * * Path to a file to be included in the in the `<head>` section of * the preview template. Make sure to wrap the content e.g. in a * `<script>` or `<style>` element. * * @param string $path Header inclusion path */ public function addHeaderInclude($path) {} /** * Add a footer JavaScript * * Will be added just before the closing `</body>` element of the preview template * * @param string $path Footer JavaScript URL */ public function addFooterScript($url) {} /** * Add a footer inclusion resource * * Path to a file to be included in the in the `<head>` section of * the preview template. Make sure to wrap the content e.g. in a * `<script>` or `<style>` element. * * @param string $path Footer inclusion path */ public function addFooterInclude($path) {} }
Example usage:
<?php namespace Vendor\ExtKey\Component; use Tollwerk\TwComponentlibrary\Component\FluidTemplateComponent; /** * Example Fluid template component */ class ExampleFluidTemplateComponent extends FluidTemplateComponent { /** * Configure the component */ protected function configure() { $this->setTemplate('EXT:ext_kex/Resources/Private/Partials/Component.html'); // Configure the preview template $this->preview->addHeaderInclude('fileadmin/js/icons-loader.html'); $this->preview->addStylesheet('EXT:ext_key/Resources/Public/Css/example.min.css'); } }
Documentation
You can add documentation to your components in two ways:
-
By using
addNotice($str)
inside theconfigure()
method of a component. -
By creating a documentation directory named after the component (without variant suffix) and dropping documentation files in there. When the component is extracted,
- it will first be checked if a file named
index.md
,readme.md
or<component>.md
exists inside that directory. If it does, this file will be used as the main documentation. - If there's no such documentation index, a simple Markdown listing will be generated, enumerating all the files in the directory. Valid image files will be embedded as images, otherwise the linked file name will be shown.
Example:
|-- Button | |-- index.md | `-- screenshot.jpg `-- ButtonComponent.php
During component extraction, linked files in the documentation (including images) will be rewritten to their root relative path starting at your TYPO3 main directory.
- it will first be checked if a file named
Directory configuration
As of version 0.3.2, you can add directory specific configuration values to your component directory layout. When exporting a component, the values for each of its parent directories will be exported as well. Add configuration values to a directory by creating a file called local.json
containing an arbitrary JSON data structure, e.g.
{ "dirsort": 4, "label": 'Icons & images' }
It's up to the consuming application to use these values for particular purposes. The TYPO3-Fractal-bridge for example uses the dirsort
value for ordering the directories other than alphabetically and label
for component folder names that couldn't be achieved via real file system directory names.
Command line component kickstarter
The extension provides a command line component kickstarter which let's you scaffold new components with ease. It's implemented as an extbase CLI command:
php vendor/bin/typo3 component:create Test/Button fluid tw_tollwerk Tollwerk
The command takes 4 arguments (in the following order; you can also enter it with explicit argument names):
--name
: Directory path and name of the component within theComponents
directory of your provider extension.--type
: Component type, must be one offluid
,typoscript
,extbase
,content
orform
--extension
: Provider extension key--vendor
: Provider extension vendor name
The above command will kickstart a Fluid component named Button
at this location inside the tw_tollwerk
extension:
Components/ `-- Test `-- ButtonComponent.php
If you're mostly adding components to a particular provider extension, you can simplify the process by defining a default provider extension along with its corresponding default vendor name. To do so, please enter the extension configuration in the extension manager and provide these two settings:
You can then omit the --extension
and --vendor
arguments when calling the CLI command:
php vendor/bin/typo3 component:create Test/Button fluid
Extracting components
The extension adds an Extbase CLI command that lets you discover the declared components in JSON format on the command line:
vendor/bin/typo3 component:discover
Sample result:
[ { "status": "wip", "name": "My Widget", "variant": null, "label": "Alternative component label", "class": "Vendor\\ExtKey\\Component\\MyWidgetComponent", "type": "fluid", "valid": true, "parameters": [], "config": "EXT:ext_key/Resources/Private/Partials/Widget.html", "template": "<f:link.action action=\"...\">...</f:link.action>", "extension": "t3s", "preview": "<!DOCTYPE html><html lang=\"en\"><head><meta charset=\"UTF-8\"><title>{{ _target.label }} \u2014 Preview Layout</title></head><body>{{{ yield }}}</body></html>", "request": { "method": "GET", "arguments": { "L": 0, "id": 1 } }, "path": [ "Demo" ] } ]
Use the exposed JSON data in any way that makes sense for you. The Fractal-TYPO3 bridge, for instance, builds an explorable component library out of it.
Important note: As of version 1.0, the components declared by an extension will only be exported if the TypoScript setup of the extension features a features.exportComponents
key with value 1
. This is expected to be found at plugin.tx_myext
in the overall TypoScript configuration, where tx_myext
is built from the prefix tx_
and your extension key without underscores:
plugin.tx_myext {
features {
exportComponents = 1
}
}
You can make this configurable via the extension constants. The component library extension comes with a language label you can use for that (in constants.typoscript
):
# cat=plugin.tx_myext/enable/a; type=boolean; label=LLL:EXT:tw_componentlibrary/Resources/Private/Language/locallang_core.xlf:enable.export
exportComponents =
Rendering components
The extension introduces the new type
parameter value 2400
which is used for calling TYPO3 as rendering engine for single components. The request
will exclusively render the component \Vendor\ExtKey\Component\MyWidgetComponent
and return the generated source code without surrounding page level HTML.
Component dependency graphs
The extension introduces the new type
parameter value 2401
which is used for dynamically rendering component dependency graphs. The request
will create a single component dependency graph like this one:
To create an overview graph of all registered components, simply omit the component argument. The URL
will create a graph like this one:
You can use these graphs for documentation purposes or to display them in the Fractal component library tool.
Utilities
SvgIconUtility
The included class \Tollwerk\TwComponentlibrary\Utility\SvgIconUtility
helps you with listing SVG graphics in backend forms and custom applications. Provide a comma-separated list of directories using the TypoScript constant plugin.tx_twcomponentlibrary.settings.iconDirectories
. The following two methods will then find and return all *.svg
files in these directories.
SvgIconUtility::getIcons(int $id = 1, int $typeNum = 0): array
Returns an array of SVG graphics, with the file base names as values and the absolute file paths as keys. Optionally provide a page ID and type for which the icon directories should be determined.
SvgIconUtility::getIconTcaSelectItems(int $id = 1, int $typeNum = 0): array
Similar to getIcons()
, but returns the graphics ready for use in TCA select
fields.
TYPO3 Backend integration
The extension provides a simple integration so that you can update your component library from within the TYPO3 backend. So far, only the Fractal component library is supported. To enable Fractal support, follow these simple steps:
-
Enter the extension configuration from the extension manager and enable the use of Fractal:
-
Provide the absolute path to a shell script that is able to run the necessary steps to update and restart your component library. You'll find an example file at
Resources/Private/Script
.Updating Fractal with a shell script is easy: Assuming you're using the TYPO3 Fractal bridge, simply
cd
into your Fractal instance directory and issuefractal update-typo3
. It depends on your specific setup if there are some extra steps involved to re-initialize Fractal. -
Make sure the shell script is executable for the user your web server runs under and use
sudo
inside the script where necessary (might require configuring appropriatesudo
privileges which is beyond this documentation). Be careful — the shell script could harm your system if not crafted properly! -
As soon as you enabled support for a component library and the shell script is in place, you should find an additional menu item in the cache menu pulldown:
-
If done properly, you can now update and re-initialize your component library easily from within the TYPO3 backend. Depending on the number of components in your system the process of updating might take a while.
Contributing
Found a bug or have a feature request? Please have a look at the known issues first and open a new issue if necessary. Please see contributing and conduct for details.
Security
If you discover any security related issues, please email joschi@kuphal.net instead of using the issue tracker.
Credits
License
Copyright © 2018 Joschi Kuphal / joschi@kuphal.net. Licensed under the terms of the GPL v2 license.