diff --git a/.env b/.env deleted file mode 100644 index 0782604c..00000000 --- a/.env +++ /dev/null @@ -1 +0,0 @@ -PHP_VERSION=8.0 diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml deleted file mode 100644 index 04f028a6..00000000 --- a/.github/workflows/ci.yml +++ /dev/null @@ -1,30 +0,0 @@ -name: ci -on: [push, pull_request_target] -jobs: - automated-tests: - runs-on: ubuntu-latest - strategy: - matrix: - PHP_VERSION: ["7.4", "8.0", "8.1"] - env: - PHP_VERSION: ${{ matrix.php_version }} - steps: - - name: clone - uses: actions/checkout@v2 - - run: docker-compose up -d - - name: composer install - run: docker-compose exec -T php composer install - - name: phpunit - run: docker-compose exec -T php ./vendor/bin/phpunit - code-sniffer: - runs-on: ubuntu-latest - env: - PHP_VERSION: "8.0" - steps: - - name: clone - uses: actions/checkout@v2 - - run: docker-compose up -d - - name: composer install - run: docker-compose exec -T php composer install - - name: grumphp - run: docker-compose exec -T php ./vendor/bin/grumphp run diff --git a/.github_changelog_generator b/.github_changelog_generator deleted file mode 100644 index 6d437180..00000000 --- a/.github_changelog_generator +++ /dev/null @@ -1,5 +0,0 @@ -merge_prefix=**Merged Pull Requests** -tag1='API change' -include-labels='API change,Bug,New feature' -enhancement_prefix=**New features** -enhancement-labels='New feature' diff --git a/.gitignore b/.gitignore deleted file mode 100644 index d8633ada..00000000 --- a/.gitignore +++ /dev/null @@ -1,10 +0,0 @@ -/.idea -/vendor -/composer.lock -/docs/_build -/build/ -/phpunit.xml -/docker-compose.override.yml -/runner.yml -/.editorconfig -/.gitattributes diff --git a/CHANGELOG.md b/CHANGELOG.md index ea637985..728e0aab 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -255,4 +255,4 @@ -\* *This Change Log was automatically generated by [github_changelog_generator](https://github.com/skywinder/Github-Changelog-Generator)* \ No newline at end of file +\* *This Change Log was automatically generated by [github_changelog_generator](https://github.com/skywinder/Github-Changelog-Generator)* diff --git a/LICENSE.txt b/LICENSE.txt deleted file mode 100644 index d159169d..00000000 --- a/LICENSE.txt +++ /dev/null @@ -1,339 +0,0 @@ - GNU GENERAL PUBLIC LICENSE - Version 2, June 1991 - - Copyright (C) 1989, 1991 Free Software Foundation, Inc., - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - The licenses for most software are designed to take away your -freedom to share and change it. By contrast, the GNU General Public -License is intended to guarantee your freedom to share and change free -software--to make sure the software is free for all its users. This -General Public License applies to most of the Free Software -Foundation's software and to any other program whose authors commit to -using it. (Some other Free Software Foundation software is covered by -the GNU Lesser General Public License instead.) You can apply it to -your programs, too. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -this service if you wish), that you receive source code or can get it -if you want it, that you can change the software or use pieces of it -in new free programs; and that you know you can do these things. - - To protect your rights, we need to make restrictions that forbid -anyone to deny you these rights or to ask you to surrender the rights. -These restrictions translate to certain responsibilities for you if you -distribute copies of the software, or if you modify it. - - For example, if you distribute copies of such a program, whether -gratis or for a fee, you must give the recipients all the rights that -you have. You must make sure that they, too, receive or can get the -source code. And you must show them these terms so they know their -rights. - - We protect your rights with two steps: (1) copyright the software, and -(2) offer you this license which gives you legal permission to copy, -distribute and/or modify the software. - - Also, for each author's protection and ours, we want to make certain -that everyone understands that there is no warranty for this free -software. If the software is modified by someone else and passed on, we -want its recipients to know that what they have is not the original, so -that any problems introduced by others will not reflect on the original -authors' reputations. - - Finally, any free program is threatened constantly by software -patents. We wish to avoid the danger that redistributors of a free -program will individually obtain patent licenses, in effect making the -program proprietary. To prevent this, we have made it clear that any -patent must be licensed for everyone's free use or not licensed at all. - - The precise terms and conditions for copying, distribution and -modification follow. - - GNU GENERAL PUBLIC LICENSE - TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION - - 0. This License applies to any program or other work which contains -a notice placed by the copyright holder saying it may be distributed -under the terms of this General Public License. The "Program", below, -refers to any such program or work, and a "work based on the Program" -means either the Program or any derivative work under copyright law: -that is to say, a work containing the Program or a portion of it, -either verbatim or with modifications and/or translated into another -language. (Hereinafter, translation is included without limitation in -the term "modification".) Each licensee is addressed as "you". - -Activities other than copying, distribution and modification are not -covered by this License; they are outside its scope. The act of -running the Program is not restricted, and the output from the Program -is covered only if its contents constitute a work based on the -Program (independent of having been made by running the Program). -Whether that is true depends on what the Program does. - - 1. You may copy and distribute verbatim copies of the Program's -source code as you receive it, in any medium, provided that you -conspicuously and appropriately publish on each copy an appropriate -copyright notice and disclaimer of warranty; keep intact all the -notices that refer to this License and to the absence of any warranty; -and give any other recipients of the Program a copy of this License -along with the Program. - -You may charge a fee for the physical act of transferring a copy, and -you may at your option offer warranty protection in exchange for a fee. - - 2. You may modify your copy or copies of the Program or any portion -of it, thus forming a work based on the Program, and copy and -distribute such modifications or work under the terms of Section 1 -above, provided that you also meet all of these conditions: - - a) You must cause the modified files to carry prominent notices - stating that you changed the files and the date of any change. - - b) You must cause any work that you distribute or publish, that in - whole or in part contains or is derived from the Program or any - part thereof, to be licensed as a whole at no charge to all third - parties under the terms of this License. - - c) If the modified program normally reads commands interactively - when run, you must cause it, when started running for such - interactive use in the most ordinary way, to print or display an - announcement including an appropriate copyright notice and a - notice that there is no warranty (or else, saying that you provide - a warranty) and that users may redistribute the program under - these conditions, and telling the user how to view a copy of this - License. (Exception: if the Program itself is interactive but - does not normally print such an announcement, your work based on - the Program is not required to print an announcement.) - -These requirements apply to the modified work as a whole. If -identifiable sections of that work are not derived from the Program, -and can be reasonably considered independent and separate works in -themselves, then this License, and its terms, do not apply to those -sections when you distribute them as separate works. But when you -distribute the same sections as part of a whole which is a work based -on the Program, the distribution of the whole must be on the terms of -this License, whose permissions for other licensees extend to the -entire whole, and thus to each and every part regardless of who wrote it. - -Thus, it is not the intent of this section to claim rights or contest -your rights to work written entirely by you; rather, the intent is to -exercise the right to control the distribution of derivative or -collective works based on the Program. - -In addition, mere aggregation of another work not based on the Program -with the Program (or with a work based on the Program) on a volume of -a storage or distribution medium does not bring the other work under -the scope of this License. - - 3. You may copy and distribute the Program (or a work based on it, -under Section 2) in object code or executable form under the terms of -Sections 1 and 2 above provided that you also do one of the following: - - a) Accompany it with the complete corresponding machine-readable - source code, which must be distributed under the terms of Sections - 1 and 2 above on a medium customarily used for software interchange; or, - - b) Accompany it with a written offer, valid for at least three - years, to give any third party, for a charge no more than your - cost of physically performing source distribution, a complete - machine-readable copy of the corresponding source code, to be - distributed under the terms of Sections 1 and 2 above on a medium - customarily used for software interchange; or, - - c) Accompany it with the information you received as to the offer - to distribute corresponding source code. (This alternative is - allowed only for noncommercial distribution and only if you - received the program in object code or executable form with such - an offer, in accord with Subsection b above.) - -The source code for a work means the preferred form of the work for -making modifications to it. For an executable work, complete source -code means all the source code for all modules it contains, plus any -associated interface definition files, plus the scripts used to -control compilation and installation of the executable. However, as a -special exception, the source code distributed need not include -anything that is normally distributed (in either source or binary -form) with the major components (compiler, kernel, and so on) of the -operating system on which the executable runs, unless that component -itself accompanies the executable. - -If distribution of executable or object code is made by offering -access to copy from a designated place, then offering equivalent -access to copy the source code from the same place counts as -distribution of the source code, even though third parties are not -compelled to copy the source along with the object code. - - 4. You may not copy, modify, sublicense, or distribute the Program -except as expressly provided under this License. Any attempt -otherwise to copy, modify, sublicense or distribute the Program is -void, and will automatically terminate your rights under this License. -However, parties who have received copies, or rights, from you under -this License will not have their licenses terminated so long as such -parties remain in full compliance. - - 5. You are not required to accept this License, since you have not -signed it. However, nothing else grants you permission to modify or -distribute the Program or its derivative works. These actions are -prohibited by law if you do not accept this License. Therefore, by -modifying or distributing the Program (or any work based on the -Program), you indicate your acceptance of this License to do so, and -all its terms and conditions for copying, distributing or modifying -the Program or works based on it. - - 6. Each time you redistribute the Program (or any work based on the -Program), the recipient automatically receives a license from the -original licensor to copy, distribute or modify the Program subject to -these terms and conditions. You may not impose any further -restrictions on the recipients' exercise of the rights granted herein. -You are not responsible for enforcing compliance by third parties to -this License. - - 7. If, as a consequence of a court judgment or allegation of patent -infringement or for any other reason (not limited to patent issues), -conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot -distribute so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you -may not distribute the Program at all. For example, if a patent -license would not permit royalty-free redistribution of the Program by -all those who receive copies directly or indirectly through you, then -the only way you could satisfy both it and this License would be to -refrain entirely from distribution of the Program. - -If any portion of this section is held invalid or unenforceable under -any particular circumstance, the balance of the section is intended to -apply and the section as a whole is intended to apply in other -circumstances. - -It is not the purpose of this section to induce you to infringe any -patents or other property right claims or to contest validity of any -such claims; this section has the sole purpose of protecting the -integrity of the free software distribution system, which is -implemented by public license practices. Many people have made -generous contributions to the wide range of software distributed -through that system in reliance on consistent application of that -system; it is up to the author/donor to decide if he or she is willing -to distribute software through any other system and a licensee cannot -impose that choice. - -This section is intended to make thoroughly clear what is believed to -be a consequence of the rest of this License. - - 8. If the distribution and/or use of the Program is restricted in -certain countries either by patents or by copyrighted interfaces, the -original copyright holder who places the Program under this License -may add an explicit geographical distribution limitation excluding -those countries, so that distribution is permitted only in or among -countries not thus excluded. In such case, this License incorporates -the limitation as if written in the body of this License. - - 9. The Free Software Foundation may publish revised and/or new versions -of the General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - -Each version is given a distinguishing version number. If the Program -specifies a version number of this License which applies to it and "any -later version", you have the option of following the terms and conditions -either of that version or of any later version published by the Free -Software Foundation. If the Program does not specify a version number of -this License, you may choose any version ever published by the Free Software -Foundation. - - 10. If you wish to incorporate parts of the Program into other free -programs whose distribution conditions are different, write to the author -to ask for permission. For software which is copyrighted by the Free -Software Foundation, write to the Free Software Foundation; we sometimes -make exceptions for this. Our decision will be guided by the two goals -of preserving the free status of all derivatives of our free software and -of promoting the sharing and reuse of software generally. - - NO WARRANTY - - 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY -FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN -OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES -PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED -OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS -TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE -PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, -REPAIR OR CORRECTION. - - 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR -REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, -INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING -OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED -TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY -YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER -PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE -POSSIBILITY OF SUCH DAMAGES. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -convey the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along - with this program; if not, write to the Free Software Foundation, Inc., - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - -Also add information on how to contact you by electronic and paper mail. - -If the program is interactive, make it output a short notice like this -when it starts in an interactive mode: - - Gnomovision version 69, Copyright (C) year name of author - Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the appropriate -parts of the General Public License. Of course, the commands you use may -be called something other than `show w' and `show c'; they could even be -mouse-clicks or menu items--whatever suits your program. - -You should also get your employer (if you work as a programmer) or your -school, if any, to sign a "copyright disclaimer" for the program, if -necessary. Here is a sample; alter the names: - - Yoyodyne, Inc., hereby disclaims all copyright interest in the program - `Gnomovision' (which makes passes at compilers) written by James Hacker. - - , 1 April 1989 - Ty Coon, President of Vice - -This General Public License does not permit incorporating your program into -proprietary programs. If your program is a subroutine library, you may -consider it more useful to permit linking proprietary applications with the -library. If this is what you want to do, use the GNU Lesser General -Public License instead of this License. diff --git a/README.md b/README.md index 92f5baab..18def1bf 100644 --- a/README.md +++ b/README.md @@ -1,72 +1,29 @@ -# UI Patterns +# UI Patterns 2.x -[![Join the chat at https://gitter.im/nuvoleweb/ui_patterns](https://badges.gitter.im/nuvoleweb/ui_patterns.svg)](https://gitter.im/nuvoleweb/ui_patterns?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) -[![Build Status](https://travis-ci.org/nuvoleweb/ui_patterns.svg?branch=8.x-1.x)](https://travis-ci.org/nuvoleweb/ui_patterns) -[![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/nuvoleweb/ui_patterns/badges/quality-score.png?b=8.x-1.x)](https://scrutinizer-ci.com/g/nuvoleweb/ui_patterns/?branch=8.x-1.x) -[![Documentation Status](https://readthedocs.org/projects/ui-patterns/badge/?version=8.x-1.x)](http://ui-patterns.readthedocs.io/en/8.x-1.x/?badge=8.x-1.x) +Define and expose self-contained UI Components as Drupal plugins and use them seamlessly in Drupal development and site-building. -Define and expose self-contained UI patterns as Drupal plugins and use them seamlessly as drop-in templates for -[panels](https://www.drupal.org/project/panels), [field groups](https://www.drupal.org/project/field_group), views, -[Display Suite](https://www.drupal.org/project/ds) view modes and field templates. +Also called "components", UI patterns are reusable, nestable, guided by clear standards, and can be assembled together to build any number of applications. Examples: card, button, slider, pager, menu, toast... -The UI Patterns module also integrates with with tools like [PatternLab](http://patternlab.io/) or modules like -[Component Libraries](https://www.drupal.org/project/components) thanks to -[definition overrides](http://ui-patterns.readthedocs.io/en/8.x-1.x/content/patterns-definition.html#override-patterns-behavior). - -![Overview](https://raw.githubusercontent.com/nuvoleweb/ui_patterns/8.x-1.x/docs/images/patterns-overview.png) +The UI Patterns module also integrates with with tools like [PatternLab](http://patternlab.io/) +or modules like [Component Libraries](https://www.drupal.org/project/components) +thanks to [definition overrides](https://www.drupal.org/docs/contributed-modules/ui-patterns/define-your-patterns#s-override-patterns-behavior). ## Project overview -The UI Patterns project provides 6 modules: +The UI Patterns project provides 5 modules: -- **UI Patterns**: the main module, it exposes the UI Patterns system APIs and it does not do much more than that. -- **UI Patterns Library**: allows to define patterns via YAML and generates a pattern library page available at `/patterns` +- **UI Patterns**: the main module, based on Drupal Core SDC API, with additional powerful API and quality-of-life improvments +- **UI Patterns Library**: generates a pattern library page available at `/patterns` to be used as documentation for content editors or as a showcase for business. Use this module if you don't plan to - use more advanced component library systems such as PatternLab or Fractal. - [Learn more](http://ui-patterns.readthedocs.io/en/8.x-1.x/content/patterns-definition.html) -- **UI Patterns Field Group**: allows to use patterns to format field groups provided by the - [Field group](https://www.drupal.org/project/field_group) module. - [Learn more](http://ui-patterns.readthedocs.io/en/8.x-1.x/content/field-group.html) -- **UI Patterns Layouts**: allows to use patterns as layouts. This allows patterns to be used on - [Display Suite](https://www.drupal.org/project/ds) view modes or on [panels](https://www.drupal.org/project/panels) - out of the box. [Learn more](http://ui-patterns.readthedocs.io/en/8.x-1.x/content/layout-plugin.html) -- **UI Patterns Display Suite**: allows to use patterns to format [Display Suite](https://www.drupal.org/project/ds) - field templates. [Learn more](http://ui-patterns.readthedocs.io/en/8.x-1.x/content/field-templates.html) -- **UI Patterns Views**: allows to use patterns as Views row templates. - [Learn more](http://ui-patterns.readthedocs.io/en/8.x-1.x/content/views.html) - -## Try it out! - -Download and install the [Bootstrap Patterns](https://github.com/nuvoleweb/bootstrap_patterns) theme on a vanilla Drupal -8 installation to quickly try out the UI Patterns module. - + use more advanced component library systems such as Storybook, PatternLab or Fractal. + [Learn more](https://www.drupal.org/docs/contributed-modules/ui-patterns/define-your-patterns) +- **UI Patterns Layouts**: allows to use patterns as layouts. This allows patterns to be used with Layout Builder, + [Display Suite](https://www.drupal.org/project/ds) or [Panels](https://www.drupal.org/project/panels) + out of the box. [Learn more](https://www.drupal.org/docs/contributed-modules/ui-patterns/use-patterns-as-layouts) +- **UI Patterns Views**: allows to use patterns as Views styles or Views rows. + [Learn more](https://www.drupal.org/docs/contributed-modules/ui-patterns/use-patterns-with-views) +- **UI Patterns Legacy**: Load your UI Patterns 1.x components inside UI Patterns 2.x ## Documentation -Documentation is hosted on [Read the Docs](https://readthedocs.org/) and available [here](http://ui-patterns.readthedocs.io/en/8.x-1.x). - -To build the documentation make sure you setup your environment by following -[these instructions](http://read-the-docs.readthedocs.io/en/latest/) first. - -After setting up your environment run: - -``` -$ cd docs -$ make html -``` - -The documentation is then available at ``./docs/_build/html/index.html``. - -If you want to contribute documentation you can setup and auto-compile that will watch for documentation changes by running: - -``` -$ make livehtml -``` - -You can then preview the compiled documentation at ``http://127.0.0.1:8000``. - -To build the documentation using Docker run: - -``` -$ docker run -it -v $(pwd)/docs:/docs xeizmendi/docker-sphinx make --directory=/docs html -``` +Documentation is available [here](https://www.drupal.org/docs/contributed-modules/ui-patterns). diff --git a/composer.json b/composer.json index fc932066..6a355514 100644 --- a/composer.json +++ b/composer.json @@ -1,77 +1,9 @@ { "name": "drupal/ui_patterns", + "description": "Define and expose self-contained UI components and use them seamlessly in development and site-building.", + "license": "GPL-2.0-or-later", "type": "drupal-module", - "description": "UI Patterns.", - "keywords": ["drupal", "web", "ui"], - "license": "GPL-2.0+", - "minimum-stability": "dev", - "prefer-stable": true, - "authors": [ - { - "name": "Nuvole Web", - "email": "info@nuvole.org" - } - ], - "require-dev": { - "composer/installers": "^1 || ^2", - "cweagans/composer-patches": "~1.4", - "drupal/core-composer-scaffold": "^8.8 || ^9", - "drupal/core-dev": "^8.8 || ^9", - "drupal/core-recommended": "^8.8 || ^9", - "drupal/ds": "~3", - "drupal/field_group": "~3", - "drupal/page_manager": "*", - "drupal/panels": "~4", - "drupal/paragraphs": "~1", - "drupal/token": "~1", - "drush/drush": "~10", - "openeuropa/task-runner-drupal-project-symlink": "^1.0-beta5", - "phpro/grumphp": "^1.5", - "phpspec/prophecy-phpunit": "^2" - }, - "repositories": [ - { - "type": "composer", - "url": "https://packages.drupal.org/8" - } - ], - "autoload": { - "psr-4": { - "Drupal\\ui_patterns\\": "./src" - } - }, - "autoload-dev": { - "psr-4": { - "Drupal\\Tests\\ui_patterns\\": "./tests/src" - } - }, - "scripts": { - "post-install-cmd": "./vendor/bin/run drupal:site-setup", - "post-update-cmd": "./vendor/bin/run drupal:site-setup" - }, - "extra": { - "composer-exit-on-patch-failure": true, - "enable-patching": true, - "drupal-scaffold": { - "locations": { - "web-root": "build/" - } - }, - "installer-paths": { - "build/core": ["type:drupal-core"], - "build/modules/contrib/{$name}": ["type:drupal-module"], - "build/profiles/contrib/{$name}": ["type:drupal-profile"], - "build/themes/contrib/{$name}": ["type:drupal-theme"] - } - }, - "config": { - "sort-packages": true, - "allow-plugins": { - "composer/installers": true, - "cweagans/composer-patches": true, - "drupal/core-composer-scaffold": true, - "phpro/grumphp": true, - "dealerdirect/phpcodesniffer-composer-installer": true - } + "require": { + "drupal/token": "^1.0" } } diff --git a/docker-compose.yml b/docker-compose.yml deleted file mode 100644 index f64c80d0..00000000 --- a/docker-compose.yml +++ /dev/null @@ -1,70 +0,0 @@ -version: "2" - -services: - - mariadb: - image: wodby/mariadb:10.3-3.8.4 - stop_grace_period: 30s - environment: - MYSQL_ROOT_PASSWORD: password - MYSQL_DATABASE: drupal - MYSQL_USER: drupal - MYSQL_PASSWORD: drupal - - php: - image: wodby/drupal-php:${PHP_VERSION} - environment: - DB_HOST: mariadb - DB_USER: drupal - DB_PASSWORD: drupal - DB_NAME: drupal - DB_DRIVER: mysql - PHP_XDEBUG: 1 - PHP_FPM_USER: wodby - PHP_FPM_GROUP: wodby - PHP_OPCACHE_PRELOAD_USER: wodby - PHP_XDEBUG_DEFAULT_ENABLE: 1 - PHP_XDEBUG_REMOTE_CONNECT_BACK: 1 - PHP_XDEBUG_REMOTE_HOST: "10.254.254.254" - PHP_XDEBUG_IDEKEY: "PHPSTORM" - PHP_IDE_CONFIG: "serverName=ui_patterns" - volumes: - - ./:/var/www/html - - nginx: - image: wodby/nginx:1.15-5.0.0 - depends_on: - - php - environment: - NGINX_STATIC_OPEN_FILE_CACHE: "off" - NGINX_ERROR_LOG_LEVEL: debug - NGINX_BACKEND_HOST: php - NGINX_SERVER_ROOT: /var/www/html/build - NGINX_VHOST_PRESET: drupal8 - volumes: - - ./:/var/www/html - ports: - - "8080:80" - - # If you would like to see what is going on you can run the following on your host: - # docker run --rm -p 4444:4444 -p 5900:5900 --network="host" selenium/standalone-chrome-debug:latest - # Newer version of this image might run into this issue: - # @link https://github.com/elgalu/docker-selenium/issues/20 - selenium: - image: selenium/standalone-chrome-debug:3.11 - expose: - - '4444' - environment: - - DISPLAY=:99 - - SE_OPTS=-debug - - SCREEN_WIDTH=1280 - - SCREEN_HEIGHT=800 - - VNC_NO_PASSWORD=1 - ports: - - '4444:4444' - - "5900:5900" - volumes: - - /dev/shm:/dev/shm - -volumes: - codebase: diff --git a/docs/Makefile b/docs/Makefile deleted file mode 100644 index 7dffbb8d..00000000 --- a/docs/Makefile +++ /dev/null @@ -1,195 +0,0 @@ -# Makefile for Sphinx documentation -# - -# You can set these variables from the command line. -SPHINXOPTS = -SPHINXBUILD = sphinx-build -PAPER = -BUILDDIR = _build - -# User-friendly check for sphinx-build -ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1) -$(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/) -endif - -# Internal variables. -PAPEROPT_a4 = -D latex_paper_size=a4 -PAPEROPT_letter = -D latex_paper_size=letter -ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . -# the i18n builder cannot share the environment and doctrees with the others -I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . - -.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest coverage gettext - -help: - @echo "Please use \`make ' where is one of" - @echo " html to make standalone HTML files" - @echo " dirhtml to make HTML files named index.html in directories" - @echo " singlehtml to make a single large HTML file" - @echo " pickle to make pickle files" - @echo " json to make JSON files" - @echo " htmlhelp to make HTML files and a HTML help project" - @echo " qthelp to make HTML files and a qthelp project" - @echo " applehelp to make an Apple Help Book" - @echo " devhelp to make HTML files and a Devhelp project" - @echo " epub to make an epub" - @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" - @echo " latexpdf to make LaTeX files and run them through pdflatex" - @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx" - @echo " text to make text files" - @echo " man to make manual pages" - @echo " texinfo to make Texinfo files" - @echo " info to make Texinfo files and run them through makeinfo" - @echo " gettext to make PO message catalogs" - @echo " changes to make an overview of all changed/added/deprecated items" - @echo " xml to make Docutils-native XML files" - @echo " pseudoxml to make pseudoxml-XML files for display purposes" - @echo " linkcheck to check all external links for integrity" - @echo " doctest to run all doctests embedded in the documentation (if enabled)" - @echo " coverage to run coverage check of the documentation (if enabled)" - -clean: - rm -rf $(BUILDDIR)/* - -html: - $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html - @echo - @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." - -dirhtml: - $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml - @echo - @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." - -singlehtml: - $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml - @echo - @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." - -pickle: - $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle - @echo - @echo "Build finished; now you can process the pickle files." - -json: - $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json - @echo - @echo "Build finished; now you can process the JSON files." - -htmlhelp: - $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp - @echo - @echo "Build finished; now you can run HTML Help Workshop with the" \ - ".hhp project file in $(BUILDDIR)/htmlhelp." - -qthelp: - $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp - @echo - @echo "Build finished; now you can run "qcollectiongenerator" with the" \ - ".qhcp project file in $(BUILDDIR)/qthelp, like this:" - @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/UIPatterns.qhcp" - @echo "To view the help file:" - @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/UIPatterns.qhc" - -applehelp: - $(SPHINXBUILD) -b applehelp $(ALLSPHINXOPTS) $(BUILDDIR)/applehelp - @echo - @echo "Build finished. The help book is in $(BUILDDIR)/applehelp." - @echo "N.B. You won't be able to view it unless you put it in" \ - "~/Library/Documentation/Help or install it in your application" \ - "bundle." - -devhelp: - $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp - @echo - @echo "Build finished." - @echo "To view the help file:" - @echo "# mkdir -p $$HOME/.local/share/devhelp/UIPatterns" - @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/UIPatterns" - @echo "# devhelp" - -epub: - $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub - @echo - @echo "Build finished. The epub file is in $(BUILDDIR)/epub." - -latex: - $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex - @echo - @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." - @echo "Run \`make' in that directory to run these through (pdf)latex" \ - "(use \`make latexpdf' here to do that automatically)." - -latexpdf: - $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex - @echo "Running LaTeX files through pdflatex..." - $(MAKE) -C $(BUILDDIR)/latex all-pdf - @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." - -latexpdfja: - $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex - @echo "Running LaTeX files through platex and dvipdfmx..." - $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja - @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." - -text: - $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text - @echo - @echo "Build finished. The text files are in $(BUILDDIR)/text." - -man: - $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man - @echo - @echo "Build finished. The manual pages are in $(BUILDDIR)/man." - -texinfo: - $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo - @echo - @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." - @echo "Run \`make' in that directory to run these through makeinfo" \ - "(use \`make info' here to do that automatically)." - -info: - $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo - @echo "Running Texinfo files through makeinfo..." - make -C $(BUILDDIR)/texinfo info - @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." - -gettext: - $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale - @echo - @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." - -changes: - $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes - @echo - @echo "The overview file is in $(BUILDDIR)/changes." - -linkcheck: - $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck - @echo - @echo "Link check complete; look for any errors in the above output " \ - "or in $(BUILDDIR)/linkcheck/output.txt." - -doctest: - $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest - @echo "Testing of doctests in the sources finished, look at the " \ - "results in $(BUILDDIR)/doctest/output.txt." - -coverage: - $(SPHINXBUILD) -b coverage $(ALLSPHINXOPTS) $(BUILDDIR)/coverage - @echo "Testing of coverage in the sources finished, look at the " \ - "results in $(BUILDDIR)/coverage/python.txt." - -xml: - $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml - @echo - @echo "Build finished. The XML files are in $(BUILDDIR)/xml." - -pseudoxml: - $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml - @echo - @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml." - -livehtml: - sphinx-autobuild -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html diff --git a/docs/conf.py b/docs/conf.py deleted file mode 100644 index dc212ace..00000000 --- a/docs/conf.py +++ /dev/null @@ -1,291 +0,0 @@ -# -*- coding: utf-8 -*- -# -# UI Patterns documentation build configuration file, created by -# sphinx-quickstart on Sun Jan 8 11:33:35 2017. -# -# This file is execfile()d with the current directory set to its -# containing dir. -# -# Note that not all possible configuration values are present in this -# autogenerated file. -# -# All configuration values have a default; values that are commented out -# serve to show the default. - -import sys -import os -import shlex - -# Check if this build is on readthedocs.org -on_rtd = os.environ.get('READTHEDOCS', None) == 'True' -if not on_rtd: - import sphinx_rtd_theme - html_theme = "sphinx_rtd_theme" - html_theme_path = [sphinx_rtd_theme.get_html_theme_path()] - -# If extensions (or modules to document with autodoc) are in another directory, -# add these directories to sys.path here. If the directory is relative to the -# documentation root, use os.path.abspath to make it absolute, like shown here. -#sys.path.insert(0, os.path.abspath('.')) - -# -- General configuration ------------------------------------------------ - -# If your documentation needs a minimal Sphinx version, state it here. -#needs_sphinx = '1.0' - -# Add any Sphinx extension module names here, as strings. They can be -# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom -# ones. -extensions = [] - -# Add any paths that contain templates here, relative to this directory. -templates_path = ['_templates'] - -# The suffix(es) of source filenames. -# You can specify multiple suffix as a list of string: -# source_suffix = ['.rst', '.md'] -source_suffix = '.rst' - -# The encoding of source files. -#source_encoding = 'utf-8-sig' - -# The master toctree document. -master_doc = 'index' - -# General information about the project. -project = u'UI Patterns' -copyright = u'2017, Nuvole Web' -author = u'Nuvole Web' - -# The version info for the project you're documenting, acts as replacement for -# |version| and |release|, also used in various other places throughout the -# built documents. -# -# The short X.Y version. -version = '1.x' -# The full version, including alpha/beta/rc tags. -release = '1.x' - -# The language for content autogenerated by Sphinx. Refer to documentation -# for a list of supported languages. -# -# This is also used if you do content translation via gettext catalogs. -# Usually you set "language" from the command line for these cases. -language = None - -# There are two options for replacing |today|: either, you set today to some -# non-false value, then it is used: -#today = '' -# Else, today_fmt is used as the format for a strftime call. -#today_fmt = '%B %d, %Y' - -# List of patterns, relative to source directory, that match files and -# directories to ignore when looking for source files. -exclude_patterns = ['_build'] - -# The reST default role (used for this markup: `text`) to use for all -# documents. -#default_role = None - -# If true, '()' will be appended to :func: etc. cross-reference text. -#add_function_parentheses = True - -# If true, the current module name will be prepended to all description -# unit titles (such as .. function::). -#add_module_names = True - -# If true, sectionauthor and moduleauthor directives will be shown in the -# output. They are ignored by default. -#show_authors = False - -# The name of the Pygments (syntax highlighting) style to use. -pygments_style = 'sphinx' - -# A list of ignored prefixes for module index sorting. -#modindex_common_prefix = [] - -# If true, keep warnings as "system message" paragraphs in the built documents. -#keep_warnings = False - -# If true, `todo` and `todoList` produce output, else they produce nothing. -todo_include_todos = False - - -# -- Options for HTML output ---------------------------------------------- - -# The theme to use for HTML and HTML Help pages. See the documentation for -# a list of builtin themes. -#html_theme = 'alabaster' - -# Theme options are theme-specific and customize the look and feel of a theme -# further. For a list of options available for each theme, see the -# documentation. -#html_theme_options = {} - -# Add any paths that contain custom themes here, relative to this directory. -#html_theme_path = [] - -# The name for this set of Sphinx documents. If None, it defaults to -# " v documentation". -#html_title = None - -# A shorter title for the navigation bar. Default is the same as html_title. -#html_short_title = None - -# The name of an image file (relative to this directory) to place at the top -# of the sidebar. -#html_logo = None - -# The name of an image file (within the static path) to use as favicon of the -# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 -# pixels large. -#html_favicon = None - -# Add any paths that contain custom static files (such as style sheets) here, -# relative to this directory. They are copied after the builtin static files, -# so a file named "default.css" will overwrite the builtin "default.css". -html_static_path = ['_static'] - -# Add any extra paths that contain custom files (such as robots.txt or -# .htaccess) here, relative to this directory. These files are copied -# directly to the root of the documentation. -#html_extra_path = [] - -# If not '', a 'Last updated on:' timestamp is inserted at every page bottom, -# using the given strftime format. -#html_last_updated_fmt = '%b %d, %Y' - -# If true, SmartyPants will be used to convert quotes and dashes to -# typographically correct entities. -#html_use_smartypants = True - -# Custom sidebar templates, maps document names to template names. -#html_sidebars = {} - -# Additional templates that should be rendered to pages, maps page names to -# template names. -#html_additional_pages = {} - -# If false, no module index is generated. -#html_domain_indices = True - -# If false, no index is generated. -#html_use_index = True - -# If true, the index is split into individual pages for each letter. -#html_split_index = False - -# If true, links to the reST sources are added to the pages. -#html_show_sourcelink = True - -# If true, "Created using Sphinx" is shown in the HTML footer. Default is True. -#html_show_sphinx = True - -# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. -#html_show_copyright = True - -# If true, an OpenSearch description file will be output, and all pages will -# contain a tag referring to it. The value of this option must be the -# base URL from which the finished HTML is served. -#html_use_opensearch = '' - -# This is the file name suffix for HTML files (e.g. ".xhtml"). -#html_file_suffix = None - -# Language to be used for generating the HTML full-text search index. -# Sphinx supports the following languages: -# 'da', 'de', 'en', 'es', 'fi', 'fr', 'hu', 'it', 'ja' -# 'nl', 'no', 'pt', 'ro', 'ru', 'sv', 'tr' -#html_search_language = 'en' - -# A dictionary with options for the search language support, empty by default. -# Now only 'ja' uses this config value -#html_search_options = {'type': 'default'} - -# The name of a javascript file (relative to the configuration directory) that -# implements a search results scorer. If empty, the default will be used. -#html_search_scorer = 'scorer.js' - -# Output file base name for HTML help builder. -htmlhelp_basename = 'UIPatternsdoc' - -# -- Options for LaTeX output --------------------------------------------- - -latex_elements = { -# The paper size ('letterpaper' or 'a4paper'). -#'papersize': 'letterpaper', - -# The font size ('10pt', '11pt' or '12pt'). -#'pointsize': '10pt', - -# Additional stuff for the LaTeX preamble. -#'preamble': '', - -# Latex figure (float) alignment -#'figure_align': 'htbp', -} - -# Grouping the document tree into LaTeX files. List of tuples -# (source start file, target name, title, -# author, documentclass [howto, manual, or own class]). -latex_documents = [ - (master_doc, 'UIPatterns.tex', u'UI Patterns Documentation', - u'Nuvole Web', 'manual'), -] - -# The name of an image file (relative to this directory) to place at the top of -# the title page. -#latex_logo = None - -# For "manual" documents, if this is true, then toplevel headings are parts, -# not chapters. -#latex_use_parts = False - -# If true, show page references after internal links. -#latex_show_pagerefs = False - -# If true, show URL addresses after external links. -#latex_show_urls = False - -# Documents to append as an appendix to all manuals. -#latex_appendices = [] - -# If false, no module index is generated. -#latex_domain_indices = True - - -# -- Options for manual page output --------------------------------------- - -# One entry per manual page. List of tuples -# (source start file, name, description, authors, manual section). -man_pages = [ - (master_doc, 'uipatterns', u'UI Patterns Documentation', - [author], 1) -] - -# If true, show URL addresses after external links. -#man_show_urls = False - - -# -- Options for Texinfo output ------------------------------------------- - -# Grouping the document tree into Texinfo files. List of tuples -# (source start file, target name, title, author, -# dir menu entry, description, category) -texinfo_documents = [ - (master_doc, 'UIPatterns', u'UI Patterns Documentation', - author, 'UIPatterns', 'One line description of project.', - 'Miscellaneous'), -] - -# Documents to append as an appendix to all manuals. -#texinfo_appendices = [] - -# If false, no module index is generated. -#texinfo_domain_indices = True - -# How to display URL addresses: 'footnote', 'no', or 'inline'. -#texinfo_show_urls = 'footnote' - -# If true, do not generate a @detailmenu in the "Top" node's menu. -#texinfo_no_detailmenu = False diff --git a/docs/content/developer-documentation.rst b/docs/content/developer-documentation.rst deleted file mode 100644 index 23a01430..00000000 --- a/docs/content/developer-documentation.rst +++ /dev/null @@ -1,322 +0,0 @@ -Developer documentation -======================= - -Render patterns programmatically --------------------------------- - -Patterns can be rendered programmatically by using the following syntax: - -.. code-block:: php - - 'pattern', - '#id' => 'blockquote', - '#fields' => [ - 'quote' => 'You must do the things you think you cannot do.', - 'attribution' => 'Eleanor Roosevelt' - ] - ]; - - \Drupal::service('renderer')->render($elements); - -The code above will produce the following result: - -.. image:: ../images/developer-1.png - :align: center - :width: 650 - -To render a specific pattern variant use: - -.. code-block:: php - - 'pattern', - '#id' => 'blockquote', - '#variant' => 'highlighted', - '#fields' => [ - 'quote' => 'You must do the things you think you cannot do.', - 'attribution' => 'Eleanor Roosevelt' - ] - ]; - -It is also possible to just render a pattern preview as displayed on the patterns overview page in the following way -(since fields are already bundled within the pattern definition we don't need to re-declare them here): - -.. code-block:: php - - 'pattern_preview', - '#id' => 'blockquote', - ]; - - \Drupal::service('renderer')->render($elements); - - -Rendering the code above will produce the following output: - -.. image:: ../images/developer-2.png - :align: center - :width: 650 - -To render a specific pattern preview variant use: - -.. code-block:: php - - 'pattern_preview', - '#id' => 'blockquote', - '#variant' => 'highlighted', - ]; - - -Render patterns using Twig functions ------------------------------------- - -The UI Patterns module also exposes two Twig functions to easily render patterns into your Twig templates. - -The following two calls: - -.. code-block:: twig - - {{ pattern('button', {title: 'Link title', url: 'http://example.com'}) }} - {{ pattern_preview('modal') }} - -Will print: - -.. image:: ../images/developer-3.png - :align: center - :width: 650 - -To render pattern variants use: - -.. code-block:: twig - - {{ pattern('button', {title: 'Link title', url: 'http://example.com'}, 'highlighted') }} - {{ pattern_preview('modal', 'highlighted') }} - -Since patterns are rendered using the render element described above all libraries and preprocess hooks will be ran when -using Twig functions. - -Working with pattern suggestions --------------------------------- - -Modules that want to add theme hook suggestions to patterns can do that by implementing the following hook: - -.. code-block:: php - - isOfType('views_row')) { - $hook = $variables['theme_hook_original']; - $view_name = $context->getProperty('view_name'); - $display = $context->getProperty('display'); - - $suggestions[] = $hook . '__views_row__' . $view_name; - $suggestions[] = $hook . '__views_row__' . $view_name . '__' . $display; - } - } - -The hook above is a ``hook_theme_suggestions_alter()`` specifically designed for patterns. The hook is invoked -with a ``PatternContext`` object that describes information on where the current pattern is being used. - -Pattern suggestions can, for example, allow developers to use alternative pattern templates in specific contexts or to -"massage" data before it sent to the pattern by implementing fine-grained preprocess hooks. - -The following suggestions are automatically exposed by the project's sub-modules: - -.. code-block:: php - - getContextProperty('view'); - foreach ($view->display_handler->getFieldLabels() as $name => $label) { - $sources[] = $this->getSourceField($name, $label); - } - return $sources; - } - - } - -At the moment the available source plugin tags are the following: - -- ``entity_display``: provided by the ``ui_patterns`` module and triggered on an entity display configuration page. -- ``ds_field_template``: provided by the ``ui_patterns_ds`` module and triggered when setting up a field template - on an entity display configuration page. -- ``views_row``: provided by the ``ui_patterns_views`` module and triggered on a Views row setting pane. -- ``test``: provided by the ``ui_patterns_test`` module and used in tests. - - -Alter pattern configuration forms ---------------------------------- - -You can alter UI Patterns configuration forms by implementing ``hook_ui_patterns_display_settings_form_alter()``. - -For example, the following implementation adds a CSS class input field to the pattern configuration: - -.. code-block:: php - - 'input', - '#title' => t('Class name'), - ]; - } - -This hook alter forms that are built using the ``PatternDisplayFormTrait`` trait, meaning: - -- Display Suite field templates -- Field groups -- Views - -If you want to alter an entity layout form that uses UI Patters for its layout use -``hook_ui_patterns_layouts_display_settings_form_alter()`` instead, for example: - -.. code-block:: php - - 'input', - '#title' => t('Class name'), - ]; - } diff --git a/docs/content/field-group.rst b/docs/content/field-group.rst deleted file mode 100644 index 090fd8e8..00000000 --- a/docs/content/field-group.rst +++ /dev/null @@ -1,28 +0,0 @@ -Use patterns with Field Groups -============================== - -Patterns can be used to style entities' `field groups `_ thanks to the -``ui_patterns_field_group`` module. - -For example, say we want to show some metadata associated with an article, such as author, post date and tags. - -After enabling the module we create a new field group of type **Pattern** and drag all fields you want to use in that -group, as shown below: - -.. image:: ../images/fieldgroup-1.png - :align: center - :width: 450 - -Once all fields are in place we access the field group settings and choose the **Metadata** pattern. At this point we -map the fields to the pattern destination fields and save our settings: - -.. image:: ../images/fieldgroup-2.png - :align: center - :width: 450 - -Articles will now always use the **Metadata** pattern to style that field group, as shown below: - -.. image:: ../images/fieldgroup-3.png - :align: center - :width: 550 - diff --git a/docs/content/field-templates.rst b/docs/content/field-templates.rst deleted file mode 100644 index 755925bb..00000000 --- a/docs/content/field-templates.rst +++ /dev/null @@ -1,47 +0,0 @@ -Use patterns with Field templates -================================= - -Patterns can be used as Display Suite field templates by enabling the ``ui_patterns_ds`` module. This opens the -following interesting possibilities: - -- Link fields can be styled as buttons by mapping their URL and link titles to specific pattern destinations. -- Image fields can be styled as an "image with caption" by mapping a formatted image and title to specific pattern - destinations. - -Let's see how to implement the first example having the following pattern definition: - -.. code-block:: yaml - - button: - label: Button - description: A simple button. - fields: - title: - type: text - label: Label - description: The button label - preview: Submit - url: - type: text - label: URL - description: The button URL - preview: http://example.com - -On the entity display setting page we access the link field setting by clicking on the gear icon: - -.. image:: ../images/field-template-1.png - :align: center - :width: 700 - -Then, after selecting the **Pattern** field template and the **Button** pattern, we map the link field columns to the -pattern's fields defined above: - -.. image:: ../images/field-template-2.png - :align: center - :width: 700 - -Our multi-valued link field will then be formatted as follow: - -.. image:: ../images/field-template-3.png - :align: center - :width: 300 diff --git a/docs/content/layout-plugin.rst b/docs/content/layout-plugin.rst deleted file mode 100644 index 188bfad3..00000000 --- a/docs/content/layout-plugin.rst +++ /dev/null @@ -1,56 +0,0 @@ -Use patterns as layouts -======================= - -Patterns can be used as layouts thanks to the ``ui_patterns_layouts`` module. - -Once exposed as layouts patterns can be used to arrange fields on entities like nodes, -`paragraphs `_, etc. or to place blocks on a page using -`Panels `_. - -In the example below we will style a **Jumbotron** paragraph using the Jumbotron paragraph. - -Once on the paragraph **Manage display** page we choose the **Jumbotron** pattern as layout: - -.. image:: ../images/layouts-1.png - :align: center - :width: 450 - -After doing that the pattern fields will be exposed as layout regions, so given the following definition: - -.. code-block:: yaml - - jumbotron: - label: Jumbotron - description: A lightweight, flexible component that can optionally extend the entire viewport to showcase key content on your site. - fields: - title: - type: text - label: Title - description: Jumbotron title. - preview: Hello, world! - subtitle: - type: text - label: Description - description: Jumbotron description. - preview: This is a simple hero unit, a simple jumbotron-style component for calling extra attention to featured content or information. - -We will get the following layout regions: - -.. image:: ../images/layouts-2.png - :align: center - :width: 450 - -We can now arrange the paragraph fields on the layout and save our settings. - -The paragraph below: - -.. image:: ../images/layouts-3.png - :align: center - :width: 450 - -will be now styled using the **Jumbotron** pattern as follows: - -.. image:: ../images/layouts-4.png - :align: center - :width: 550 - diff --git a/docs/content/patterns-definition.rst b/docs/content/patterns-definition.rst deleted file mode 100644 index 93562195..00000000 --- a/docs/content/patterns-definition.rst +++ /dev/null @@ -1,203 +0,0 @@ -Define your patterns -==================== - -Patterns can be exposed by both modules and themes by enabling the ``ui_patterns_library`` module. - -Defined patterns will be available at ``/patterns``, only accessible by roles having the ``access patterns page`` -permission. Below an example of a pattern library page styled using the `Bootstrap Patterns `_ -theme: - -.. image:: ../images/pattern-library.png - :align: center - -Pattern definitions -------------------- - -To define your patterns simply create a YAML file named ``MY_MODULE.ui_patterns.yml`` or ``MY_THEME.ui_patterns.yml`` -and list them using the following format: - -.. code-block:: yaml - - blockquote: - label: Blockquote - description: Display a quote with attribution information. - variants: - default: - label: Default - description: An ordinary quote. - highlighted: - label: Highlighted - description: A special quote. - fields: - quote: - type: text - label: Quote - description: Quote text. - preview: Life is like riding a bicycle. To keep your balance, you must keep moving. - attribution: - type: text - label: Attribution - description: Quote attribution. - preview: Albert Einstein - libraries: - - MY_MODULE/module_library_one - - MY_MODULE/module_library_two - - pattern_library_one: - css: - component: - css/my_component.css: {} - http://example.com/external.min.css: { type: external, minified: true } - - pattern_library_two: - js: - js/library_two.js: {} - dependencies: - - core/jquery - -Let's break this down: - -``id`` - The root of a new pattern definition (``blockquote`` in the example above). It must contain only lowercase - characters, numbers, underscores and hyphens (i.e. it should validate against ``[^a-z0-9_-]+``). -``label`` - Pattern label, used on pattern library page. -``description`` - Pattern description (optional), used on pattern library page. -``fields`` - Hash defining the pattern fields (optional). Each field can have the following properties: - - ``type`` - Field type, can be ``text``, ``numeric``, etc. at the moment only used for documentation purposes. Optional. - ``label`` - Field label, used on pattern library page. - ``description`` - Field description, used on pattern library page. Optional. - ``preview`` - Preview content, used on pattern library page. It can be either a string or a Drupal render array, in which case - we can use keys like ``type: processed_text`` or ``theme: image``. Optional. - -``variants`` - Hash defining the pattern variants (optional). Each variant can have the following properties: - - ``label`` - Variant label, used on pattern library page. - ``description`` - Variant description, used on pattern library page. Optional. - -``libraries`` - List of libraries to be loaded when rendering the pattern (optional). UI patterns are supposed to be self-contained so they - should define along all needed libraries. - -Once the pattern is defined the module will expose them as standard Drupal theme definitions. - -For example, given the ``my_pattern`` pattern ID then a theme function ``pattern_my_pattern`` is created and, -consequently, the module will look for a template file called ``pattern-my-pattern.html.twig``. - -The Twig template can be placed it either under ``MY_MODULE/templates``, if the pattern is exposed by a module, -or under ``MY_THEME/templates``, if it is exposed by a theme. As expected themes will override templates exposed by modules. - -For example, consider the following Twig template file ``pattern-blockquote.html.twig`` for the ``blockquote`` pattern -defined above: - -.. code-block:: twig - -
-

{{ quote }}

-
{{ attribution }}
-
- - -The pattern will be rendered as shown below (styled using the `Bootstrap `_ theme): - -.. image:: ../images/blockquote-preview.png - :align: center - -**Remember**: we can always visit the ``/pattern`` page in order to have access to a full preview of all our patterns. - -Organize your patterns in sub-folders -------------------------------------- - -Patterns can be defined using a single ``NAME.ui_patterns.yml`` file. However, in case of sites with a large number of -patterns, this might quickly becomes difficult to manage. - -If that's the case pattern definitions can also be organised in sub-folders, as shown below: - -.. code-block:: bash - - . - ├── templates - │   └── patterns - │   ├── button - │   │   ├── button.ui_patterns.yml - │   │   └── pattern-button.html.twig - │   ├── media - │   │   ├── media.ui_patterns.yml - │   │   └── pattern-media.html.twig - ... - │   └── pattern-jumbotron.html.twig - ├── ui_patterns_test_theme.info.yml - └── ui_patterns_test_theme.ui_patterns.yml - -**Note:** the example above is taken by the actual test target site that is used to test the module itself: have a look -at ``./tests/README.md`` and at ``./tests/target/custom`` for working examples on how to use the UI Patterns module. - -Expose pattern assets as libraries ----------------------------------- - -In case you wish to bundle your assets within the pattern directory you can define libraries with the alternative syntax -below: - -.. code-block:: yaml - - blockquote: - label: Blockquote - ... - libraries: - ... - - pattern_library_one: - css: - component: - css/my_component.css: {} - http://example.com/external.min.css: { type: external, minified: true } - - pattern_library_two: - js: - js/library_two.js: {} - -Libraries defined as above will be automatically loaded when the pattern is rendered. They are also exposed as ordinary -Drupal libraries as follows: ``ui_patterns/PATTERN_ID.LIBRARY_NAME`` - -For example, the two local libraries above can be attached to your render arrays in the following way: - -.. code-block:: php - - `_, so the following notations - are valid examples: - -.. code-block:: yaml - - use: "@my_module/templates/my-template.html.twig" - -.. code-block:: yaml - - use: "@molecules/media/media-block.twig" - -The possibility of using stand-alone Twig templates allows for a swift integration with tools like -`PatternLab `_ or modules like `Component Libraries `_. - -**Attention:** always remember to double-quote ``use:`` values or some YAML parsers (including PatternLab's) will -complain. diff --git a/docs/content/tests.rst b/docs/content/tests.rst deleted file mode 100644 index 2d5a7352..00000000 --- a/docs/content/tests.rst +++ /dev/null @@ -1,55 +0,0 @@ -Working with tests -================== - -UI Patterns is tested using `PHPUnit in Drupal 8 `_. - -To build the test site just run: - -.. code-block:: bash - - $ composer install - -After that you'll find a fully functional Drupal 8 site under ``./build``, thanks to the integration with the -`OpenEuropa Task Runner `_. - -Tu run tests use: - -.. code-block:: bash - - $ ./vendor/bin/phpunit - -Working with coding standards -============================= - -UI Patterns coding standards checks are ran using `GrumPHP `_. - -.. code-block:: bash - - $ ./vendor/bin/grumphp run - -Docker Compose -============== - -UI Patterns ships with a ``docker-compose.yml`` file which can be used to streamline local development and tests execution. - -Setup Docker Compose by copying ``docker-compose.yml`` to ``docker-compose.override.yml`` and replace ``${TRAVIS_PHP_VERSION}`` -with the desired PHP version (either "5.6" or "7.1"). - -After that run: - -.. code-block:: bash - - $ docker-compose up -d - $ docker-compose exec -u www-data php composer install - $ docker-compose exec -u www-data php ./vendor/bin/run drupal:site-setup - $ docker-compose exec -u www-data php ./vendor/bin/run drupal:site-install - $ docker-compose exec -u www-data php chown -R www-data:www-data build - -You'll then have a fully functional test site at `http://localhost:8080 `_. - -To run all tests use: - -.. code-block:: bash - - $ docker-compose exec -u www-data php ./vendor/bin/grumphp run - $ docker-compose exec -u www-data php ./vendor/bin/phpunit diff --git a/docs/content/views.rst b/docs/content/views.rst deleted file mode 100644 index a0ac1db2..00000000 --- a/docs/content/views.rst +++ /dev/null @@ -1,18 +0,0 @@ -Use patterns with Views ------------------------ - -Patterns can be used as Views row templates thanks to the ``ui_patterns_views`` module, which exposes a **Patterns** row -style plugin. - -.. image:: ../images/views-1.png - :align: center - :width: 550 - -After choosing the **Pattern** row style plugin we can map the current Views display fields to the chosen pattern -destinations, as shown below: - -.. image:: ../images/views-2.png - :align: center - :width: 550 - -Views rows will now be styled using the selected pattern. diff --git a/docs/images/blockquote-preview.png b/docs/images/blockquote-preview.png deleted file mode 100644 index 0312bfab..00000000 Binary files a/docs/images/blockquote-preview.png and /dev/null differ diff --git a/docs/images/developer-1.png b/docs/images/developer-1.png deleted file mode 100644 index 35398edb..00000000 Binary files a/docs/images/developer-1.png and /dev/null differ diff --git a/docs/images/developer-2.png b/docs/images/developer-2.png deleted file mode 100644 index f59da9fa..00000000 Binary files a/docs/images/developer-2.png and /dev/null differ diff --git a/docs/images/developer-3.png b/docs/images/developer-3.png deleted file mode 100644 index 36caa2ea..00000000 Binary files a/docs/images/developer-3.png and /dev/null differ diff --git a/docs/images/field-template-1.png b/docs/images/field-template-1.png deleted file mode 100644 index 13285d96..00000000 Binary files a/docs/images/field-template-1.png and /dev/null differ diff --git a/docs/images/field-template-2.png b/docs/images/field-template-2.png deleted file mode 100644 index 9e6a7e23..00000000 Binary files a/docs/images/field-template-2.png and /dev/null differ diff --git a/docs/images/field-template-3.png b/docs/images/field-template-3.png deleted file mode 100644 index 05dfda9f..00000000 Binary files a/docs/images/field-template-3.png and /dev/null differ diff --git a/docs/images/fieldgroup-1.png b/docs/images/fieldgroup-1.png deleted file mode 100644 index 386bf0ce..00000000 Binary files a/docs/images/fieldgroup-1.png and /dev/null differ diff --git a/docs/images/fieldgroup-2.png b/docs/images/fieldgroup-2.png deleted file mode 100644 index 80a68966..00000000 Binary files a/docs/images/fieldgroup-2.png and /dev/null differ diff --git a/docs/images/fieldgroup-3.png b/docs/images/fieldgroup-3.png deleted file mode 100644 index a5637ddb..00000000 Binary files a/docs/images/fieldgroup-3.png and /dev/null differ diff --git a/docs/images/layouts-1.png b/docs/images/layouts-1.png deleted file mode 100644 index 096d2713..00000000 Binary files a/docs/images/layouts-1.png and /dev/null differ diff --git a/docs/images/layouts-2.png b/docs/images/layouts-2.png deleted file mode 100644 index 180d0b8e..00000000 Binary files a/docs/images/layouts-2.png and /dev/null differ diff --git a/docs/images/layouts-3.png b/docs/images/layouts-3.png deleted file mode 100644 index d9d0ec77..00000000 Binary files a/docs/images/layouts-3.png and /dev/null differ diff --git a/docs/images/layouts-4.png b/docs/images/layouts-4.png deleted file mode 100644 index be2d5a0d..00000000 Binary files a/docs/images/layouts-4.png and /dev/null differ diff --git a/docs/images/pattern-library.png b/docs/images/pattern-library.png deleted file mode 100644 index 994dd76b..00000000 Binary files a/docs/images/pattern-library.png and /dev/null differ diff --git a/docs/images/patterns-overview.png b/docs/images/patterns-overview.png deleted file mode 100644 index bbe945f9..00000000 Binary files a/docs/images/patterns-overview.png and /dev/null differ diff --git a/docs/images/views-1.png b/docs/images/views-1.png deleted file mode 100644 index 5132253a..00000000 Binary files a/docs/images/views-1.png and /dev/null differ diff --git a/docs/images/views-2.png b/docs/images/views-2.png deleted file mode 100644 index d5cbf0d0..00000000 Binary files a/docs/images/views-2.png and /dev/null differ diff --git a/docs/index.rst b/docs/index.rst deleted file mode 100644 index 90ff1669..00000000 --- a/docs/index.rst +++ /dev/null @@ -1,49 +0,0 @@ -Welcome to UI Patterns' documentation -===================================== - -The UI Patterns module allows developers to define self-contained UI patterns as Drupal plugins and use them seamlessly -in their `panels `_, `field groups `_ -or `Display Suite `_ view modes. - -.. image:: images/patterns-overview.png - :align: center - -Project overview ----------------- - -The UI Patterns project provides 6 modules: - -* **UI Patterns**: the main module, it exposes the UI Patterns system APIs and it does not do much more than that. -* **UI Patterns Library**: allows to define patterns via YAML and generates a pattern library page available at ``/patterns`` - to be used as documentation for content editors or as a showcase for business. Use this module if you don't plan to - use more advanced component library systems such as PatternLab or Fractal. - `Learn more `_ -* **UI Patterns Field Group**: allows to use patterns to format field groups - provided by the `Field group `_ module. -* **UI Patterns Layouts**: allows to use patterns as layouts. This allows patterns to be used on - `Display Suite `_ view modes or on `panels `_ - out of the box. -* **UI Patterns Display Suite**: allows to use patterns to format `Display Suite `_ - field templates. -* **UI Patterns Views**: allows to use patterns as Views row templates. - -By the way plugin definitions are handled the UI Patterns module also integrates with with tools like -`PatternLab `_ or modules like `Component Libraries `_. - -Try it out -^^^^^^^^^^ - -Download and install the `Bootstrap Patterns `_ theme on a vanilla -Drupal 8 installation to quickly try out the UI Patterns module. - -.. toctree:: - :caption: Table of Contents - - content/patterns-definition - content/field-group - content/layout-plugin - content/field-templates - content/views - content/developer-documentation - content/tests - diff --git a/grumphp.drupal8.yml b/grumphp.drupal8.yml deleted file mode 100644 index 7d9c0de2..00000000 --- a/grumphp.drupal8.yml +++ /dev/null @@ -1,16 +0,0 @@ -parameters: - ascii: - failed: ~ - succeeded: ~ - tasks: - phpcs: - standard: vendor/drupal/coder/coder_sniffer/Drupal/ - ignore_patterns: - - build/ - - vendor/ - triggered_by: - - php - - module - - install - - inc - - theme diff --git a/grumphp.yml b/grumphp.yml deleted file mode 100644 index 79311e22..00000000 --- a/grumphp.yml +++ /dev/null @@ -1,16 +0,0 @@ -grumphp: - ascii: - failed: ~ - succeeded: ~ - tasks: - phpcs: - standard: vendor/drupal/coder/coder_sniffer/Drupal/ - ignore_patterns: - - build/ - - vendor/ - triggered_by: - - php - - module - - install - - inc - - theme diff --git a/modules/ui_patterns_ds/src/FieldTemplateProcessor.php b/modules/ui_patterns_ds/src/FieldTemplateProcessor.php deleted file mode 100644 index cf54f7cd..00000000 --- a/modules/ui_patterns_ds/src/FieldTemplateProcessor.php +++ /dev/null @@ -1,170 +0,0 @@ -variables = $variables; - - $content = []; - foreach ($variables['items'] as $delta => $item) { - $fields = []; - foreach ($this->getMapping() as $mapping) { - $fields[$mapping['destination']][] = $this->getSourceValue($mapping, $delta); - } - - $content['pattern_' . $delta] = [ - '#type' => 'pattern', - '#id' => $this->getPatternId(), - '#variant' => $this->getVariant(), - '#fields' => $fields, - '#context' => $this->getContext(), - '#multiple_sources' => TRUE, - ]; - } - - $variables['pattern'] = $content; - } - - /** - * Get source value. - * - * @param array $mapping - * Mapping array. - * @param int $delta - * Field delta. - * - * @return mixed - * Source value. - */ - public function getSourceValue(array $mapping, $delta) { - $value = $this->variables['items'][$delta]['content']; - if ($mapping['source'] != $this->getFieldName()) { - $column = $this->getColumnName($mapping['source']); - $value = $this->getEntity()->get($this->getFieldName())->getValue(); - $value = $value[$delta][$column]; - } - return $value; - } - - /** - * Get field parent entity. - * - * @return \Drupal\Core\Entity\ContentEntityBase - * Parent entity. - */ - protected function getEntity() { - return $this->variables['element']['#object']; - } - - /** - * Get Pattern ID. - * - * @return string - * Pattern ID. - */ - protected function getPatternId() { - return $this->getSetting('pattern'); - } - - /** - * Get mapping settings. - * - * @return array - * Mapping settings. - */ - protected function getMapping() { - return $this->getSetting('pattern_mapping', []); - } - - /** - * Get mapping settings. - * - * @return string - * Mapping settings. - */ - protected function getVariant() { - return $this->getSetting('pattern_variant'); - } - - /** - * Get setting value or default to given value if none set. - * - * @param string $name - * Setting name. - * @param string $default - * Setting default value. - * - * @return mixed - * Setting value. - */ - protected function getSetting($name, $default = '') { - return $this->variables['ds-config']['settings'][$name] ?? $default; - } - - /** - * Get field name. - * - * @return string - * Field name. - */ - protected function getFieldName() { - return $this->variables['field_name']; - } - - /** - * Extract column name from a source name. - * - * @param string $source - * Source name. - * - * @return string - * Column name. - */ - protected function getColumnName($source) { - return str_replace($this->getFieldName() . '__', '', $source); - } - - /** - * Get pattern context. - * - * @return array - * Pattern context. - */ - protected function getContext() { - $element = $this->variables['element']; - $context = [ - 'type' => 'ds_field_template', - 'field_name' => $this->getFieldName(), - 'entity_type' => $element['#entity_type'], - 'bundle' => $element['#bundle'], - 'view_mode' => $element['#view_mode'], - 'entity' => NULL, - ]; - - if (isset($element['#object']) && is_object($element['#object']) && $element['#object'] instanceof ContentEntityBase) { - $context['entity'] = $element['#object']; - } - - return $context; - } - -} diff --git a/modules/ui_patterns_ds/src/FieldTemplateProcessorInterface.php b/modules/ui_patterns_ds/src/FieldTemplateProcessorInterface.php deleted file mode 100644 index e5f7f552..00000000 --- a/modules/ui_patterns_ds/src/FieldTemplateProcessorInterface.php +++ /dev/null @@ -1,22 +0,0 @@ -patternsManager = $patterns_manager; - $this->sourceManager = $source_manager; - $this->parameters = $parameters->getCurrentRequest()->request; - $this->fieldManager = $field_manager; - $this->moduleHandler = $module_handler; - } - - /** - * {@inheritdoc} - */ - public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) { - return new static( - $configuration, - $plugin_id, - $plugin_definition, - $container->get('plugin.manager.ui_patterns'), - $container->get('plugin.manager.ui_patterns_source'), - $container->get('request_stack'), - $container->get('entity_field.manager'), - $container->get('module_handler') - ); - } - - /** - * {@inheritdoc} - */ - public function alterForm(&$form) { - $context = $this->getContext(); - if ($this->isSupportedField($context)) { - $this->buildPatternDisplayForm($form, 'ds_field_template', $context, $this->getConfiguration()); - } - else { - $form['#markup'] = $this->t("The current field is not supported."); - } - } - - /** - * Get source field plugin context. - * - * @return array - * Context array. - */ - protected function getContext() { - $fields = $this->parameters->get('fields'); - $field_name = $this->getCurrentField(); - - return [ - 'field_name' => $field_name, - 'field_settings' => $fields[$field_name], - 'entity_type' => $this->parameters->get('ds_entity_type'), - 'bundle' => $this->parameters->get('ds_bundle'), - 'view_mode' => $this->parameters->get('ds_view_mode'), - ]; - } - - /** - * {@inheritdoc} - */ - public function defaultConfiguration() { - return [ - 'pattern' => '', - 'pattern_variant' => '', - 'pattern_mapping' => [], - ]; - } - - /** - * Get name of field currently being edited. - * - * @return string - * Name of field currently being edited. - */ - protected function getCurrentField() { - $fields = array_filter($this->parameters->get('fields', []), function ($field) { - return isset($field['settings_edit_form']['third_party_settings']['ds']['ft']['id']) && $field['settings_edit_form']['third_party_settings']['ds']['ft']['id'] == 'pattern'; - }); - $fields = array_keys($fields); - $field = reset($fields); - - if (empty($field)) { - $trigger_element = $this->parameters->get('_triggering_element_name'); - $field = str_replace('_plugin_settings_edit', '', $trigger_element); - } - - return $field; - } - - /** - * Pattern Display Suite field template plugin only supports actual fields. - * - * @param array $context - * Current context. - * - * @return bool - * TRUE if supported, FALSE otherwise. - */ - protected function isSupportedField(array $context) { - /** @var \Drupal\field\Entity\FieldConfig $field */ - if ($context['entity_type'] && $context['bundle']) { - $field = $this->fieldManager->getFieldDefinitions($context['entity_type'], $context['bundle']); - return isset($field[$context['field_name']]); - } - return FALSE; - } - -} diff --git a/modules/ui_patterns_ds/src/Plugin/UiPatterns/Source/DsFieldSource.php b/modules/ui_patterns_ds/src/Plugin/UiPatterns/Source/DsFieldSource.php deleted file mode 100644 index 54ebd5f2..00000000 --- a/modules/ui_patterns_ds/src/Plugin/UiPatterns/Source/DsFieldSource.php +++ /dev/null @@ -1,69 +0,0 @@ -dsManager = $ds_manager; - } - - /** - * {@inheritdoc} - */ - public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) { - return new static( - $configuration, - $plugin_id, - $plugin_definition, - $container->get('plugin.manager.ds') - ); - } - - /** - * {@inheritdoc} - */ - public function getSourceFields() { - $sources = []; - $fields = $this->dsManager->getDefinitions(); - - foreach ($fields as $field) { - if (!$this->getContextProperty('limit')) { - $sources[] = $this->getSourceField($field['id'], $field['title']); - } - elseif (in_array($field['id'], $this->getContextProperty('limit'))) { - $sources[] = $this->getSourceField($field['id'], $field['title']); - } - } - return $sources; - } - -} diff --git a/modules/ui_patterns_ds/src/Plugin/UiPatterns/Source/DsFieldTemplateSource.php b/modules/ui_patterns_ds/src/Plugin/UiPatterns/Source/DsFieldTemplateSource.php deleted file mode 100644 index f391db1a..00000000 --- a/modules/ui_patterns_ds/src/Plugin/UiPatterns/Source/DsFieldTemplateSource.php +++ /dev/null @@ -1,71 +0,0 @@ -fieldManager = $field_manager; - } - - /** - * {@inheritdoc} - */ - public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) { - return new static( - $configuration, - $plugin_id, - $plugin_definition, - $container->get('entity_field.manager') - ); - } - - /** - * {@inheritdoc} - */ - public function getSourceFields() { - $sources = []; - $field_name = $this->getContextProperty('field_name'); - $entity_type = $this->getContextProperty('entity_type'); - $bundle = $this->getContextProperty('bundle'); - - /** @var \Drupal\field\Entity\FieldConfig $field */ - $field = $this->fieldManager->getFieldDefinitions($entity_type, $bundle)[$field_name]; - $label = $field->getLabel(); - - $sources[] = $this->getSourceField($field_name, $label); - foreach ($field->getFieldStorageDefinition()->getColumns() as $column_name => $column) { - $sources[] = $this->getSourceField($field_name . '__' . $column_name, $label . ': ' . $column_name); - } - return $sources; - } - -} diff --git a/modules/ui_patterns_ds/templates/pattern-ds-field-template.html.twig b/modules/ui_patterns_ds/templates/pattern-ds-field-template.html.twig deleted file mode 100644 index c46be2fd..00000000 --- a/modules/ui_patterns_ds/templates/pattern-ds-field-template.html.twig +++ /dev/null @@ -1,9 +0,0 @@ -{# -/** - * @file - * Default UI Patterns for Display Suite field templates. - * - * @ingroup themeable - */ -#} -{{ pattern }} diff --git a/modules/ui_patterns_ds/tests/modules/ui_patterns_ds_test/config/install/core.entity_view_display.node.article.default.yml b/modules/ui_patterns_ds/tests/modules/ui_patterns_ds_test/config/install/core.entity_view_display.node.article.default.yml deleted file mode 100644 index 3188b7ba..00000000 --- a/modules/ui_patterns_ds/tests/modules/ui_patterns_ds_test/config/install/core.entity_view_display.node.article.default.yml +++ /dev/null @@ -1,60 +0,0 @@ -langcode: en -status: true -dependencies: - config: - - field.field.node.article.body - - node.type.article - module: - - ds - - text - - user -third_party_settings: - ds: - layout: - id: ds_1col - library: null - disable_css: false - entity_classes: all_classes - settings: - wrappers: - ds_content: div - outer_wrapper: div - attributes: '' - link_attribute: '' - link_custom: '' - classes: - layout_class: { } - regions: - ds_content: - - links - - body -id: node.article.default -targetEntityType: node -bundle: article -mode: default -content: - body: - label: above - type: text_default - weight: 101 - settings: { } - third_party_settings: - ds: - ft: - id: pattern - settings: - pattern: field - pattern_variant: default - pattern_mapping: - 'ds_field_template:body': - destination: value - weight: 0 - plugin: ds_field_template - source: body - region: ds_content - links: - weight: 100 - region: ds_content - settings: { } - third_party_settings: { } -hidden: { } diff --git a/modules/ui_patterns_ds/tests/modules/ui_patterns_ds_test/config/install/field.field.node.article.body.yml b/modules/ui_patterns_ds/tests/modules/ui_patterns_ds_test/config/install/field.field.node.article.body.yml deleted file mode 100644 index 8f3681d9..00000000 --- a/modules/ui_patterns_ds/tests/modules/ui_patterns_ds_test/config/install/field.field.node.article.body.yml +++ /dev/null @@ -1,21 +0,0 @@ -langcode: en -status: true -dependencies: - config: - - field.storage.node.body - - node.type.article - module: - - text -id: node.article.body -field_name: body -entity_type: node -bundle: article -label: Body -description: '' -required: false -translatable: true -default_value: { } -default_value_callback: '' -settings: - display_summary: true -field_type: text_with_summary diff --git a/modules/ui_patterns_ds/tests/modules/ui_patterns_ds_test/config/install/node.type.article.yml b/modules/ui_patterns_ds/tests/modules/ui_patterns_ds_test/config/install/node.type.article.yml deleted file mode 100644 index f47f2add..00000000 --- a/modules/ui_patterns_ds/tests/modules/ui_patterns_ds_test/config/install/node.type.article.yml +++ /dev/null @@ -1,10 +0,0 @@ -langcode: en -status: true -dependencies: { } -name: Article -type: article -description: '' -help: '' -new_revision: true -preview_mode: 1 -display_submitted: true diff --git a/modules/ui_patterns_ds/tests/modules/ui_patterns_ds_test/templates/field.ui_patterns.yml b/modules/ui_patterns_ds/tests/modules/ui_patterns_ds_test/templates/field.ui_patterns.yml deleted file mode 100644 index af8aeeb4..00000000 --- a/modules/ui_patterns_ds/tests/modules/ui_patterns_ds_test/templates/field.ui_patterns.yml +++ /dev/null @@ -1,16 +0,0 @@ -field: - label: "Field" - variants: - default: - label: "Default" - overridden: - label: "Overridden" - fields: - value: - type: "text" - label: "Value" - preview: "value" - format: - type: "text" - label: "Format" - preview: "format" diff --git a/modules/ui_patterns_ds/tests/modules/ui_patterns_ds_test/templates/pattern-field.html.twig b/modules/ui_patterns_ds/tests/modules/ui_patterns_ds_test/templates/pattern-field.html.twig deleted file mode 100644 index 17aa29ea..00000000 --- a/modules/ui_patterns_ds/tests/modules/ui_patterns_ds_test/templates/pattern-field.html.twig +++ /dev/null @@ -1,8 +0,0 @@ -{# -/** - * @file - * Test field pattern. - */ -#} -

{{ "Value:"|t }} {{ value }}

-

{{ "Format:"|t }} {{ format }}

diff --git a/modules/ui_patterns_ds/tests/modules/ui_patterns_ds_test/ui_patterns_ds_test.info.yml b/modules/ui_patterns_ds/tests/modules/ui_patterns_ds_test/ui_patterns_ds_test.info.yml deleted file mode 100644 index 2142c359..00000000 --- a/modules/ui_patterns_ds/tests/modules/ui_patterns_ds_test/ui_patterns_ds_test.info.yml +++ /dev/null @@ -1,17 +0,0 @@ -name: 'UI Patterns Display Suite Test' -type: module -description: 'Test module for UI Patterns.' -core: 8.x -core_version_requirement: ^8 || ^9 -hidden: true -package: 'User interface' -dependencies: - - ui_patterns - - ui_patterns_ds - - ui_patterns_library - -config_devel: - install: - - core.entity_view_display.node.article.default - - field.field.node.article.body - - node.type.article diff --git a/modules/ui_patterns_ds/tests/modules/ui_patterns_ds_test/ui_patterns_ds_test.install b/modules/ui_patterns_ds/tests/modules/ui_patterns_ds_test/ui_patterns_ds_test.install deleted file mode 100644 index e1ebeb86..00000000 --- a/modules/ui_patterns_ds/tests/modules/ui_patterns_ds_test/ui_patterns_ds_test.install +++ /dev/null @@ -1,17 +0,0 @@ -getEditable('ds.settings') - ->set('field_template', TRUE) - ->save(); -} diff --git a/modules/ui_patterns_ds/tests/src/FunctionalJavascript/UiPatternsFieldRenderTest.php b/modules/ui_patterns_ds/tests/src/FunctionalJavascript/UiPatternsFieldRenderTest.php deleted file mode 100644 index 72e931ed..00000000 --- a/modules/ui_patterns_ds/tests/src/FunctionalJavascript/UiPatternsFieldRenderTest.php +++ /dev/null @@ -1,93 +0,0 @@ -assertSession(); - - $this->enableTwigDebugMode(); - - $user = $this->drupalCreateUser([], NULL, TRUE); - $this->drupalLogin($user); - - $node = $this->drupalCreateNode([ - 'title' => 'Test article', - 'body' => 'Test body', - 'type' => 'article', - ]); - - $this->drupalGet($node->toUrl()); - - // Assert correct variant suggestions. - $suggestions = [ - 'pattern-field--variant-default--ds-field-template--body--node--article--full.html.twig', - 'pattern-field--variant-default--ds-field-template--body--node--full.html.twig', - 'pattern-field--variant-default--ds-field-template--body--node--article.html.twig', - 'pattern-field--variant-default--ds-field-template--body--node.html.twig', - 'pattern-field--variant-default--ds-field-template--body.html.twig', - 'pattern-field--variant-default--ds-field-template.html.twig', - - 'pattern-field--ds-field-template--body--node--article--full.html.twig', - 'pattern-field--ds-field-template--body--node--full.html.twig', - 'pattern-field--ds-field-template--body--node--article.html.twig', - 'pattern-field--ds-field-template--body--node.html.twig', - 'pattern-field--ds-field-template--body.html.twig', - 'pattern-field--ds-field-template.html.twig', - - 'pattern-field--variant-default.html.twig', - 'pattern-field.html.twig', - ]; - foreach ($suggestions as $suggestion) { - $assert_session->responseContains($suggestion); - } - - // Test content is rendered in the pattern. - $assert_session->pageTextContains('Value: Test body'); - } - -} diff --git a/modules/ui_patterns_ds/tests/src/FunctionalJavascript/UiPatternsFieldSettingsTest.php b/modules/ui_patterns_ds/tests/src/FunctionalJavascript/UiPatternsFieldSettingsTest.php deleted file mode 100644 index cf9c1d1c..00000000 --- a/modules/ui_patterns_ds/tests/src/FunctionalJavascript/UiPatternsFieldSettingsTest.php +++ /dev/null @@ -1,113 +0,0 @@ -getSession(); - $page = $session->getPage(); - $assert_session = $this->assertSession(); - - $user = $this->drupalCreateUser([], NULL, TRUE); - $this->drupalLogin($user); - - // Visit Article's default display settings page. - $this->drupalGet('/admin/structure/types/manage/article/display'); - - // Click on Body field display settings. - $page->pressButton('body_plugin_settings_edit'); - $assert_session->assertWaitOnAjaxRequest(); - - // Select "Pattern" field template. - $page->selectFieldOption('Choose a Field Template', 'Pattern'); - $assert_session->assertWaitOnAjaxRequest(); - - // Choose test pattern. - $page->selectFieldOption('fields[body][settings_edit_form][third_party_settings][ds][ft][settings][pattern]', 'Field'); - $assert_session->assertWaitOnAjaxRequest(); - - // Choose test variant. - $page->selectFieldOption('Variant', 'Overridden'); - $assert_session->assertWaitOnAjaxRequest(); - - // Map pattern fields. - $page->selectFieldOption('Destination for Body', '- Hidden -'); - $page->selectFieldOption('Destination for Body: value', 'Value'); - $page->selectFieldOption('Destination for Body: format', 'Format'); - - // Submit field settings. - // @todo Make sure values are persisted when re-editing the field settings. - $page->pressButton('Update'); - $assert_session->assertWaitOnAjaxRequest(); - - // Save view mode setting page. - $page->pressButton('Save'); - - // Get default view mode for Article node bundle. - $display = EntityViewDisplay::load("node.article.default"); - - // Assert existence of third party settings. - $third_party_settings = $display->getComponent('body')['third_party_settings']; - $this->assertNotEmpty($third_party_settings['ds']['ft'], "Field template settings not found."); - - // Assert settings value. - $settings = $third_party_settings['ds']['ft']; - $this->assertEquals($settings['id'], 'pattern'); - $this->assertEquals($settings['settings']['pattern'], 'field'); - $this->assertEquals($settings['settings']['pattern_variant'], 'overridden'); - - // Assert mappings. - $this->assertNotEmpty($settings['settings']['pattern_mapping'], "Pattern mapping is empty."); - - $mapping = $settings['settings']['pattern_mapping']; - $this->assertArrayNotHasKey('ds_field_template:body', $mapping, "Body mapping found."); - $this->assertArrayHasKey('ds_field_template:body__value', $mapping, "Body value mapping not found."); - $this->assertArrayHasKey('ds_field_template:body__format', $mapping, "Body format mapping not found."); - - $this->assertEquals($mapping['ds_field_template:body__value']['destination'], 'value', "Body value mapping not valid."); - $this->assertEquals($mapping['ds_field_template:body__format']['destination'], 'format', "Body format mapping not valid."); - } - -} diff --git a/modules/ui_patterns_ds/ui_patterns_ds.info.yml b/modules/ui_patterns_ds/ui_patterns_ds.info.yml deleted file mode 100644 index a37275e2..00000000 --- a/modules/ui_patterns_ds/ui_patterns_ds.info.yml +++ /dev/null @@ -1,9 +0,0 @@ -name: UI Patterns Display Suite -type: module -description: Use patterns as Display Suite field templates. It also provides Display Suite pattern sources. -core: 8.x -core_version_requirement: ^8 || ^9 -package: User interface -dependencies: - - ds:ds - - ui_patterns:ui_patterns diff --git a/modules/ui_patterns_ds/ui_patterns_ds.module b/modules/ui_patterns_ds/ui_patterns_ds.module deleted file mode 100644 index 07b22913..00000000 --- a/modules/ui_patterns_ds/ui_patterns_ds.module +++ /dev/null @@ -1,94 +0,0 @@ - [ - 'variables' => ['pattern' => NULL], - ], - ]; -} - -/** - * Implements hook_form_FORM_ID_alter(). - */ -function ui_patterns_ds_form_entity_view_display_edit_form_alter(&$form, FormStateInterface $form_state, $form_id) { - array_unshift($form['actions']['submit']['#submit'], 'ui_patterns_ds_field_overview_submit'); -} - -/** - * Form submit callback to fix the field_group configuration. - * - * @param array $form - * The form. - * @param \Drupal\Core\Form\FormStateInterface $form_state - * The form state. - */ -function ui_patterns_ds_field_overview_submit(array $form, FormStateInterface $form_state) { - /** @var \Drupal\Core\Entity\EntityFormInterface $entity_form */ - /** @var \Drupal\Core\Entity\Display\EntityDisplayInterface $display */ - - $entity_form = $form_state->getFormObject(); - $display = $entity_form->getEntity(); - $components = array_filter($display->getComponents(), function ($component) { - return isset($component['third_party_settings']['ds']['ft']['settings']['pattern']); - }); - - foreach ($components as $name => $component) { - PatternDisplayFormTrait::processFormStateValues($component['third_party_settings']['ds']['ft']['settings']); - $display->setComponent($name, $component); - } - - $display->save(); -} - -/** - * Preprocess hook. - * - * @param array $variables - * Theme variables. - */ -function template_preprocess_field__pattern_ds_field_template(array &$variables) { - \Drupal::service('ui_patterns_ds.field_template_processor')->process($variables); -} - -/** - * Implements hook_ui_patterns_suggestions_alter(). - */ -function ui_patterns_ds_ui_patterns_suggestions_alter(array &$suggestions, array $variables, PatternContext $context) { - if ($context->isOfType('ds_field_template')) { - $hook = $variables['theme_hook_original']; - $variant = $variables["variant"] ?? ''; - $field_name = $context->getProperty('field_name'); - $entity_type = $context->getProperty('entity_type'); - $bundle = $context->getProperty('bundle'); - $view_mode = $context->getProperty('view_mode'); - - $suggestions[] = $hook . '__ds_field_template'; - $suggestions[] = $hook . '__ds_field_template__' . $field_name; - $suggestions[] = $hook . '__ds_field_template__' . $field_name . '__' . $entity_type; - $suggestions[] = $hook . '__ds_field_template__' . $field_name . '__' . $entity_type . '__' . $bundle; - $suggestions[] = $hook . '__ds_field_template__' . $field_name . '__' . $entity_type . '__' . $view_mode; - $suggestions[] = $hook . '__ds_field_template__' . $field_name . '__' . $entity_type . '__' . $bundle . '__' . $view_mode; - - if (!empty($variant)) { - $suggestions[] = $hook . '__variant_' . $variant . '__ds_field_template'; - $suggestions[] = $hook . '__variant_' . $variant . '__ds_field_template__' . $field_name; - $suggestions[] = $hook . '__variant_' . $variant . '__ds_field_template__' . $field_name . '__' . $entity_type; - $suggestions[] = $hook . '__variant_' . $variant . '__ds_field_template__' . $field_name . '__' . $entity_type . '__' . $bundle; - $suggestions[] = $hook . '__variant_' . $variant . '__ds_field_template__' . $field_name . '__' . $entity_type . '__' . $view_mode; - $suggestions[] = $hook . '__variant_' . $variant . '__ds_field_template__' . $field_name . '__' . $entity_type . '__' . $bundle . '__' . $view_mode; - } - } -} diff --git a/modules/ui_patterns_ds/ui_patterns_ds.services.yml b/modules/ui_patterns_ds/ui_patterns_ds.services.yml deleted file mode 100644 index 1cea2e17..00000000 --- a/modules/ui_patterns_ds/ui_patterns_ds.services.yml +++ /dev/null @@ -1,3 +0,0 @@ -services: - ui_patterns_ds.field_template_processor: - class: Drupal\ui_patterns_ds\FieldTemplateProcessor diff --git a/modules/ui_patterns_field_formatters/src/Plugin/Field/FieldFormatter/ComponentOneForAllFormatter.php b/modules/ui_patterns_field_formatters/src/Plugin/Field/FieldFormatter/ComponentOneForAllFormatter.php new file mode 100644 index 00000000..f0e16eb3 --- /dev/null +++ b/modules/ui_patterns_field_formatters/src/Plugin/Field/FieldFormatter/ComponentOneForAllFormatter.php @@ -0,0 +1,84 @@ +find($component_id); + $context = []; + $form['ui_patterns'] = $this->buildComponentForm($form_state, $component, $context); + return $form; + } + + /** + * {@inheritdoc} + */ + public function settingsSummary() { + $summary = parent::settingsSummary(); + return $summary; + } + + /** + * {@inheritdoc} + */ + public function viewElements(FieldItemListInterface $items, $langcode) { + $component_id = "ui_patterns_test:card"; + $variant_id = ""; + $slots = $this->getSetting('slots'); + $props = array_merge( + $this->getSetting('props'), + [ + "variant" => $variant_id, + ] + ); + return [ + '#type' => 'component', + '#component' => $component_id, + '#slots' => $slots, + '#props' => $props, + ]; + } + +} diff --git a/modules/ui_patterns_field_formatters/src/Plugin/Field/FieldFormatter/ComponentOneForEachFormatter.php b/modules/ui_patterns_field_formatters/src/Plugin/Field/FieldFormatter/ComponentOneForEachFormatter.php new file mode 100644 index 00000000..1c5be4f4 --- /dev/null +++ b/modules/ui_patterns_field_formatters/src/Plugin/Field/FieldFormatter/ComponentOneForEachFormatter.php @@ -0,0 +1,21 @@ +getSourceField('_label', 'Label'); + $sources[] = $this->getSourceField('_formatted', 'Formatted values'); + return $sources; + } + +} diff --git a/modules/ui_patterns_field_formatters/src/Plugin/UiPatterns/Source/FieldRawPropertiesSource.php b/modules/ui_patterns_field_formatters/src/Plugin/UiPatterns/Source/FieldRawPropertiesSource.php new file mode 100644 index 00000000..b58ddd03 --- /dev/null +++ b/modules/ui_patterns_field_formatters/src/Plugin/UiPatterns/Source/FieldRawPropertiesSource.php @@ -0,0 +1,38 @@ +getContextProperty('storageDefinition'); + $fields = $storageDefinition->getPropertyNames(); + foreach ($fields as $field) { + if (!$this->getContextProperty('limit')) { + $sources[] = $this->getSourceField($field, $storageDefinition->getPropertyDefinition($field)->getLabel()); + } + elseif (in_array($field, $this->getContextProperty('limit'))) { + $sources[] = $this->getSourceField($field, $storageDefinition->getPropertyDefinition($field)->getLabel()); + } + } + return $sources; + } + +} diff --git a/modules/ui_patterns_field_formatters/ui_patterns_field_formatters.info.yml b/modules/ui_patterns_field_formatters/ui_patterns_field_formatters.info.yml new file mode 100644 index 00000000..b30ab67d --- /dev/null +++ b/modules/ui_patterns_field_formatters/ui_patterns_field_formatters.info.yml @@ -0,0 +1,7 @@ +name: UI Patterns Field Formatters +type: module +description: Use UI components with field formatters. +core_version_requirement: ^10 +package: User interface +dependencies: + - ui_patterns:ui_patterns diff --git a/modules/ui_patterns_field_formatters/ui_patterns_field_formatters.module b/modules/ui_patterns_field_formatters/ui_patterns_field_formatters.module new file mode 100644 index 00000000..6eac378f --- /dev/null +++ b/modules/ui_patterns_field_formatters/ui_patterns_field_formatters.module @@ -0,0 +1,19 @@ +getDefinitions()); + $info['component_all']['field_types'] = $field_types; + $info['component_each']['field_types'] = $field_types; +} diff --git a/modules/ui_patterns_field_group/README.md b/modules/ui_patterns_field_group/README.md deleted file mode 100644 index 6e427526..00000000 --- a/modules/ui_patterns_field_group/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# UI Patterns Field Group - -Integrates UI Patterns with the [Field Group](https://www.drupal.org/project/field_group) module. diff --git a/modules/ui_patterns_field_group/src/Plugin/field_group/FieldGroupFormatter/PatternFormatter.php b/modules/ui_patterns_field_group/src/Plugin/field_group/FieldGroupFormatter/PatternFormatter.php deleted file mode 100644 index 20970b0c..00000000 --- a/modules/ui_patterns_field_group/src/Plugin/field_group/FieldGroupFormatter/PatternFormatter.php +++ /dev/null @@ -1,186 +0,0 @@ -configuration = $configuration; - $this->patternsManager = $patterns_manager; - $this->sourceManager = $source_manager; - $this->entityFinder = new EntityFinder(); - $this->moduleHandler = $module_handler; - } - - /** - * {@inheritdoc} - */ - public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) { - return new static( - $configuration, - $plugin_id, - $plugin_definition, - $container->get('plugin.manager.ui_patterns'), - $container->get('plugin.manager.ui_patterns_source'), - $container->get('module_handler') - ); - } - - /** - * {@inheritdoc} - */ - public function preRender(&$element, $rendering_object) { - - $fields = []; - $mapping = $this->getSetting('pattern_mapping'); - foreach ($mapping as $field) { - $fields[$field['destination']][] = $element[$field['source']]; - } - - $element['#type'] = 'pattern'; - $element['#id'] = $this->getSetting('pattern'); - $element['#fields'] = $fields; - $element['#multiple_sources'] = TRUE; - $element['#variant'] = $this->getSetting('pattern_variant'); - - // Allow default context values to not override those exposed elsewhere. - $element['#context']['type'] = 'field_group'; - $element['#context']['group_name'] = $this->configuration['group']->group_name; - $element['#context']['entity_type'] = $this->configuration['group']->entity_type; - $element['#context']['bundle'] = $this->configuration['group']->bundle; - $element['#context']['view_mode'] = $this->configuration['group']->mode; - - // Pass current entity to pattern context, if any. - $element['#context']['entity'] = $this->entityFinder->findEntityFromFields($element['#fields']); - } - - /** - * Get field group name. - * - * @return string - * Field group name. - */ - protected function getFieldGroupName() { - return $this->configuration['group']->group_name; - } - - /** - * {@inheritdoc} - */ - public function settingsForm() { - $form = parent::settingsForm(); - unset($form['id']); - unset($form['classes']); - - if (isset($this->configuration['group']->children) && !empty($this->configuration['group']->children)) { - $context = [ - 'entity_type' => $this->configuration['group']->entity_type, - 'entity_bundle' => $this->configuration['group']->bundle, - 'limit' => $this->configuration['group']->children, - ]; - - $this->buildPatternDisplayForm($form, 'entity_display', $context, $this->configuration['settings']); - } - else { - $form['message'] = [ - '#markup' => $this->t('Attention: you have to add fields to this field group and save the whole entity display before being able to to access the pattern display configuration.'), - ]; - } - - return $form; - } - - /** - * {@inheritdoc} - */ - public function settingsSummary() { - $label = $this->t('None'); - if (!empty($this->getSetting('pattern'))) { - $label = $this->patternsManager->getDefinition($this->getSetting('pattern'))->getLabel(); - } - - return [ - $this->t('Pattern: @pattern', ['@pattern' => $label]), - ]; - } - - /** - * {@inheritdoc} - */ - public static function defaultContextSettings($context) { - return [ - 'pattern' => '', - 'pattern_mapping' => [], - 'pattern_variant' => '', - ] + parent::defaultContextSettings($context); - } - -} diff --git a/modules/ui_patterns_field_group/src/Utility/EntityFinder.php b/modules/ui_patterns_field_group/src/Utility/EntityFinder.php deleted file mode 100644 index 4e06b65a..00000000 --- a/modules/ui_patterns_field_group/src/Utility/EntityFinder.php +++ /dev/null @@ -1,35 +0,0 @@ - $value) { - if ($key === '#object' && $value instanceof ContentEntityBase) { - return $value; - } - } - } - -} diff --git a/modules/ui_patterns_field_group/tests/modules/ui_patterns_field_group_test/config/install/core.entity_form_display.node.article.default.yml b/modules/ui_patterns_field_group/tests/modules/ui_patterns_field_group_test/config/install/core.entity_form_display.node.article.default.yml deleted file mode 100644 index 4032847a..00000000 --- a/modules/ui_patterns_field_group/tests/modules/ui_patterns_field_group_test/config/install/core.entity_form_display.node.article.default.yml +++ /dev/null @@ -1,76 +0,0 @@ -langcode: en -status: true -dependencies: - config: - - field.field.node.article.body - - field.field.node.article.field_text - - node.type.article - module: - - text -id: node.article.default -targetEntityType: node -bundle: article -mode: default -content: - body: - type: text_textarea_with_summary - weight: 121 - settings: - rows: 9 - summary_rows: 3 - placeholder: '' - third_party_settings: { } - region: content - created: - type: datetime_timestamp - weight: 10 - region: content - settings: { } - third_party_settings: { } - field_text: - weight: 122 - settings: - size: 60 - placeholder: '' - third_party_settings: { } - type: string_textfield - region: content - promote: - type: boolean_checkbox - settings: - display_label: true - weight: 15 - region: content - third_party_settings: { } - status: - type: boolean_checkbox - settings: - display_label: true - weight: 120 - region: content - third_party_settings: { } - sticky: - type: boolean_checkbox - settings: - display_label: true - weight: 16 - region: content - third_party_settings: { } - title: - type: string_textfield - weight: -5 - region: content - settings: - size: 60 - placeholder: '' - third_party_settings: { } - uid: - type: entity_reference_autocomplete - weight: 5 - settings: - match_operator: CONTAINS - size: 60 - placeholder: '' - region: content - third_party_settings: { } -hidden: { } diff --git a/modules/ui_patterns_field_group/tests/modules/ui_patterns_field_group_test/config/install/core.entity_view_display.node.article.default.yml b/modules/ui_patterns_field_group/tests/modules/ui_patterns_field_group_test/config/install/core.entity_view_display.node.article.default.yml deleted file mode 100644 index b7461a28..00000000 --- a/modules/ui_patterns_field_group/tests/modules/ui_patterns_field_group_test/config/install/core.entity_view_display.node.article.default.yml +++ /dev/null @@ -1,56 +0,0 @@ -langcode: en -status: true -dependencies: - config: - - field.field.node.article.body - - field.field.node.article.field_text - - node.type.article - module: - - field_group - - text - - user -third_party_settings: - field_group: - group_pattern_group: - children: - - field_text - parent_name: '' - weight: 20 - format_type: pattern_formatter - format_settings: - label: 'Pattern group' - pattern_variant: first - pattern: metadata - pattern_mapping: - 'fields:field_text': - destination: field_1 - weight: 0 - plugin: fields - source: field_text - label: 'Pattern group' -id: node.article.default -targetEntityType: node -bundle: article -mode: default -content: - body: - label: hidden - type: text_default - weight: 101 - settings: { } - third_party_settings: { } - region: content - field_text: - weight: 102 - label: above - settings: - link_to_entity: false - third_party_settings: { } - type: string - region: content - links: - weight: 100 - region: content - settings: { } - third_party_settings: { } -hidden: { } diff --git a/modules/ui_patterns_field_group/tests/modules/ui_patterns_field_group_test/config/install/field.field.node.article.body.yml b/modules/ui_patterns_field_group/tests/modules/ui_patterns_field_group_test/config/install/field.field.node.article.body.yml deleted file mode 100644 index 8f3681d9..00000000 --- a/modules/ui_patterns_field_group/tests/modules/ui_patterns_field_group_test/config/install/field.field.node.article.body.yml +++ /dev/null @@ -1,21 +0,0 @@ -langcode: en -status: true -dependencies: - config: - - field.storage.node.body - - node.type.article - module: - - text -id: node.article.body -field_name: body -entity_type: node -bundle: article -label: Body -description: '' -required: false -translatable: true -default_value: { } -default_value_callback: '' -settings: - display_summary: true -field_type: text_with_summary diff --git a/modules/ui_patterns_field_group/tests/modules/ui_patterns_field_group_test/config/install/field.field.node.article.field_text.yml b/modules/ui_patterns_field_group/tests/modules/ui_patterns_field_group_test/config/install/field.field.node.article.field_text.yml deleted file mode 100644 index b4ea928b..00000000 --- a/modules/ui_patterns_field_group/tests/modules/ui_patterns_field_group_test/config/install/field.field.node.article.field_text.yml +++ /dev/null @@ -1,18 +0,0 @@ -langcode: en -status: true -dependencies: - config: - - field.storage.node.field_text - - node.type.article -id: node.article.field_text -field_name: field_text -entity_type: node -bundle: article -label: Text -description: '' -required: false -translatable: false -default_value: { } -default_value_callback: '' -settings: { } -field_type: string diff --git a/modules/ui_patterns_field_group/tests/modules/ui_patterns_field_group_test/config/install/field.storage.node.field_text.yml b/modules/ui_patterns_field_group/tests/modules/ui_patterns_field_group_test/config/install/field.storage.node.field_text.yml deleted file mode 100644 index 1aeeadc2..00000000 --- a/modules/ui_patterns_field_group/tests/modules/ui_patterns_field_group_test/config/install/field.storage.node.field_text.yml +++ /dev/null @@ -1,20 +0,0 @@ -langcode: en -status: true -dependencies: - module: - - node -id: node.field_text -field_name: field_text -entity_type: node -type: string -settings: - max_length: 255 - is_ascii: false - case_sensitive: false -module: core -locked: false -cardinality: 1 -translatable: true -indexes: { } -persist_with_no_fields: false -custom_storage: false diff --git a/modules/ui_patterns_field_group/tests/modules/ui_patterns_field_group_test/config/install/node.type.article.yml b/modules/ui_patterns_field_group/tests/modules/ui_patterns_field_group_test/config/install/node.type.article.yml deleted file mode 100644 index f47f2add..00000000 --- a/modules/ui_patterns_field_group/tests/modules/ui_patterns_field_group_test/config/install/node.type.article.yml +++ /dev/null @@ -1,10 +0,0 @@ -langcode: en -status: true -dependencies: { } -name: Article -type: article -description: '' -help: '' -new_revision: true -preview_mode: 1 -display_submitted: true diff --git a/modules/ui_patterns_field_group/tests/modules/ui_patterns_field_group_test/templates/metadata.ui_patterns.yml b/modules/ui_patterns_field_group/tests/modules/ui_patterns_field_group_test/templates/metadata.ui_patterns.yml deleted file mode 100644 index 4905e1bc..00000000 --- a/modules/ui_patterns_field_group/tests/modules/ui_patterns_field_group_test/templates/metadata.ui_patterns.yml +++ /dev/null @@ -1,20 +0,0 @@ -metadata: - label: "Metadata" - variants: - first: - label: "First" - second: - label: "Second" - fields: - field_1: - type: "text" - label: "Field 1" - preview: "Field 1" - field_2: - type: "text" - label: "Field 2" - preview: "Field 3" - field_3: - type: "text" - label: "Field 3" - preview: "Field 3" diff --git a/modules/ui_patterns_field_group/tests/modules/ui_patterns_field_group_test/templates/pattern-metadata.html.twig b/modules/ui_patterns_field_group/tests/modules/ui_patterns_field_group_test/templates/pattern-metadata.html.twig deleted file mode 100644 index 6866bf69..00000000 --- a/modules/ui_patterns_field_group/tests/modules/ui_patterns_field_group_test/templates/pattern-metadata.html.twig +++ /dev/null @@ -1,9 +0,0 @@ -{# -/** - * @file - * Test metadata pattern. - */ -#} -

{{ "Field 1:"|t }} {{ field_1 }}

-

{{ "Field 2:"|t }} {{ field_2 }}

-

{{ "Field 3:"|t }} {{ field_3 }}

diff --git a/modules/ui_patterns_field_group/tests/modules/ui_patterns_field_group_test/ui_patterns_field_group_test.info.yml b/modules/ui_patterns_field_group/tests/modules/ui_patterns_field_group_test/ui_patterns_field_group_test.info.yml deleted file mode 100644 index a6226496..00000000 --- a/modules/ui_patterns_field_group/tests/modules/ui_patterns_field_group_test/ui_patterns_field_group_test.info.yml +++ /dev/null @@ -1,20 +0,0 @@ -name: 'UI Patterns Field Group Test' -type: module -description: 'Test module for UI Patterns.' -core: 8.x -core_version_requirement: ^8 || ^9 -hidden: true -package: 'User interface' -dependencies: -- ui_patterns -- ui_patterns_field_group -- ui_patterns_library - -config_devel: - install: - - core.entity_form_display.node.article.default - - core.entity_view_display.node.article.default - - field.field.node.article.body - - field.field.node.article.field_text - - field.storage.node.field_text - - node.type.article diff --git a/modules/ui_patterns_field_group/tests/src/FunctionalJavascript/UiPatternsFieldGroupRenderTest.php b/modules/ui_patterns_field_group/tests/src/FunctionalJavascript/UiPatternsFieldGroupRenderTest.php deleted file mode 100644 index 066c1e16..00000000 --- a/modules/ui_patterns_field_group/tests/src/FunctionalJavascript/UiPatternsFieldGroupRenderTest.php +++ /dev/null @@ -1,91 +0,0 @@ -assertSession(); - - $this->enableTwigDebugMode(); - - $user = $this->drupalCreateUser([], NULL, TRUE); - $this->drupalLogin($user); - - $node = $this->drupalCreateNode([ - 'title' => 'Test article', - 'field_text' => 'Test text field', - 'type' => 'article', - ]); - - $this->drupalGet($node->toUrl()); - - // Assert correct variant suggestions. - $suggestions = [ - 'pattern-metadata--variant-first--field-group--group-pattern-group--node--article--default.html.twig', - 'pattern-metadata--variant-first--field-group--group-pattern-group--node--default.html.twig', - 'pattern-metadata--variant-first--field-group--group-pattern-group--node--article.html.twig', - 'pattern-metadata--variant-first--field-group--group-pattern-group--node.html.twig', - 'pattern-metadata--variant-first--field-group--group-pattern-group.html.twig', - 'pattern-metadata--variant-first--field-group.html.twig', - - 'pattern-metadata--field-group--group-pattern-group--node--article--default.html.twig', - 'pattern-metadata--field-group--group-pattern-group--node--default.html.twig', - 'pattern-metadata--field-group--group-pattern-group--node--article.html.twig', - 'pattern-metadata--field-group--group-pattern-group--node.html.twig', - 'pattern-metadata--field-group--group-pattern-group.html.twig', - 'pattern-metadata--field-group.html.twig', - - 'pattern-metadata--variant-first.html.twig', - 'pattern-metadata.html.twig', - ]; - foreach ($suggestions as $suggestion) { - $assert_session->responseContains($suggestion); - } - - // Test field content is rendered in field group pattern. - $assert_session->pageTextContains('Field 1: Text Test text field'); - } - -} diff --git a/modules/ui_patterns_field_group/tests/src/FunctionalJavascript/UiPatternsFieldGroupSettingsTest.php b/modules/ui_patterns_field_group/tests/src/FunctionalJavascript/UiPatternsFieldGroupSettingsTest.php deleted file mode 100644 index b6f4fedc..00000000 --- a/modules/ui_patterns_field_group/tests/src/FunctionalJavascript/UiPatternsFieldGroupSettingsTest.php +++ /dev/null @@ -1,120 +0,0 @@ -getSession()->getPage(); - $assert_session = $this->assertSession(); - - $user = $this->drupalCreateUser([], NULL, TRUE); - $this->drupalLogin($user); - - // Visit Article's field group creation page. - $this->drupalGet('/admin/structure/types/manage/article/display/add-group'); - - // Add new Pattern field group. - $page->selectFieldOption('Add a new group', 'Pattern'); - $assert_session->assertWaitOnAjaxRequest(); - - // Select pattern and save. - $page->fillField('Label', 'Metadata'); - $page->waitFor(10, function (DocumentElement $page) { - return $page->hasContent('Machine name: group_metadata'); - }); - $page->pressButton('Save and continue'); - - // Assert warning message. - $assert_session->pageTextContains("Attention: you have to add fields to this field group and save the whole entity display before being able to to access the pattern display configuration."); - } - - /** - * Test that pattern field group settings are correctly saved. - */ - public function testUiPatternsFieldGroupSettings() { - $page = $this->getSession()->getPage(); - $assert_session = $this->assertSession(); - - $user = $this->drupalCreateUser([], NULL, TRUE); - $this->drupalLogin($user); - - // Visit Article's default view mode page. - $this->drupalGet('/admin/structure/types/manage/article/display'); - - // Click on field group settings button. - $page->pressButton('group_pattern_group_group_settings_edit'); - $assert_session->assertWaitOnAjaxRequest(); - - // Choose variant. - $page->selectFieldOption('Variant', 'Second'); - $page->selectFieldOption('Destination for Text', 'Field 2'); - $assert_session->assertWaitOnAjaxRequest(); - - // Submit field group settings. - $page->pressButton('Update'); - $assert_session->assertWaitOnAjaxRequest(); - - // Save view mode setting page. - $page->pressButton('Save'); - - // Get default view mode for Article node bundle. - $display = EntityViewDisplay::load("node.article.default"); - - // Assert existence of third party settings. - $settings = $display->getThirdPartySetting('field_group', 'group_pattern_group'); - - // Assert settings value. - $this->assertEquals($settings['format_type'], 'pattern_formatter'); - $this->assertEquals($settings['format_settings']['pattern'], 'metadata'); - $this->assertEquals($settings['format_settings']['pattern_variant'], 'second'); - - // Assert mappings. - $this->assertNotEmpty($settings['format_settings']['pattern_mapping'], "Pattern mapping is empty."); - - $mapping = $settings['format_settings']['pattern_mapping']; - $this->assertArrayHasKey('fields:field_text', $mapping, 'Mapping not found.'); - $this->assertEquals($mapping['fields:field_text']['destination'], 'field_2', "Mapping not valid."); - } - -} diff --git a/modules/ui_patterns_field_group/tests/src/Unit/EntityFinderTest.php b/modules/ui_patterns_field_group/tests/src/Unit/EntityFinderTest.php deleted file mode 100644 index 8259468d..00000000 --- a/modules/ui_patterns_field_group/tests/src/Unit/EntityFinderTest.php +++ /dev/null @@ -1,113 +0,0 @@ -findEntityFromFields($fields); - $this->assertEquals($expected, $entity); - } - - /** - * Test data provider. - * - * @return array - * Test data. - */ - public function fieldsDataProvider() { - $good = $this->getMockBuilder(ContentEntityBase::class) - ->disableOriginalConstructor() - ->getMock(); - $bad = new \stdClass(); - - return [ - // Found with singe value per field. - [ - 'fields' => [ - 'foo' => ['#object' => $good], - 'bar' => ['#object' => $bad], - ], - 'expected' => $good, - ], - - // Found with singe value per field. - [ - 'fields' => [ - 'bar' => ['#object' => $bad], - 'foo' => ['#object' => $good], - ], - 'expected' => $good, - ], - - // Found with multiple values per field. - [ - 'fields' => [ - 'foo' => [['#object' => $good]], - 'bar' => [['#object' => $bad]], - ], - 'expected' => $good, - ], - - // Found with multiple values per field. - [ - 'fields' => [ - 'bar' => [ - ['#object' => $bad], - ['#object' => $good], - ], - 'foo' => [ - ['#object' => $bad], - ['#object' => $bad], - ], - ], - 'expected' => $good, - ], - - // Found with one empty array field and multiple values per field. - [ - 'fields' => [ - 'foo' => [ - [], - ['#object' => $good], - ], - ], - 'expected' => $good, - ], - - // Found with one empty null field and multiple values per field. - [ - 'fields' => [ - 'foo' => [NULL, ['#object' => $good], - ], - ], - 'expected' => $good, - ], - - // Not found with one empty null field and multiple values per field. - [ - 'fields' => [ - 'foo' => [NULL, ['#object' => $bad], - ], - ], - 'expected' => NULL, - ], - ]; - } - -} diff --git a/modules/ui_patterns_field_group/ui_patterns_field_group.info.yml b/modules/ui_patterns_field_group/ui_patterns_field_group.info.yml deleted file mode 100644 index 2c5a4b0b..00000000 --- a/modules/ui_patterns_field_group/ui_patterns_field_group.info.yml +++ /dev/null @@ -1,9 +0,0 @@ -name: UI Patterns Field Group -type: module -description: Use patterns as field groups templates. -core: 8.x -core_version_requirement: ^8 || ^9 -package: User interface -dependencies: - - field_group:field_group - - ui_patterns:ui_patterns diff --git a/modules/ui_patterns_field_group/ui_patterns_field_group.module b/modules/ui_patterns_field_group/ui_patterns_field_group.module deleted file mode 100644 index 130ed17e..00000000 --- a/modules/ui_patterns_field_group/ui_patterns_field_group.module +++ /dev/null @@ -1,112 +0,0 @@ -get('field_group'); - if (!empty($field_group_form_state)) { - foreach ($form['#fieldgroups'] as $group_name) { - // Only save updated groups. - if (!isset($field_group_form_state[$group_name])) { - continue; - } - - if (isset($field_group_form_state[$group_name]->format_settings)) { - // Call static processFormStateValues if the plugin implements it. - $plugin_definition = \Drupal::service('plugin.manager.field_group.formatters')->getDefinition($field_group_form_state[$group_name]->format_type, FALSE); - if (method_exists($plugin_definition['class'], 'processFormStateValues')) { - call_user_func_array([ - $plugin_definition['class'], - 'processFormStateValues', - ], - [&$field_group_form_state[$group_name]->format_settings]); - } - } - } - - // Set the form_state so that the submit hook of field_groups can work. - $form_state->set('field_group', $field_group_form_state); - } -} - -/** - * Implements hook_ui_patterns_suggestions_alter(). - */ -function ui_patterns_field_group_ui_patterns_suggestions_alter(array &$suggestions, array $variables, PatternContext $context) { - if ($context->isOfType('field_group')) { - $hook = $variables['theme_hook_original']; - $variant = $variables["variant"] ?? ''; - $group_name = $context->getProperty('group_name'); - $entity_type = $context->getProperty('entity_type'); - $bundle = $context->getProperty('bundle'); - $view_mode = $context->getProperty('view_mode'); - - $suggestions[] = $hook . '__field_group'; - $suggestions[] = $hook . '__field_group__' . $group_name; - $suggestions[] = $hook . '__field_group__' . $group_name . '__' . $entity_type; - $suggestions[] = $hook . '__field_group__' . $group_name . '__' . $entity_type . '__' . $bundle; - $suggestions[] = $hook . '__field_group__' . $group_name . '__' . $entity_type . '__' . $view_mode; - $suggestions[] = $hook . '__field_group__' . $group_name . '__' . $entity_type . '__' . $bundle . '__' . $view_mode; - - if (!empty($variant)) { - $suggestions[] = $hook . '__variant_' . $variant . '__field_group'; - $suggestions[] = $hook . '__variant_' . $variant . '__field_group__' . $group_name; - $suggestions[] = $hook . '__variant_' . $variant . '__field_group__' . $group_name . '__' . $entity_type; - $suggestions[] = $hook . '__variant_' . $variant . '__field_group__' . $group_name . '__' . $entity_type . '__' . $bundle; - $suggestions[] = $hook . '__variant_' . $variant . '__field_group__' . $group_name . '__' . $entity_type . '__' . $view_mode; - $suggestions[] = $hook . '__variant_' . $variant . '__field_group__' . $group_name . '__' . $entity_type . '__' . $bundle . '__' . $view_mode; - } - } -} - -/** - * Implements hook_ui_patterns_destination_suggestions_alter(). - */ -function ui_patterns_field_group_ui_patterns_destination_suggestions_alter(array &$suggestions, array $variables, PatternContext $context) { - if ($context->isOfType('field_group')) { - $hook = $variables['theme_hook_original']; - $variant = $variables["variant"] ?? ''; - $group_name = $context->getProperty('group_name'); - $entity_type = $context->getProperty('entity_type'); - $bundle = $context->getProperty('bundle'); - $view_mode = $context->getProperty('view_mode'); - $pattern = $context->getProperty('pattern'); - $field = $context->getProperty('field'); - - $suggestions[] = $hook . '__field_group__' . $group_name . '__' . $pattern . '__' . $field; - $suggestions[] = $hook . '__field_group__' . $group_name . '__' . $entity_type . '__' . $pattern . '__' . $field; - $suggestions[] = $hook . '__field_group__' . $group_name . '__' . $entity_type . '__' . $bundle . '__' . $pattern . '__' . $field; - $suggestions[] = $hook . '__field_group__' . $group_name . '__' . $entity_type . '__' . $view_mode . '__' . $pattern . '__' . $field; - $suggestions[] = $hook . '__field_group__' . $group_name . '__' . $entity_type . '__' . $bundle . '__' . $view_mode . '__' . $pattern . '__' . $field; - - if (!empty($variant)) { - $suggestions[] = $hook . '__variant_' . $variant . '__field_group__' . $group_name . '__' . $pattern . '__' . $field; - $suggestions[] = $hook . '__variant_' . $variant . '__field_group__' . $group_name . '__' . $entity_type . '__' . $pattern . '__' . $field; - $suggestions[] = $hook . '__variant_' . $variant . '__field_group__' . $group_name . '__' . $entity_type . '__' . $bundle . '__' . $pattern . '__' . $field; - $suggestions[] = $hook . '__variant_' . $variant . '__field_group__' . $group_name . '__' . $entity_type . '__' . $view_mode . '__' . $pattern . '__' . $field; - $suggestions[] = $hook . '__variant_' . $variant . '__field_group__' . $group_name . '__' . $entity_type . '__' . $bundle . '__' . $view_mode . '__' . $pattern . '__' . $field; - } - } -} diff --git a/modules/ui_patterns_layouts/README.md b/modules/ui_patterns_layouts/README.md deleted file mode 100644 index f133c8dc..00000000 --- a/modules/ui_patterns_layouts/README.md +++ /dev/null @@ -1,12 +0,0 @@ -# UI Patterns Layouts - -Integrates UI Patterns with the **Layout Discovery** core module. - -- To use pattern layouts on view modes install the [Display Suite](https://www.drupal.org/project/ds) module. -- To use pattern layouts with [Page Manager](https://www.drupal.org/project/page_manager) - install the [Panels](https://www.drupal.org/project/panels) module. - -## Note - -Integration with the [Layout Plugin](https://www.drupal.org/project/layout_plugin) module is supported only until -version 1.0-beta6 included. diff --git a/modules/ui_patterns_layouts/src/Plugin/Layout/PatternLayout.php b/modules/ui_patterns_layouts/src/Plugin/Layout/PatternLayout.php index 12ce3b14..9d1d16e3 100644 --- a/modules/ui_patterns_layouts/src/Plugin/Layout/PatternLayout.php +++ b/modules/ui_patterns_layouts/src/Plugin/Layout/PatternLayout.php @@ -4,12 +4,13 @@ use Drupal\Core\Extension\ModuleHandlerInterface; use Drupal\Core\Form\FormStateInterface; +use Drupal\Core\Form\SubformState; use Drupal\Core\Layout\LayoutDefault; use Drupal\Core\Layout\LayoutDefinition; use Drupal\Core\Plugin\ContainerFactoryPluginInterface; use Drupal\Core\Plugin\PluginFormInterface; -use Drupal\ui_patterns\UiPatternsManager; use Drupal\Core\Render\ElementInfoManagerInterface; +use Drupal\ui_patterns\Form\UiPatternsFormBuilderTrait; use Symfony\Component\DependencyInjection\ContainerInterface; /** @@ -19,6 +20,8 @@ */ class PatternLayout extends LayoutDefault implements PluginFormInterface, ContainerFactoryPluginInterface { + use UiPatternsFormBuilderTrait; + /** * Module Handler. * @@ -26,13 +29,6 @@ class PatternLayout extends LayoutDefault implements PluginFormInterface, Contai */ protected $moduleHandler = NULL; - /** - * Pattern manager service. - * - * @var \Drupal\ui_patterns\UiPatternsManager - */ - protected $patternManager = NULL; - /** * The element info. * @@ -51,15 +47,12 @@ class PatternLayout extends LayoutDefault implements PluginFormInterface, Contai * The plugin implementation definition. * @param \Drupal\Core\Render\ElementInfoManagerInterface $element_info * Element info object. - * @param \Drupal\ui_patterns\UiPatternsManager $pattern_manager - * Pattern manager service. * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler * Module handler. */ - public function __construct(array $configuration, $plugin_id, LayoutDefinition $plugin_definition, ElementInfoManagerInterface $element_info, UiPatternsManager $pattern_manager, ModuleHandlerInterface $module_handler) { + public function __construct(array $configuration, $plugin_id, LayoutDefinition $plugin_definition, ElementInfoManagerInterface $element_info, ModuleHandlerInterface $module_handler) { parent::__construct($configuration, $plugin_id, $plugin_definition); $this->elementInfo = $element_info; - $this->patternManager = $pattern_manager; $this->moduleHandler = $module_handler; } @@ -72,7 +65,6 @@ public static function create(ContainerInterface $container, array $configuratio $plugin_id, $plugin_definition, $container->get('plugin.manager.element_info'), - $container->get('plugin.manager.ui_patterns'), $container->get('module_handler') ); } @@ -83,23 +75,19 @@ public static function create(ContainerInterface $container, array $configuratio public function build(array $regions) { $configuration = $this->getConfiguration(); - // Remove default field template if "Only content" option has been selected. - if ($configuration['pattern']['field_templates'] == 'only_content') { - $this->processOnlyContentFields($regions); + // Components expect slots to be passed along in a render array fashion. + $slots = []; + foreach (array_keys($regions) as $region_name) { + $slots[$region_name] = $regions[$region_name]; } - - // Patterns expect regions to be passed along in a render array fashion. - $fields = []; - foreach ($regions as $region_name => $region) { - $fields[$region_name] = $regions[$region_name]; - } - + $props_configuration = $configuration['pattern']['settings'] ?? []; return [ - '#type' => 'pattern', - '#id' => $this->getPluginDefinition()->get('additional')['pattern'], - '#fields' => $fields, + '#type' => 'component', + '#component' => $this->getPluginDefinition()->id(), + '#slots' => $slots, + '#props_configuration' => $props_configuration, '#variant' => $configuration['pattern']['variant'], - ] + $this->elementInfo->getInfo('pattern'); + ]; } /** @@ -118,40 +106,22 @@ public function defaultConfiguration() { * {@inheritdoc} */ public function buildConfigurationForm(array $form, FormStateInterface $form_state) { + $form = parent::buildConfigurationForm($form, $form_state); $configuration = $this->getConfiguration(); - $form = []; - - $form['pattern'] = [ + $form['ui_patterns'] = [ '#group' => 'additional_settings', - '#type' => 'details', - '#title' => $this->t('Pattern settings'), + '#type' => 'container', + '#title' => $this->t('Configuration'), '#tree' => TRUE, ]; - $form['pattern']['field_templates'] = [ - '#type' => 'select', - '#title' => $this->t('Field templates'), - '#options' => [ - 'default' => $this->t("Default"), - 'only_content' => $this->t("Only content"), - ], - '#description' => implode('
', [ - $this->t("Default: use field templates to wrap field content."), - $this->t("Only content: only print field content, without field wrapping or label."), - ]), - '#default_value' => $configuration['pattern']['field_templates'], - ]; - - $pattern_id = $this->getPluginDefinition()->get('additional')['pattern']; - $definition = $this->patternManager->getDefinition($pattern_id); - if ($definition->hasVariants()) { - $form['pattern']['variant'] = [ - '#type' => 'select', - '#title' => $this->t('Variant'), - '#options' => $definition->getVariantsAsOptions(), - '#default_value' => $configuration['pattern']['variant'], - ]; - } - $this->moduleHandler->alter('ui_patterns_layouts_display_settings_form', $form['pattern'], $definition, $configuration); + $component_id = $this->getPluginDefinition()->id(); + /** @var \Drupal\sdc\ComponentPluginManager $plugin_manager */ + $plugin_manager = \Drupal::service('plugin.manager.sdc'); + /** @var \Drupal\sdc\Component\ComponentMetadata[] $components */ + $component = $plugin_manager->find($component_id); + $context = ['form_type' => 'layout', 'form' => $form, 'layout' => $this, 'form_values' => $this->configuration['ui_patterns'] ?? []]; + $form['ui_patterns'] = $this->buildComponentForm($form_state, $component, $context); + $this->moduleHandler->alter('ui_patterns_layouts_display_configuration_form', $form['ui_patterns'], $component, $configuration); return $form; } @@ -165,25 +135,8 @@ public function validateConfigurationForm(array &$form, FormStateInterface $form * {@inheritdoc} */ public function submitConfigurationForm(array &$form, FormStateInterface $form_state) { - $this->configuration = $form_state->getValues(); - } - - /** - * Remove default field template if "Only content" option has been selected. - * - * @param array $regions - * Layout regions. - */ - protected function processOnlyContentFields(array &$regions) { - foreach ($regions as $region_name => $region) { - if (is_array($region)) { - foreach ($regions[$region_name] as $field_name => $field) { - if (is_array($field) && isset($field['#theme']) && $field['#theme'] == 'field') { - $regions[$region_name][$field_name]['#theme'] = NULL; - } - } - } - } + $sub_form_values = $this->submitComponentForm($form, $form_state, []); + $this->configuration['ui_patterns'] = $sub_form_values; + parent::submitConfigurationForm($form, $form_state); } - } diff --git a/modules/ui_patterns_layouts/tests/modules/ui_patterns_layouts_test/components/one-column/one-column.component.yml b/modules/ui_patterns_layouts/tests/modules/ui_patterns_layouts_test/components/one-column/one-column.component.yml new file mode 100644 index 00000000..c1b8e228 --- /dev/null +++ b/modules/ui_patterns_layouts/tests/modules/ui_patterns_layouts_test/components/one-column/one-column.component.yml @@ -0,0 +1,22 @@ +$schema: https://git.drupalcode.org/project/sdc/-/raw/1.x/src/metadata.schema.json +name: One columns +description: Widget Test with One Column +libraryOverrides: + dependencies: + - core/drupal +props: + type: object + properties: + heading: + title: Heading + description: The title for the banner text. + examples: + - Join us at The Conference + type: string +slots: + body: + title: Body + description: The contents of the banner. + examples: + -

Foo is NOT bar.

+ diff --git a/modules/ui_patterns_layouts/tests/modules/ui_patterns_layouts_test/components/one-column/one-column.twig b/modules/ui_patterns_layouts/tests/modules/ui_patterns_layouts_test/components/one-column/one-column.twig new file mode 100644 index 00000000..27e50fff --- /dev/null +++ b/modules/ui_patterns_layouts/tests/modules/ui_patterns_layouts_test/components/one-column/one-column.twig @@ -0,0 +1,4 @@ +
+{% block body %} +{% endblock %} +
diff --git a/modules/ui_patterns_layouts/tests/modules/ui_patterns_layouts_test/config/install/core.entity_view_display.node.article.default.yml b/modules/ui_patterns_layouts/tests/modules/ui_patterns_layouts_test/config/install/core.entity_view_display.node.article.default.yml index 20cffe3b..d595c4eb 100644 --- a/modules/ui_patterns_layouts/tests/modules/ui_patterns_layouts_test/config/install/core.entity_view_display.node.article.default.yml +++ b/modules/ui_patterns_layouts/tests/modules/ui_patterns_layouts_test/config/install/core.entity_view_display.node.article.default.yml @@ -11,11 +11,9 @@ dependencies: - user third_party_settings: field_layout: - id: pattern_one_column + id: ui_patterns_layouts_test:one_column settings: - pattern: - field_templates: default - variant: default + pattern: {} id: node.article.default targetEntityType: node bundle: article diff --git a/modules/ui_patterns_layouts/tests/modules/ui_patterns_layouts_test/templates/one_column.ui_patterns.yml b/modules/ui_patterns_layouts/tests/modules/ui_patterns_layouts_test/templates/one_column.ui_patterns.yml deleted file mode 100644 index c838098a..00000000 --- a/modules/ui_patterns_layouts/tests/modules/ui_patterns_layouts_test/templates/one_column.ui_patterns.yml +++ /dev/null @@ -1,12 +0,0 @@ -one_column: - label: "One column" - variants: - default: - label: "Default" - highlighted: - label: "Highlighted" - fields: - body: - type: "text" - label: "Body" - preview: "Body" diff --git a/modules/ui_patterns_layouts/tests/modules/ui_patterns_layouts_test/templates/pattern-one-column.html.twig b/modules/ui_patterns_layouts/tests/modules/ui_patterns_layouts_test/templates/pattern-one-column.html.twig deleted file mode 100644 index af9d618c..00000000 --- a/modules/ui_patterns_layouts/tests/modules/ui_patterns_layouts_test/templates/pattern-one-column.html.twig +++ /dev/null @@ -1,7 +0,0 @@ -{# -/** - * @file - * "One column" layout pattern. - */ -#} -

{{ body }}

\ No newline at end of file diff --git a/modules/ui_patterns_layouts/tests/modules/ui_patterns_layouts_test/ui_patterns_layouts_test.info.yml b/modules/ui_patterns_layouts/tests/modules/ui_patterns_layouts_test/ui_patterns_layouts_test.info.yml index 9f2d26c6..9362391d 100644 --- a/modules/ui_patterns_layouts/tests/modules/ui_patterns_layouts_test/ui_patterns_layouts_test.info.yml +++ b/modules/ui_patterns_layouts/tests/modules/ui_patterns_layouts_test/ui_patterns_layouts_test.info.yml @@ -1,21 +1,17 @@ name: 'UI Patterns Layouts Test' type: module description: 'Test module for UI Patterns.' -core: 8.x -core_version_requirement: ^8 || ^9 -hidden: true -package: 'User interface' +package: 'Testing' dependencies: -- ui_patterns -- ui_patterns_layouts -- ui_patterns_library -- node -- text -- field_layout -- field_ui - + - drupal:field_layout + - drupal:field_ui + - drupal:node + - drupal:text + - ui_patterns:ui_patterns + - ui_patterns:ui_patterns_layouts + - drupal:sdc config_devel: install: - - field.field.node.article.body - - node.type.article - - core.entity_view_display.node.article.default + - field.field.node.article.body + - node.type.article + - core.entity_view_display.node.article.default diff --git a/modules/ui_patterns_layouts/tests/src/FunctionalJavascript/UiPatternsLayoutsRenderTest.php b/modules/ui_patterns_layouts/tests/src/FunctionalJavascript/UiPatternsLayoutsRenderTest.php deleted file mode 100644 index 5402a1b9..00000000 --- a/modules/ui_patterns_layouts/tests/src/FunctionalJavascript/UiPatternsLayoutsRenderTest.php +++ /dev/null @@ -1,92 +0,0 @@ -enableTwigDebugMode(); - $this->drupalLogin($this->drupalCreateUser([], NULL, TRUE)); - - $node = $this->drupalCreateNode([ - 'title' => 'Test article', - 'body' => 'Test body', - 'type' => 'article', - ]); - - $this->drupalGet($node->toUrl()); - - $assert_session = $this->assertSession(); - - // Assert correct variant suggestions. - $suggestions = [ - 'pattern-one-column--variant-default--layout--node--1.html.twig', - 'pattern-one-column--variant-default--layout--node--article--full.html.twig', - 'pattern-one-column--variant-default--layout--node--full.html.twig', - 'pattern-one-column--variant-default--layout--node--article.html.twig', - 'pattern-one-column--variant-default--layout--node.html.twig', - 'pattern-one-column--variant-default--layout.html.twig', - - 'pattern-one-column--layout--node--1.html.twig', - 'pattern-one-column--layout--node--article--full.html.twig', - 'pattern-one-column--layout--node--full.html.twig', - 'pattern-one-column--layout--node--article.html.twig', - 'pattern-one-column--layout--node.html.twig', - 'pattern-one-column--layout.html.twig', - - 'pattern-one-column--variant-default.html.twig', - 'pattern-one-column.html.twig', - ]; - foreach ($suggestions as $suggestion) { - $assert_session->responseContains($suggestion); - } - - // Test content is rendered in the pattern. - $assert_session->elementContains('css', 'article', 'Test body'); - } - -} diff --git a/modules/ui_patterns_layouts/tests/src/FunctionalJavascript/UiPatternsLayoutsSettingsTest.php b/modules/ui_patterns_layouts/tests/src/FunctionalJavascript/UiPatternsLayoutsSettingsTest.php deleted file mode 100644 index 1db3df03..00000000 --- a/modules/ui_patterns_layouts/tests/src/FunctionalJavascript/UiPatternsLayoutsSettingsTest.php +++ /dev/null @@ -1,77 +0,0 @@ -getSession()->getPage(); - - $user = $this->drupalCreateUser([], NULL, TRUE); - $this->drupalLogin($user); - - // Visit Article's default display settings page. - $this->drupalGet('/admin/structure/types/manage/article/display'); - - // Click on Pattern settings. - $page->pressButton('Layout settings'); - $page->pressButton('Pattern settings'); - - // Select "Highlighted" field template. - $page->selectFieldOption('Variant', 'Highlighted'); - - $page->pressButton('Save'); - - // Get default view mode for Article node bundle. - $display = EntityViewDisplay::load("node.article.default"); - - // Assert existence of third party settings. - $third_party_settings = $display->getThirdPartySettings('field_layout'); - - // Assert settings value. - $this->assertEquals($third_party_settings['settings']['pattern']['variant'], 'highlighted'); - } - -} diff --git a/modules/ui_patterns_layouts/ui_patterns_layouts.api.php b/modules/ui_patterns_layouts/ui_patterns_layouts.api.php deleted file mode 100644 index 455b718d..00000000 --- a/modules/ui_patterns_layouts/ui_patterns_layouts.api.php +++ /dev/null @@ -1,22 +0,0 @@ - 'input']; -} diff --git a/modules/ui_patterns_layouts/ui_patterns_layouts.info.yml b/modules/ui_patterns_layouts/ui_patterns_layouts.info.yml index d0f06b18..198c9b49 100644 --- a/modules/ui_patterns_layouts/ui_patterns_layouts.info.yml +++ b/modules/ui_patterns_layouts/ui_patterns_layouts.info.yml @@ -1,9 +1,8 @@ -name: UI Patterns Layouts +name: "UI Patterns Layouts" type: module -description: Use patterns as layouts via the Layout Discovery module. -core: 8.x -core_version_requirement: ^8 || ^9 -package: User interface +description: "Use UI components as layouts plugins." +core_version_requirement: ^10 +package: "User interface" dependencies: - drupal:layout_discovery - ui_patterns:ui_patterns diff --git a/modules/ui_patterns_layouts/ui_patterns_layouts.install b/modules/ui_patterns_layouts/ui_patterns_layouts.install deleted file mode 100644 index e510cd34..00000000 --- a/modules/ui_patterns_layouts/ui_patterns_layouts.install +++ /dev/null @@ -1,22 +0,0 @@ -moduleExists('layout_plugin')) { - $installer->uninstall(['layout_plugin']); - } - - if (!\Drupal::moduleHandler()->moduleExists('layout_discovery')) { - $installer->install(['layout_discovery']); - } -} diff --git a/modules/ui_patterns_layouts/ui_patterns_layouts.module b/modules/ui_patterns_layouts/ui_patterns_layouts.module index 9d5e2786..5ee63f76 100644 --- a/modules/ui_patterns_layouts/ui_patterns_layouts.module +++ b/modules/ui_patterns_layouts/ui_patterns_layouts.module @@ -5,129 +5,30 @@ * Contains module file. */ -use Drupal\ui_patterns\Element\PatternContext; use Drupal\Core\Layout\LayoutDefinition; -use Drupal\ui_patterns\UiPatterns; -use Drupal\Core\Entity\Display\EntityViewDisplayInterface; -use Drupal\Core\Entity\EntityInterface; -use Drupal\field_layout\Display\EntityDisplayWithLayoutInterface; /** * Implements hook_layout_alter(). */ function ui_patterns_layouts_layout_alter(&$definitions) { - /** @var \Drupal\ui_patterns\Definition\PatternDefinition[] $pattern_definitions */ - - // @todo Use layout deriver instead. - // @link https://github.com/nuvoleweb/ui_patterns/issues/94 - foreach (UiPatterns::getPatternDefinitions() as $pattern_definition) { + /** @var \Drupal\sdc\ComponentPluginManager $plugin_manager */ + $plugin_manager = \Drupal::service('plugin.manager.sdc'); + /** @var \Drupal\sdc\Component\ComponentMetadata[] $components */ + $components = $plugin_manager->getDefinitions(); + foreach ($components as $component) { $definition = [ - 'label' => $pattern_definition->getLabel(), - 'theme' => $pattern_definition->getThemeHook(), - 'provider' => $pattern_definition->getProvider(), - 'category' => 'Patterns', + 'label' => $component['name'] ?? $component['id'], + 'category' => 'sdc', + 'provider' => $component['provider'], 'class' => '\Drupal\ui_patterns_layouts\Plugin\Layout\PatternLayout', - 'pattern' => $pattern_definition->id(), - 'template' => 'pattern-' . $pattern_definition->id(), + 'id' => $component['id'], ]; - foreach ($pattern_definition->getFields() as $field) { - $definition['regions'][$field->getName()]['label'] = $field->getLabel(); - } - $definitions['pattern_' . $pattern_definition->id()] = new LayoutDefinition($definition); - } -} - -/** - * Implements hook_preprocess_HOOK(). - */ -function ui_patterns_layouts_preprocess_ds_entity_view(&$variables) { - if (isset($variables['content']['#type']) && $variables['content']['#type'] == 'pattern') { - - /** @var \Drupal\Core\Entity\EntityInterface $entity */ - $entity = $variables['content']['#entity']; - - // Allow default context values to not override those exposed elsewhere. - $variables['content']['#context']['type'] = 'layout'; - $variables['content']['#context']['entity_type'] = $variables['content']['#entity_type']; - $variables['content']['#context']['bundle'] = $variables['content']['#bundle']; - $variables['content']['#context']['view_mode'] = $variables['content']['#view_mode']; - $variables['content']['#context']['entity_id'] = $entity->id(); - $variables['content']['#context']['entity'] = $entity; - } -} - -/** - * Implements hook_entity_view_alter(). - */ -function ui_patterns_layouts_entity_view_alter(array &$build, EntityInterface $entity, EntityViewDisplayInterface $display) { - if ($display instanceof EntityDisplayWithLayoutInterface && isset($build['_field_layout']['#type']) && $build['_field_layout']['#type'] == 'pattern') { - $build['_field_layout']['#context']['type'] = 'layout'; - $build['_field_layout']['#context']['entity_type'] = $build['#entity_type']; - $build['_field_layout']['#context']['bundle'] = $entity->bundle(); - $build['_field_layout']['#context']['view_mode'] = $build['#view_mode']; - $build['_field_layout']['#context']['entity_id'] = $entity->id(); - $build['_field_layout']['#context']['entity'] = $entity; - } -} - -/** - * Implements hook_ui_patterns_suggestions_alter(). - */ -function ui_patterns_layouts_ui_patterns_suggestions_alter(array &$suggestions, array $variables, PatternContext $context) { - if ($context->isOfType('layout')) { - $hook = $variables['theme_hook_original']; - $variant = $variables["variant"] ?? ''; - $entity_type = $context->getProperty('entity_type'); - $bundle = $context->getProperty('bundle'); - $view_mode = $context->getProperty('view_mode'); - $entity_id = $context->getProperty('entity_id'); - - $suggestions[] = $hook . '__layout'; - $suggestions[] = $hook . '__layout__' . $entity_type; - $suggestions[] = $hook . '__layout__' . $entity_type . '__' . $bundle; - $suggestions[] = $hook . '__layout__' . $entity_type . '__' . $view_mode; - $suggestions[] = $hook . '__layout__' . $entity_type . '__' . $bundle . '__' . $view_mode; - $suggestions[] = $hook . '__layout__' . $entity_type . '__' . $entity_id; - - if (!empty($variant)) { - $suggestions[] = $hook . '__variant_' . $variant . '__layout'; - $suggestions[] = $hook . '__variant_' . $variant . '__layout__' . $entity_type; - $suggestions[] = $hook . '__variant_' . $variant . '__layout__' . $entity_type . '__' . $bundle; - $suggestions[] = $hook . '__variant_' . $variant . '__layout__' . $entity_type . '__' . $view_mode; - $suggestions[] = $hook . '__variant_' . $variant . '__layout__' . $entity_type . '__' . $bundle . '__' . $view_mode; - $suggestions[] = $hook . '__variant_' . $variant . '__layout__' . $entity_type . '__' . $entity_id; + if (isset($component['slots'])) { + foreach ($component['slots'] as $slot_id => $slot) { + $definition['regions'][$slot_id]['label'] = $slot['title']; + } } - } -} -/** - * Implements hook_ui_patterns_destination_suggestions_alter(). - */ -function ui_patterns_layouts_ui_patterns_destination_suggestions_alter(array &$suggestions, array $variables, PatternContext $context) { - if ($context->isOfType('layout')) { - $hook = $variables['theme_hook_original']; - $variant = $variables["variant"] ?? ''; - $entity_type = $context->getProperty('entity_type'); - $bundle = $context->getProperty('bundle'); - $view_mode = $context->getProperty('view_mode'); - $entity_id = $context->getProperty('entity_id'); - $pattern = $context->getProperty('pattern'); - $field = $context->getProperty('field'); - - $suggestions[] = $hook . '__layout__' . $pattern . '__' . $field; - $suggestions[] = $hook . '__layout__' . $entity_type . '__' . $pattern . '__' . $field; - $suggestions[] = $hook . '__layout__' . $entity_type . '__' . $bundle . '__' . $pattern . '__' . $field; - $suggestions[] = $hook . '__layout__' . $entity_type . '__' . $view_mode . '__' . $pattern . '__' . $field; - $suggestions[] = $hook . '__layout__' . $entity_type . '__' . $bundle . '__' . $view_mode . '__' . $pattern . '__' . $field; - $suggestions[] = $hook . '__layout__' . $entity_type . '__' . $entity_id . '__' . $pattern . '__' . $field; - - if (!empty($variant)) { - $suggestions[] = $hook . '__variant_' . $variant . '__layout__' . $pattern . '__' . $field; - $suggestions[] = $hook . '__variant_' . $variant . '__layout__' . $entity_type . '__' . $pattern . '__' . $field; - $suggestions[] = $hook . '__variant_' . $variant . '__layout__' . $entity_type . '__' . $bundle . '__' . $pattern . '__' . $field; - $suggestions[] = $hook . '__variant_' . $variant . '__layout__' . $entity_type . '__' . $view_mode . '__' . $pattern . '__' . $field; - $suggestions[] = $hook . '__variant_' . $variant . '__layout__' . $entity_type . '__' . $bundle . '__' . $view_mode . '__' . $pattern . '__' . $field; - $suggestions[] = $hook . '__variant_' . $variant . '__layout__' . $entity_type . '__' . $entity_id . '__' . $pattern . '__' . $field; - } + $definitions[str_replace('-', '_', $component['id'])] = new LayoutDefinition($definition); } } diff --git a/modules/ui_patterns_legacy/src/Element/Pattern.php b/modules/ui_patterns_legacy/src/Element/Pattern.php new file mode 100644 index 00000000..5edf9a1c --- /dev/null +++ b/modules/ui_patterns_legacy/src/Element/Pattern.php @@ -0,0 +1,89 @@ + [ + [$this, 'convert'], + [$this, 'preRenderComponent'], + ], + '#component' => '', + '#props' => [], + '#slots' => [], + '#propsAlter' => [], + '#slotsAlter' => [], + ]; + } + + /** + * + */ + private static function resolveCompactFormat(array $element): array { + foreach (Element::properties($element) as $property) { + if (in_array($property, self::COMMON_RENDER_PROPERTIES)) { + continue; + } + // @todo test if slot or prop, looking the component definition. + } + return $element; + } + + /** + * + */ + public function convert(array $element): array { + $element = self::resolveCompactFormat($element); + $element["#type"] = "component"; + if (array_key_exists("#id", $element) && is_string($element["#id"])) { + $element["#id"] = \Drupal::service('plugin.manager.sdc')->getNamespacedId($element["#id"]); + $element["#component"] = $element["#id"]; + unset($element["#id"]); + } + if (array_key_exists("#fields", $element) && is_array($element["#fields"])) { + $element["#slots"] = $element["#fields"]; + unset($element["#fields"]); + } + if (array_key_exists("#settings", $element) && is_array($element["#settings"])) { + $element["#props"] = $element["#settings"]; + unset($element["#settings"]); + } + if (array_key_exists("#variant", $element) && is_string($element["#variant"])) { + $element["#props"]["variant"] = $element["#variant"]; + unset($element["#variant"]); + } + // @todo Translate message + \Drupal::logger('ui_patterns_legacy')->warning("Deprecated pattern render element or pattern Twig function: " . $element["#component"]); + // @todo Remove before shipping + $messenger = \Drupal::service('messenger'); + $messenger->addWarning("Deprecated pattern render element or pattern Twig function: " . $element["#component"]); + return $element; + } + +} diff --git a/modules/ui_patterns_legacy/src/Element/PatternPreview.php b/modules/ui_patterns_legacy/src/Element/PatternPreview.php new file mode 100644 index 00000000..e54bd290 --- /dev/null +++ b/modules/ui_patterns_legacy/src/Element/PatternPreview.php @@ -0,0 +1,69 @@ + [ + [$this, 'convert'], + [$this, 'loadPreviewStory'], + [$this, 'preRenderComponent'], + ], + '#component' => '', + '#props' => [], + '#slots' => [], + '#propsAlter' => [], + '#slotsAlter' => [], + ]; + } + + /** + * Load preview. + * + * @param array $element + * Render array. + * + * @return array + * Render array. + */ + public function loadPreviewStory(array $element): array { + $manager = \Drupal::service('plugin.manager.sdc'); + $component = $manager->getDefinition($element["#component"]); + if (!isset($component["stories"])) { + return $element; + } + if (empty($component["stories"])) { + return $element; + } + $story_id = self::getStoryId($component["stories"]); + $story = $component["stories"][$story_id]; + $slots = $story["slots"] ?? []; + $props = $story["props"] ?? []; + $slots = array_merge($element["#slots"], $slots); + $element["#slots"] = $manager::processStoriesSlots($slots); + $element["#props"] = array_merge($element["#props"], $props); + return $element; + } + + /** + * + */ + private function getStoryId(array $stories): string { + // In UI Patterns 1.x, there was only one story by component, called "preview". + if (array_key_exists("preview", $stories)) { + return "preview"; + } + return array_key_first($stories); + } + +} diff --git a/modules/ui_patterns_legacy/src/Plugin/Discovery/UIPatternsLegacyDiscovery.php b/modules/ui_patterns_legacy/src/Plugin/Discovery/UIPatternsLegacyDiscovery.php new file mode 100644 index 00000000..162a8672 --- /dev/null +++ b/modules/ui_patterns_legacy/src/Plugin/Discovery/UIPatternsLegacyDiscovery.php @@ -0,0 +1,103 @@ +fileSystem->basename($file, '.ui_patterns.yml'); + $provider_paths = array_flip($this->directories); + $provider = $this->findProvider($file, $provider_paths); + // We use the provider to dedupe components because it does not make sense + // for a single provider to fork itself. + return sprintf('%s:%s', $provider, $id); + } + + /** + * Finds the provider of the discovered file. + * + * The approach here is suboptimal because the provider is actually set in + * the plugin definition after the getIdentifier is called. So we either do + * this, or we forego the base class. + * + * @param string $file + * The discovered file. + * @param array $provider_paths + * The associative array of the path to the provider. + * + * @return string + * The provider + */ + private function findProvider(string $file, array $provider_paths): string { + $parts = explode(DIRECTORY_SEPARATOR, $file); + array_pop($parts); + if (empty($parts)) { + return ''; + } + $provider = $provider_paths[implode(DIRECTORY_SEPARATOR, $parts)] ?? ''; + return empty($provider) + ? $this->findProvider(implode(DIRECTORY_SEPARATOR, $parts), $provider_paths) + : $provider; + } + +} diff --git a/modules/ui_patterns_legacy/src/Plugin/Discovery/UiPatternsLegacyPluginDiscovery.php b/modules/ui_patterns_legacy/src/Plugin/Discovery/UiPatternsLegacyPluginDiscovery.php new file mode 100644 index 00000000..15c999d5 --- /dev/null +++ b/modules/ui_patterns_legacy/src/Plugin/Discovery/UiPatternsLegacyPluginDiscovery.php @@ -0,0 +1,39 @@ +discovery = new UIPatternsLegacyDiscovery($directories, $file_cache_key_suffix, $file_system); + } + +} diff --git a/modules/ui_patterns_legacy/src/Template/TwigExtension.php b/modules/ui_patterns_legacy/src/Template/TwigExtension.php new file mode 100644 index 00000000..8bd3d102 --- /dev/null +++ b/modules/ui_patterns_legacy/src/Template/TwigExtension.php @@ -0,0 +1,83 @@ + 'pattern', + '#id' => $id, + '#variant' => $variant, + '#fields' => $fields, + ]; + } + + /** + * Render given pattern. + * + * @param string $id + * Pattern ID. + * @param string $variant + * Variant name. + * + * @return array + * Pattern render array. + * + * @see \Drupal\ui_patterns_legacy\Element\PatternPreview + */ + public function renderPatternPreview(string $id, string $variant = ""): array { + return [ + '#type' => 'pattern_preview', + '#id' => $id, + '#variant' => $variant, + ]; + } + +} diff --git a/modules/ui_patterns_legacy/src/UiPatternsLegacyManager.php b/modules/ui_patterns_legacy/src/UiPatternsLegacyManager.php new file mode 100644 index 00000000..2d9510ef --- /dev/null +++ b/modules/ui_patterns_legacy/src/UiPatternsLegacyManager.php @@ -0,0 +1,31 @@ +getDefinitions(); + foreach ($components as $component) { + if (isset($component['ui_pattern_id']) && $component['ui_pattern_id'] === $ui_pattern_id) { + return $component; + } + } + } + +} diff --git a/modules/ui_patterns_legacy/src/UiPatternsLegacyPluginManager.php b/modules/ui_patterns_legacy/src/UiPatternsLegacyPluginManager.php new file mode 100644 index 00000000..54c83624 --- /dev/null +++ b/modules/ui_patterns_legacy/src/UiPatternsLegacyPluginManager.php @@ -0,0 +1,143 @@ +moduleHandler->getModuleDirectories(), + ...$this->themeHandler->getThemeDirectories(), + ]; + return array_map( + static fn(string $path) => rtrim($path, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . 'templates' . DIRECTORY_SEPARATOR . 'patterns', + $extension_directories + ); + } + /** + * {@inheritdoc} + */ + protected function getDiscovery() { + if (!isset($this->discovery)) { + $directories = $this->getScanDirectories(); + $this->discovery = new UiPatternsLegacyPluginDiscovery( + $directories, + 'ui_patterns_legacy_sdc', + $this->fileSystem + ); + } + return $this->discovery; + } + + /** + * {@inheritdoc} + */ + protected function isUiPatternFile($definition) { + return isset($definition['_discovered_file_path']) && str_ends_with( + $definition['_discovered_file_path'], + 'ui_patterns.yml' + ); + } + + /** + * {@inheritdoc} + */ + protected function mapPatternToComponent($pattern, $component) { + $component['props'] = [ + 'type' => 'object', + 'properties' => [ + 'context' => [ + ], + ], + ]; + if (isset($pattern['fields'])) { + foreach ($pattern['fields'] as $field_id => $field) { + $component['slots'][$field_id] = [ + 'title' => $field['label'], + 'description' => $field['description'] ?? NULL, + 'examples' => $field['preview'] ?? NULL, + ]; + } + } + return $component; + } + + /** + * {@inheritdoc} + */ + protected function alterDefinitions(&$definitions) { + foreach ($definitions as & $definition) { + $id = $definition['id'] ?? NULL; + if ($id) { + $definition_patterns_id = explode(':', $id)[1] ?? NULL; + if ($this->isUiPatternFile($definition)) { + $patterns = []; + // First detect UI Patterns definitions. + foreach ($definition as $key => $ui_pattern_definition) { + // Simple check for sub key label? + // Not sure if there is something better. + if (isset($ui_pattern_definition['label'])) { + $patterns[$key] = $definition[$key]; + unset($definition[$key]); + } + } + + // Components only accept one component for one file + // To support multiple components for one file we clone them. + foreach ($patterns as $pattern_id => $pattern) { + if ($definition_patterns_id !== $pattern_id) { + $cloned_definition = $definition; + $cloned_definition_id = $cloned_definition['provider'] . ':' . $pattern_id; + $cloned_definition['id'] = $cloned_definition_id; + $definitions[$cloned_definition_id] = $this->mapPatternToComponent( + $pattern, + $cloned_definition + ); + } + else { + $definitions[$id] = $this->mapPatternToComponent( + $pattern, + $definition + ); + } + $definitions[$id]['ui_pattern_id'] = $pattern_id; + } + } + } + } + parent::alterDefinitions($definitions); + foreach ($definitions as & $definition) { + $pattern_directory = dirname($definition['_discovered_file_path']); + $template = $this->findAsset( + $pattern_directory, + 'pattern-' . $definition['machineName'], + 'html.twig' + ); + $definition['template'] = basename($template); + } + + } + + +} diff --git a/modules/ui_patterns_legacy/tests/modules/ui_patterns_legacy_test/templates/patterns/alert/alert.ui_patterns.yml b/modules/ui_patterns_legacy/tests/modules/ui_patterns_legacy_test/templates/patterns/alert/alert.ui_patterns.yml new file mode 100644 index 00000000..31cde6a4 --- /dev/null +++ b/modules/ui_patterns_legacy/tests/modules/ui_patterns_legacy_test/templates/patterns/alert/alert.ui_patterns.yml @@ -0,0 +1,41 @@ +alert: + label: "Alert (Legacy)" + description: "Provide contextual feedback messages for typical user actions with the handful of available and flexible alert messages." + links: + - "https://getbootstrap.com/docs/5.3/components/alerts/" + variants: + primary: + label: "Primary" + secondary: + label: "Secondary" + success: + label: "Success" + danger: + label: "Danger" + warning: + label: "Warning" + info: + label: "Info" + light: + label: "Light" + dark: + label: "Dark" + settings: + dismissible: + type: "boolean" + label: "Dismissible?" + description: "It is possible to dismiss any alert inline." + preview: True + allow_expose: true + allow_token: true + fields: + heading: + type: "text" + label: "Heading" + description: "The alert heading. Optional." + preview: "Well done!" + message: + type: "render" + label: "Message" + description: "The alert message." + preview: "A simple alert. Check it out!" diff --git a/modules/ui_patterns_legacy/tests/modules/ui_patterns_legacy_test/templates/patterns/alert/pattern-alert.html.twig b/modules/ui_patterns_legacy/tests/modules/ui_patterns_legacy_test/templates/patterns/alert/pattern-alert.html.twig new file mode 100644 index 00000000..e482d41d --- /dev/null +++ b/modules/ui_patterns_legacy/tests/modules/ui_patterns_legacy_test/templates/patterns/alert/pattern-alert.html.twig @@ -0,0 +1,22 @@ +{% if variant and variant|lower != 'default' %} + {% set attributes = attributes.addClass('alert-' ~ variant|lower|replace({'_': '-'})) %} +{% endif %} + +{% if dismissible %} + {% set attributes = attributes.addClass(['alert-dismissible', 'fade', 'show']) %} +{% endif %} + + + {% if heading %} +

{{ heading }}

+ {% endif %} + {{ message|add_class('alert-link') }} + {% if dismissible %} + {{ pattern('close_button', { + attributes: create_attribute({ + 'data-bs-dismiss': 'alert' + }), + aria_label: 'Close'|t, + }) }} + {% endif %} + diff --git a/modules/ui_patterns_legacy/tests/modules/ui_patterns_legacy_test/templates/patterns/blockquote/blockquote.ui_patterns.yml b/modules/ui_patterns_legacy/tests/modules/ui_patterns_legacy_test/templates/patterns/blockquote/blockquote.ui_patterns.yml new file mode 100644 index 00000000..70e3b835 --- /dev/null +++ b/modules/ui_patterns_legacy/tests/modules/ui_patterns_legacy_test/templates/patterns/blockquote/blockquote.ui_patterns.yml @@ -0,0 +1,31 @@ +blockquote: + label: "Blockquote (Legacy)22" + description: "For quoting blocks of content from another source within your document." + links: + - "https://getbootstrap.com/docs/5.3/content/typography/#blockquotes" + category: "Typography" + fields: + content: + type: "render" + label: "Content" + description: "The quote." + preview: + - type: "html_tag" + tag: "p" + value: "A well-known quote, contained in a blockquote element." + footer: + type: "render" + label: "Footer" + description: "For identifying the source. Wrap the name of the source work in ." + preview: + - type: "markup" + markup: "Someone famous in " + - type: "html_tag" + tag: "cite" + value: "Source Title" + attributes: + title: "Source Title" + settings: + dismissible: + type: "boolean" + label: "Dismissible?" diff --git a/modules/ui_patterns_legacy/tests/modules/ui_patterns_legacy_test/templates/patterns/blockquote/pattern-blockquote.html.twig b/modules/ui_patterns_legacy/tests/modules/ui_patterns_legacy_test/templates/patterns/blockquote/pattern-blockquote.html.twig new file mode 100644 index 00000000..5721b60a --- /dev/null +++ b/modules/ui_patterns_legacy/tests/modules/ui_patterns_legacy_test/templates/patterns/blockquote/pattern-blockquote.html.twig @@ -0,0 +1,14 @@ +{% if footer %} + +
+ {{ content }} +
+ + +{% else %} + + {{ content }} + +{% endif %} diff --git a/modules/ui_patterns_legacy/tests/modules/ui_patterns_legacy_test/templates/patterns/button/button.ui_patterns.yml b/modules/ui_patterns_legacy/tests/modules/ui_patterns_legacy_test/templates/patterns/button/button.ui_patterns.yml new file mode 100644 index 00000000..0e27a7d0 --- /dev/null +++ b/modules/ui_patterns_legacy/tests/modules/ui_patterns_legacy_test/templates/patterns/button/button.ui_patterns.yml @@ -0,0 +1,140 @@ +button: + label: "Button (Legacy)" + description: "For actions in forms, dialogs, and more with support for multiple sizes, states, and more." + links: + - "https://getbootstrap.com/docs/5.3/components/buttons/" + category: "Button" + variants: + default: + label: "Default" + description: "No 'btn' class added." + primary__sm: + label: "Primary small" + secondary__sm: + label: "Secondary small" + success__sm: + label: "Success small" + danger__sm: + label: "Danger small" + warning__sm: + label: "Warning small" + info__sm: + label: "Info small" + light__sm: + label: "Light small" + dark__sm: + label: "Dark small" + link__sm: + label: "Link small" + primary: + label: "Primary" + secondary: + label: "Secondary" + success: + label: "Success" + danger: + label: "Danger" + warning: + label: "Warning" + info: + label: "Info" + light: + label: "Light" + dark: + label: "Dark" + link: + label: "Link" + primary__lg: + label: "Primary large" + secondary__lg: + label: "Secondary large" + success__lg: + label: "Success large" + danger__lg: + label: "Danger large" + warning__lg: + label: "Warning large" + info__lg: + label: "Info large" + light__lg: + label: "Light large" + dark__lg: + label: "Dark large" + link__lg: + label: "Link large" + outline_primary__sm: + label: "Outline Primary small" + outline_secondary__sm: + label: "Outline Secondary small" + outline_success__sm: + label: "Outline Success small" + outline_danger__sm: + label: "Outline Danger small" + outline_warning__sm: + label: "Outline Warning small" + outline_info__sm: + label: "Outline Info small" + outline_light__sm: + label: "Outline Light small" + outline_dark__sm: + label: "Outline Dark small" + outline_primary: + label: "Outline Primary" + outline_secondary: + label: "Outline Secondary" + outline_success: + label: "Outline Success" + outline_danger: + label: "Outline Danger" + outline_warning: + label: "Outline Warning" + outline_info: + label: "Outline Info" + outline_light: + label: "Outline Light" + outline_dark: + label: "Outline Dark" + outline_primary__lg: + label: "Outline Primary large" + outline_secondary__lg: + label: "Outline Secondary large" + outline_success__lg: + label: "Outline Success large" + outline_danger__lg: + label: "Outline Danger large" + outline_warning__lg: + label: "Outline Warning large" + outline_info__lg: + label: "Outline Info large" + outline_light__lg: + label: "Outline Light large" + outline_dark__lg: + label: "Outline Dark large" + dropdown_item: + label: "(Dropdown item)" + settings: + disabled: + type: "boolean" + label: "Disabled?" + description: "Is the button disabled?" + preview: false + allow_expose: true + allow_token: true + label_visually_hidden: + type: "boolean" + label: "Hide button label?" + description: "Is the button's label hidden?" + preview: false + allow_expose: true + allow_token: true + url: + type: "url" + label: "URL" + description: "The button URL. Optional." + preview: "https://example.com" + fields: + label: + type: "text" + label: "Label" + description: "The button label." + preview: "Submit" diff --git a/modules/ui_patterns_legacy/tests/modules/ui_patterns_legacy_test/templates/patterns/button/pattern-button.html.twig b/modules/ui_patterns_legacy/tests/modules/ui_patterns_legacy_test/templates/patterns/button/pattern-button.html.twig new file mode 100644 index 00000000..5fc3ddb9 --- /dev/null +++ b/modules/ui_patterns_legacy/tests/modules/ui_patterns_legacy_test/templates/patterns/button/pattern-button.html.twig @@ -0,0 +1,31 @@ +{% if variant and variant|lower != 'default' and variant|lower != 'dropdown_item' %} + {% set variants = variant|split('__')|map(v => v|lower|replace({(v): 'btn-' ~ v})|replace({'_': '-'})) %} + {% set attributes = attributes.addClass(variants) %} + {% set attributes = attributes.addClass('btn') %} +{% endif %} + +{% set attributes = (variant|lower == 'dropdown_item') ? attributes.addClass('dropdown-item') : attributes %} + +{% if label_visually_hidden %} + {% set label %} + + {{ label }} + + {% endset %} +{% endif %} + +{% if url or attributes.href %} + {% set url = url|default(attributes.href) %} + {% set attributes = attributes.setAttribute('href', url) %} + {% if disabled %} + {% set attributes = attributes.setAttribute('href', false).setAttribute('tabindex', '-1').setAttribute('aria-disabled', 'true').addClass('disabled') %} + {% endif %} + + {{ label }} +{% else %} + {% if disabled %} + {% set attributes = attributes.setAttribute('disabled', '') %} + {% endif %} + + {{ label }} +{% endif %} diff --git a/modules/ui_patterns_legacy/tests/modules/ui_patterns_legacy_test/templates/patterns/card/card.ui_patterns.yml b/modules/ui_patterns_legacy/tests/modules/ui_patterns_legacy_test/templates/patterns/card/card.ui_patterns.yml new file mode 100644 index 00000000..d5138f6a --- /dev/null +++ b/modules/ui_patterns_legacy/tests/modules/ui_patterns_legacy_test/templates/patterns/card/card.ui_patterns.yml @@ -0,0 +1,89 @@ +card: + label: "Card (Legacy)" + description: "A card is a flexible and extensible content container. It includes options for headers and footers, a wide variety of content, contextual background colors, and powerful display options." + links: + - "https://getbootstrap.com/docs/5.3/components/card/" + category: "Card" + variants: + default: + label: "Default" + horizontal: + label: "Horizontal" + settings: + image_position: + type: "select" + label: "Image position" + description: "Only for default variant." + options: + top: "Top (Default)" + bottom: "Bottom" + preview: "top" + allow_expose: true + allow_token: true + image_col_classes: + type: "textfield" + label: "Image column classes" + description: "Only for horizontal variant. Default value: col-md-4" + default_value: "col-md-4" + preview: "col-md-4" + allow_expose: true + allow_token: true + content_col_classes: + type: "textfield" + label: "Content column classes" + description: "Only for horizontal variant. Default value: col-md-8" + default_value: "col-md-8" + preview: "col-md-8" + allow_expose: true + allow_token: true + fields: + image: + type: "render" + label: "Image" + description: "Card image." + preview: + theme: "image" + uri: "data:image/svg+xml;base64,PHN2ZyBzdHlsZT0iZm9udC1zaXplOiAxLjEyNXJlbTsgdGV4dC1hbmNob3I6IG1pZGRsZTsgdXNlci1zZWxlY3Q6IG5vbmU7IiB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIyNTQiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgcHJlc2VydmVBc3BlY3RSYXRpbz0ieE1pZFlNaWQgc2xpY2UiIGZvY3VzYWJsZT0iZmFsc2UiIHJvbGU9ImltZyIgYXJpYS1sYWJlbD0iUGxhY2Vob2xkZXI6IEltYWdlIGNhcCI+PHRpdGxlPlBsYWNlaG9sZGVyPC90aXRsZT48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjODY4ZTk2Ij48L3JlY3Q+PHRleHQgeD0iNTAlIiB5PSI1MCUiIGZpbGw9IiNkZWUyZTYiIGR5PSIuM2VtIj5JbWFnZSBjYXA8L3RleHQ+PC9zdmc+Cg==" + alt: "© 2017 John Smith photography" + header: + type: "render" + label: "Header" + description: "Card header." + preview: "Featured" + content: + type: "render" + label: "Content" + description: "Card body." + preview: + - type: "pattern" + id: "card_body" + variant: "body" + fields: + title: "Card title" + subtitle: "Card subtitle" + text: "Cras justo odio, dapibus ac facilisis in, egestas eget quam. Donec id elit non mi porta gravida at eget metus. Nullam id dolor id nibh ultricies vehicula ut id elit." + content: + type: "pattern" + id: "button" + variant: "primary" + fields: + label: "Go somewhere" + links: + - type: "html_tag" + tag: "a" + value: "Card link" + attributes: + href: "#" + - type: "html_tag" + tag: "a" + value: "Another link" + attributes: + href: "#" + footer: + type: "render" + label: "Footer" + description: "Card footer." + preview: + type: "html_tag" + tag: "span" + value: "2 days ago" diff --git a/modules/ui_patterns_legacy/tests/modules/ui_patterns_legacy_test/templates/patterns/card/pattern-card--variant-horizontal--preview.html.twig b/modules/ui_patterns_legacy/tests/modules/ui_patterns_legacy_test/templates/patterns/card/pattern-card--variant-horizontal--preview.html.twig new file mode 100644 index 00000000..05e46072 --- /dev/null +++ b/modules/ui_patterns_legacy/tests/modules/ui_patterns_legacy_test/templates/patterns/card/pattern-card--variant-horizontal--preview.html.twig @@ -0,0 +1,4 @@ +{% set header = '' %} +{% set footer = '' %} +{% set attributes = attributes.setAttribute('style', 'max-width: 540px;') %} +{% extends 'pattern-card--variant-horizontal.html.twig' %} diff --git a/modules/ui_patterns_legacy/tests/modules/ui_patterns_legacy_test/templates/patterns/card/pattern-card--variant-horizontal.html.twig b/modules/ui_patterns_legacy/tests/modules/ui_patterns_legacy_test/templates/patterns/card/pattern-card--variant-horizontal.html.twig new file mode 100644 index 00000000..9e579b35 --- /dev/null +++ b/modules/ui_patterns_legacy/tests/modules/ui_patterns_legacy_test/templates/patterns/card/pattern-card--variant-horizontal.html.twig @@ -0,0 +1,20 @@ + +
+
+ {{ image|add_class('img-fluid rounded-start') }} +
+
+ {% if header %} +
+ {{ header }} +
+ {% endif %} + {{ content }} + {% if footer %} + + {% endif %} +
+
+ diff --git a/modules/ui_patterns_legacy/tests/modules/ui_patterns_legacy_test/templates/patterns/card/pattern-card.html.twig b/modules/ui_patterns_legacy/tests/modules/ui_patterns_legacy_test/templates/patterns/card/pattern-card.html.twig new file mode 100644 index 00000000..e7e33a55 --- /dev/null +++ b/modules/ui_patterns_legacy/tests/modules/ui_patterns_legacy_test/templates/patterns/card/pattern-card.html.twig @@ -0,0 +1,19 @@ + + {% if image and image_position != 'bottom' %} + {{ image|add_class('card-img-top') }} + {% endif %} + {% if header %} +
+ {{ header }} +
+ {% endif %} + {{ content }} + {% if footer %} + + {% endif %} + {% if image and image_position == 'bottom' %} + {{ image|add_class('card-img-bottom') }} + {% endif %} + diff --git a/modules/ui_patterns_legacy/tests/modules/ui_patterns_legacy_test/templates/patterns/card_body/card_body.ui_patterns.yml b/modules/ui_patterns_legacy/tests/modules/ui_patterns_legacy_test/templates/patterns/card_body/card_body.ui_patterns.yml new file mode 100644 index 00000000..ef737a0b --- /dev/null +++ b/modules/ui_patterns_legacy/tests/modules/ui_patterns_legacy_test/templates/patterns/card_body/card_body.ui_patterns.yml @@ -0,0 +1,60 @@ +card_body: + label: "(Card body) (Legacy)" + description: "Internal: to be used in the 'Card' component." + links: + - "https://getbootstrap.com/docs/5.3/components/card/" + category: "Card" + settings: + heading_level: + type: "select" + label: "Heading level" + options: + 2: "h2" + 3: "h3" + 4: "h4" + 5: "h5 (Default)" + 6: "h6" + preview: 5 + allow_expose: true + allow_token: true + fields: + title: + type: "text" + label: "Title" + description: "Card title. Plain text." + preview: "Card title" + subtitle: + type: "text" + label: "Subtitle" + description: "Card subtitle. Plain text." + preview: "Card subtitle" + text: + type: "text" + label: "Text" + description: "Card text. Plain text." + preview: "Cras justo odio, dapibus ac facilisis in, egestas eget quam. Donec id elit non mi porta gravida at eget metus. Nullam id dolor id nibh ultricies vehicula ut id elit." + content: + type: "render" + label: "Content" + description: "Free content outside of any wrapper." + preview: + type: "pattern" + id: "button" + variant: "primary" + fields: + label: "Go somewhere" + links: + type: "render" + label: "Links" + description: "Array of link elements" + preview: + - type: "html_tag" + tag: "a" + value: "Card link" + attributes: + href: "#" + - type: "html_tag" + tag: "a" + value: "Another link" + attributes: + href: "#" diff --git a/modules/ui_patterns_library/tests/modules/ui_patterns_library_module_test/templates/with_local_libraries/css/library_one.css b/modules/ui_patterns_legacy/tests/modules/ui_patterns_legacy_test/templates/patterns/card_body/pattern-card-body--preview.html.twig similarity index 100% rename from modules/ui_patterns_library/tests/modules/ui_patterns_library_module_test/templates/with_local_libraries/css/library_one.css rename to modules/ui_patterns_legacy/tests/modules/ui_patterns_legacy_test/templates/patterns/card_body/pattern-card-body--preview.html.twig diff --git a/modules/ui_patterns_legacy/tests/modules/ui_patterns_legacy_test/templates/patterns/card_body/pattern-card-body.html.twig b/modules/ui_patterns_legacy/tests/modules/ui_patterns_legacy_test/templates/patterns/card_body/pattern-card-body.html.twig new file mode 100644 index 00000000..78908777 --- /dev/null +++ b/modules/ui_patterns_legacy/tests/modules/ui_patterns_legacy_test/templates/patterns/card_body/pattern-card-body.html.twig @@ -0,0 +1,15 @@ +{% set heading_level = heading_level|default(5) %} + + + {% if title %} + {{ title }} + {% endif %} + {% if subtitle %} + {{ subtitle }} + {% endif %} + {% if text %} +

{{ text }}

+ {% endif %} + {{ content }} + {{ links|add_class('card-link') }} + diff --git a/modules/ui_patterns_legacy/tests/modules/ui_patterns_legacy_test/templates/patterns/close_button/close_button.ui_patterns.yml b/modules/ui_patterns_legacy/tests/modules/ui_patterns_legacy_test/templates/patterns/close_button/close_button.ui_patterns.yml new file mode 100644 index 00000000..cf10343f --- /dev/null +++ b/modules/ui_patterns_legacy/tests/modules/ui_patterns_legacy_test/templates/patterns/close_button/close_button.ui_patterns.yml @@ -0,0 +1,25 @@ +close_button: + label: "Close button (Legacy)" + description: "A generic close button for dismissing content like modals and alerts." + links: + - "https://getbootstrap.com/docs/5.3/components/close-button" + category: "Button" + variants: + default: + label: "Default" + white: + label: "White (deprecated)" + settings: + disabled: + type: "boolean" + label: "Disabled?" + description: "Is the button disabled?" + preview: false + allow_expose: true + allow_token: true + aria_label: + type: "textfield" + label: "Aria label" + description: "Name of the close button for assistive technology." + preview: "Close" + allow_token: true diff --git a/modules/ui_patterns_library/tests/modules/ui_patterns_library_module_test/templates/with_local_libraries/css/library_two.css b/modules/ui_patterns_legacy/tests/modules/ui_patterns_legacy_test/templates/patterns/close_button/pattern-close-button--variant-white--preview.html.twig similarity index 100% rename from modules/ui_patterns_library/tests/modules/ui_patterns_library_module_test/templates/with_local_libraries/css/library_two.css rename to modules/ui_patterns_legacy/tests/modules/ui_patterns_legacy_test/templates/patterns/close_button/pattern-close-button--variant-white--preview.html.twig diff --git a/modules/ui_patterns_library/tests/modules/ui_patterns_library_module_test/templates/with_local_libraries/js/library_two_1.js b/modules/ui_patterns_legacy/tests/modules/ui_patterns_legacy_test/templates/patterns/close_button/pattern-close-button.html.twig similarity index 100% rename from modules/ui_patterns_library/tests/modules/ui_patterns_library_module_test/templates/with_local_libraries/js/library_two_1.js rename to modules/ui_patterns_legacy/tests/modules/ui_patterns_legacy_test/templates/patterns/close_button/pattern-close-button.html.twig diff --git a/modules/ui_patterns_legacy/tests/modules/ui_patterns_legacy_test/templates/patterns/figure/figure.ui_patterns.yml b/modules/ui_patterns_legacy/tests/modules/ui_patterns_legacy_test/templates/patterns/figure/figure.ui_patterns.yml new file mode 100644 index 00000000..27881a25 --- /dev/null +++ b/modules/ui_patterns_legacy/tests/modules/ui_patterns_legacy_test/templates/patterns/figure/figure.ui_patterns.yml @@ -0,0 +1,26 @@ +figure: + label: "Figure (Legacy)" + description: "Used to display a piece of self-contained content (illustrations, diagrams, photos, code, etc) along with an optional caption. This content can be removed from the document without affecting the meaning of the document." + links: + - "https://getbootstrap.com/docs/5.3/content/figures/" + settings: + figcaption_attributes: + type: "attributes" + label: "Figcaption attributes" + description: "The attributes to customize the figcaption tag." + preview: 'class="text-end"' + allow_expose: true + fields: + image: + type: "render" + label: "Image" + description: "The content of the figure." + preview: + theme: "image" + uri: "data:image/svg+xml;base64,PHN2ZyBzdHlsZT0iZm9udC1zaXplOiAxLjEyNXJlbTsgdGV4dC1hbmNob3I6IG1pZGRsZTsgdXNlci1zZWxlY3Q6IG5vbmU7IiB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIyNTQiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgcHJlc2VydmVBc3BlY3RSYXRpbz0ieE1pZFlNaWQgc2xpY2UiIGZvY3VzYWJsZT0iZmFsc2UiIHJvbGU9ImltZyIgYXJpYS1sYWJlbD0iUGxhY2Vob2xkZXI6IEltYWdlIGNhcCI+PHRpdGxlPlBsYWNlaG9sZGVyPC90aXRsZT48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjODY4ZTk2Ij48L3JlY3Q+PHRleHQgeD0iNTAlIiB5PSI1MCUiIGZpbGw9IiNkZWUyZTYiIGR5PSIuM2VtIj5JbWFnZSBjYXA8L3RleHQ+PC9zdmc+Cg==" + alt: "© 2017 John Smith photography" + caption: + type: "text" + label: "Caption" + description: "The caption that appears under the content." + preview: "A caption for the above image." diff --git a/modules/ui_patterns_legacy/tests/modules/ui_patterns_legacy_test/templates/patterns/figure/pattern-figure.html.twig b/modules/ui_patterns_legacy/tests/modules/ui_patterns_legacy_test/templates/patterns/figure/pattern-figure.html.twig new file mode 100644 index 00000000..21dd87df --- /dev/null +++ b/modules/ui_patterns_legacy/tests/modules/ui_patterns_legacy_test/templates/patterns/figure/pattern-figure.html.twig @@ -0,0 +1,6 @@ + + {{ image|add_class('figure-img') }} + + {{ caption }} + + diff --git a/modules/ui_patterns_legacy/tests/modules/ui_patterns_legacy_test/templates/patterns/progress/pattern-progress.html.twig b/modules/ui_patterns_legacy/tests/modules/ui_patterns_legacy_test/templates/patterns/progress/pattern-progress.html.twig new file mode 100644 index 00000000..b7bc09f6 --- /dev/null +++ b/modules/ui_patterns_legacy/tests/modules/ui_patterns_legacy_test/templates/patterns/progress/pattern-progress.html.twig @@ -0,0 +1,31 @@ +{% if variant and variant|lower != 'default' %} + {% set variants = variant|split('__')|map(v => v|lower|replace({(v): 'progress-bar-' ~ v})|replace({'_': '-'})) %} + {% set attributes = attributes.addClass(variants) %} +{% endif %} + +{% set wrapper_attributes = create_attribute() %} +{# Handle wrapper ID. #} +{% if attributes.hasAttribute('id') %} + {% set wrapper_attributes = wrapper_attributes.setAttribute('id', attributes.offsetGet('id')) %} + {% set attributes = attributes.removeAttribute('id') %} +{% endif %} + +{% set wrapper_attributes = bar_height ? wrapper_attributes.setAttribute('style', 'height:' ~ bar_height ~ 'px') : wrapper_attributes %} +{% set percent = percent|default(0) %} +{% set min = min|default(0) %} +{% set max = max|default(100) %} +{% set width = (percent * 100) // max %} + + + + {{ label }} + + diff --git a/modules/ui_patterns_legacy/tests/modules/ui_patterns_legacy_test/templates/patterns/progress/progress.ui_patterns.yml b/modules/ui_patterns_legacy/tests/modules/ui_patterns_legacy_test/templates/patterns/progress/progress.ui_patterns.yml new file mode 100644 index 00000000..fe06ac6f --- /dev/null +++ b/modules/ui_patterns_legacy/tests/modules/ui_patterns_legacy_test/templates/patterns/progress/progress.ui_patterns.yml @@ -0,0 +1,47 @@ +progress: + label: "Progress (Legacy)" + description: "The progress element displays an indicator showing the completion progress of a task, typically in the form of a bar. Progress components are built with two HTML elements, some CSS to set the width, and a few attributes. Bootstrap does not use the HTML5 element, ensuring you can stack progress bars, animate them, and place text labels over them." + links: + - "https://getbootstrap.com/docs/5.3/components/progress/" + variants: + default: + label: "Default" + striped: + label: "Striped" + striped__animated: + label: "Animated stripes" + settings: + aria_label: + type: "textfield" + label: "Aria label" + description: "Name of the progress bar for assistive technology." + allow_token: true + percent: + type: "number" + label: "Total progress (%)" + description: "Width of the progress element representing total progress (25%, 50%, etc.)." + allow_token: true + preview: 50 + min: + type: "number" + label: "Minimum value" + description: "Minimum value of the progress element (default is 0). Used for an aria attribute." + allow_token: true + preview: 0 + max: + type: "number" + label: "Maximum value" + description: "Maximum value of the progress element (default is 100). Used for an aria attribute." + allow_token: true + preview: 100 + bar_height: + type: "number" + label: "Height" + description: "Height of progress element in pixels (px). Leave empty for default height." + allow_token: true + fields: + label: + type: "text" + label: "Label" + description: "Text shown inside the progress bar." + preview: "Label" diff --git a/modules/ui_patterns_legacy/tests/modules/ui_patterns_legacy_test/ui_patterns_legacy_test.info.yml b/modules/ui_patterns_legacy/tests/modules/ui_patterns_legacy_test/ui_patterns_legacy_test.info.yml new file mode 100644 index 00000000..e06299d8 --- /dev/null +++ b/modules/ui_patterns_legacy/tests/modules/ui_patterns_legacy_test/ui_patterns_legacy_test.info.yml @@ -0,0 +1,6 @@ +name: "UI Patterns Legacy Test" +type: module +description: "Provides test plugin." +package: "Testing" +dependencies: + - ui_patterns_legacy diff --git a/modules/ui_patterns_legacy/ui_patterns_legacy.info.yml b/modules/ui_patterns_legacy/ui_patterns_legacy.info.yml new file mode 100644 index 00000000..6221865c --- /dev/null +++ b/modules/ui_patterns_legacy/ui_patterns_legacy.info.yml @@ -0,0 +1,7 @@ +name: "UI Patterns Legacy" +type: module +description: "Compatibility layer for UI Patterns 1.x components." +core_version_requirement: ^10 +package: "User interface" +dependencies: + - ui_patterns:ui_patterns diff --git a/modules/ui_patterns_legacy/ui_patterns_legacy.services.yml b/modules/ui_patterns_legacy/ui_patterns_legacy.services.yml new file mode 100644 index 00000000..37c9e11f --- /dev/null +++ b/modules/ui_patterns_legacy/ui_patterns_legacy.services.yml @@ -0,0 +1,26 @@ +parameters: + ui_patterns_legacy.file_extensions: + - ".component.yml" +services: + ui_patterns_legacy.twig.extension: + class: Drupal\ui_patterns_legacy\Template\TwigExtension + tags: + - { name: twig.extension } +# ui_patterns_legacy.plugin.manager.sdc.decorator: +# class: Drupal\ui_patterns_legacy\UiPatternsLegacyPluginManager +# decorates: plugin.manager.sdc +# decoration_priority: 9 +# public: false +# arguments: +# - '@ui_patterns_legacy.plugin.manager.sdc.decorator.inner' +# - '@plugin.manager.ui_patterns_prop_type' +# - '@module_handler' +# - '@theme_handler' +# - '@cache.discovery' +# - '@config.factory' +# - '@theme.manager' +# - '@Drupal\sdc\ComponentNegotiator' +# - '@file_system' +# - '@Drupal\sdc\Component\SchemaCompatibilityChecker' +# - '@Drupal\sdc\Component\ComponentValidator' +# - '%app.root%' diff --git a/modules/ui_patterns_library/README.md b/modules/ui_patterns_library/README.md deleted file mode 100644 index 3fa42511..00000000 --- a/modules/ui_patterns_library/README.md +++ /dev/null @@ -1,6 +0,0 @@ -# UI Patterns Library - -The UI Patterns Library module allows developers to expose patterns via YAML definitions and to display them via a -pattern library page to be used as documentation for content editors or as a showcase for business, available at `/patterns`. - -For more information please refer to the [official documentation](http://ui-patterns.readthedocs.io). diff --git a/modules/ui_patterns_library/src/Controller/LibraryController.php b/modules/ui_patterns_library/src/Controller/LibraryController.php new file mode 100644 index 00000000..5a32066a --- /dev/null +++ b/modules/ui_patterns_library/src/Controller/LibraryController.php @@ -0,0 +1,73 @@ +get('plugin.manager.sdc') + ); + } + + /** + * Title callback. + * + * @return string + * Pattern label. + */ + public function title($name) { + $definition = $this->componentPluginManager->getDefinition($name); + return $definition["name"]; + } + + /** + * Render a single component page. + * + * @param string $name + * Plugin ID. + * + * @return array + * Return render array. + */ + public function single($name) { + $definition = $this->componentPluginManager->getDefinition($name); + return [ + '#theme' => 'ui_patterns_single_page', + '#component' => $definition, + ]; + } + + /** + * Render the components overview page. + * + * @return array + * Patterns overview page render array. + */ + public function overview() { + $groups = $this->componentPluginManager->getGroupedDefinitions(); + return [ + '#theme' => 'ui_patterns_overview_page', + '#groups' => $groups, + ]; + } + +} diff --git a/modules/ui_patterns_library/src/Controller/PatternsLibraryController.php b/modules/ui_patterns_library/src/Controller/PatternsLibraryController.php deleted file mode 100644 index e6494cb9..00000000 --- a/modules/ui_patterns_library/src/Controller/PatternsLibraryController.php +++ /dev/null @@ -1,150 +0,0 @@ -patternsManager = $ui_patterns_manager; - } - - /** - * {@inheritdoc} - */ - public static function create(ContainerInterface $container) { - return new static($container->get('plugin.manager.ui_patterns')); - } - - /** - * Title callback. - * - * @return string - * Pattern label. - */ - public function title($name) { - return $this->patternsManager->getDefinition($name)->getLabel(); - } - - /** - * Render pattern library page. - * - * @param string $name - * Plugin ID. - * - * @return array - * Return render array. - */ - public function single($name) { - $definition = $this->patternsManager->getDefinition($name); - - return [ - '#theme' => 'patterns_single_page', - '#pattern' => [ - 'meta' => [ - '#theme' => 'patterns_meta_information', - '#pattern' => $definition->toArray(), - ], - 'rendered' => $this->getPatternRenderArray($definition), - 'definition' => $definition->toArray(), - ], - ]; - } - - /** - * Render pattern library page. - * - * @return array - * Patterns overview page render array. - */ - public function overview() { - - $patterns = []; - foreach ($this->patternsManager->getDefinitions() as $definition) { - $patterns[$definition->id()] = $definition->toArray() + [ - 'meta' => [ - '#theme' => 'patterns_meta_information', - '#pattern' => $definition->toArray(), - ], - 'rendered' => $this->getPatternRenderArray($definition), - 'definition' => $definition->toArray(), - ]; - } - - return [ - '#theme' => 'patterns_overview_page', - '#patterns' => $patterns, - ]; - } - - /** - * Get pattern preview render array, handling variants. - * - * @param \Drupal\ui_patterns\Definition\PatternDefinition $definition - * Pattern definition object. - * - * @return array - * Render array. - */ - protected function getPatternRenderArray(PatternDefinition $definition) { - $render = []; - - // If pattern has variants then render them all adding meta information - // on top of each one, or simply render pattern preview otherwise. - if ($definition->hasVariants()) { - foreach ($definition->getVariants() as $variant) { - $render[$definition->id() . '_' . $variant->getName()] = [ - 'meta' => [ - '#theme' => 'patterns_variant_meta_information', - '#variant' => $variant->toArray(), - ], - 'pattern' => [ - '#type' => 'pattern_preview', - '#id' => $definition->id(), - '#variant' => $variant->getName(), - '#theme_wrappers' => [ - 'container' => [ - '#attributes' => ['class' => 'pattern-preview__markup pattern-preview__markup--variant_' . $variant->getName()], - ], - ], - ], - ]; - } - } - else { - $render[$definition->id()] = [ - 'pattern' => [ - '#type' => 'pattern_preview', - '#id' => $definition->id(), - '#theme_wrappers' => [ - 'container' => [ - '#attributes' => ['class' => 'pattern-preview__markup'], - ], - ], - ], - ]; - } - - return $render; - } - -} diff --git a/modules/ui_patterns_library/src/Element/ComponentStory.php b/modules/ui_patterns_library/src/Element/ComponentStory.php new file mode 100644 index 00000000..255efddb --- /dev/null +++ b/modules/ui_patterns_library/src/Element/ComponentStory.php @@ -0,0 +1,57 @@ + [ + [$this, 'loadStory'], + [$this, 'preRenderComponent'], + ], + '#component' => '', + '#story' => '', + '#props' => [], + '#slots' => [], + '#propsAlter' => [], + '#slotsAlter' => [], + ]; + } + + /** + * + */ + public function loadStory(array $element): array { + $manager = \Drupal::service('plugin.manager.sdc'); + if (!isset($element["#story"])) { + return $element; + } + $story_id = $element["#story"]; + $component = $manager->getDefinition($element["#component"]); + if (!isset($component["stories"])) { + return $element; + } + if (!isset($component["stories"][$story_id])) { + return $element; + } + $story = $component["stories"][$story_id]; + $slots = $story["slots"] ?? []; + $props = $story["props"] ?? []; + $slots = array_merge($element["#slots"], $slots); + $element["#slots"] = $manager::processStoriesSlots($slots); + $element["#props"] = array_merge($element["#props"], $props); + return $element; + } + +} diff --git a/modules/ui_patterns_library/src/Plugin/Deriver/LibraryDeriver.php b/modules/ui_patterns_library/src/Plugin/Deriver/LibraryDeriver.php deleted file mode 100644 index 520ca076..00000000 --- a/modules/ui_patterns_library/src/Plugin/Deriver/LibraryDeriver.php +++ /dev/null @@ -1,218 +0,0 @@ -root = $root; - $this->fileExtensions = $extensions; - $this->moduleHandler = $module_handler; - $this->themeHandler = $theme_handler; - $this->extensionDiscovery = new ExtensionDiscovery($root); - } - - /** - * {@inheritdoc} - */ - public static function create(ContainerInterface $container, $base_plugin_id) { - return new static( - $base_plugin_id, - $container->get('typed_data_manager'), - $container->get('messenger'), - $container->get('file_system'), - $container->get('app.root'), - $container->getParameter('ui_patterns_library.file_extensions'), - $container->get('module_handler'), - $container->get('theme_handler') - ); - } - - /** - * {@inheritdoc} - */ - public function getFileExtensions() { - return $this->fileExtensions; - } - - /** - * {@inheritdoc} - */ - public function getPatterns() { - $patterns = []; - foreach ($this->getDirectories() as $provider => $directory) { - foreach ($this->fileScanDirectory($directory) as $file_path => $file) { - $host_extension = $this->getHostExtension($file_path); - if ($host_extension == FALSE || $host_extension == $provider) { - $content = file_get_contents($file_path); - foreach (Yaml::decode($content) as $id => $definition) { - $definition['id'] = $id; - $definition['base path'] = dirname($file_path); - $definition['file name'] = basename($file_path); - $definition['provider'] = $provider; - $patterns[] = $this->getPatternDefinition($definition); - } - } - } - } - - return $patterns; - } - - /** - * Create a list of all directories to scan. - * - * This includes all module directories and directories of the default theme - * and all of its possible base themes. - * - * @return array - * An array containing directory paths keyed by their extension name. - */ - protected function getDirectories() { - $default_theme = $this->themeHandler->getDefault(); - $base_themes = $this->themeHandler->getBaseThemes($this->themeHandler->listInfo(), $default_theme); - $theme_directories = $this->themeHandler->getThemeDirectories(); - - $directories = []; - if (isset($theme_directories[$default_theme])) { - $directories[$default_theme] = $theme_directories[$default_theme]; - foreach ($base_themes as $name => $theme) { - $directories[$name] = $theme_directories[$name]; - } - } - - return $directories + $this->moduleHandler->getModuleDirectories(); - } - - /** - * Get extension name that hosts the given YAML definition file. - * - * @param string $pathname - * YAML definition file full path. - * - * @return bool|string - * Either extension machine name or FALSE if not found. - */ - protected function getHostExtension($pathname) { - $extensions = $this->getExtensionLocations(); - $parts = explode(DIRECTORY_SEPARATOR, $pathname); - while (!empty($parts)) { - $path = implode(DIRECTORY_SEPARATOR, $parts); - if (isset($extensions[$path])) { - return $extensions[$path]; - } - array_pop($parts); - } - return FALSE; - } - - /** - * Get extension locations. - * - * @return array - * Array of extensions keyed by their path location. - */ - protected function getExtensionLocations() { - /** @var \Drupal\Core\Extension\Extension[] $extensions */ - if (empty($this->extensionLocations)) { - $extensions = $this->extensionDiscovery->scan('theme') + $this->extensionDiscovery->scan('module'); - foreach ($extensions as $name => $extension) { - $this->extensionLocations[$this->root . DIRECTORY_SEPARATOR . $extension->getPath()] = $name; - } - } - return $this->extensionLocations; - } - -} diff --git a/modules/ui_patterns_library/src/Plugin/UiPatterns/Pattern/LibraryPattern.php b/modules/ui_patterns_library/src/Plugin/UiPatterns/Pattern/LibraryPattern.php deleted file mode 100644 index f57aad21..00000000 --- a/modules/ui_patterns_library/src/Plugin/UiPatterns/Pattern/LibraryPattern.php +++ /dev/null @@ -1,120 +0,0 @@ -themeHandler = $theme_handler; - } - - /** - * {@inheritdoc} - */ - public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) { - return new static( - $configuration, - $plugin_id, - $plugin_definition, - $container->get('app.root'), - $container->get('module_handler'), - $container->get('theme_handler') - ); - } - - /** - * {@inheritdoc} - */ - public function getThemeImplementation() { - $item = parent::getThemeImplementation(); - $definition = $this->getPluginDefinition(); - $item[$definition['theme hook']] += $this->processTemplateProperty($definition); - $item[$definition['theme hook']] += $this->processCustomThemeHookProperty($definition); - return $item; - } - - /** - * Process 'custom hook theme' definition property. - * - * @param \Drupal\ui_patterns\Definition\PatternDefinition $definition - * Pattern definition array. - * - * @return array - * Processed hook definition portion. - */ - protected function processCustomThemeHookProperty(PatternDefinition $definition) { - /** @var \Drupal\Core\Extension\Extension $module */ - $return = []; - if (!$definition->hasCustomThemeHook() && $this->moduleHandler->moduleExists($definition->getProvider())) { - $module = $this->moduleHandler->getModule($definition->getProvider()); - $return['path'] = $module->getPath() . '/templates'; - if ($this->templateExists($definition->getBasePath(), $definition->getTemplate())) { - $return['path'] = str_replace($this->root, '', $definition->getBasePath()); - } - } - return $return; - } - - /** - * Weather template exists in given directory. - * - * @param string $directory - * Directory full path. - * @param string $template - * Template name, without default Twig extension. - * - * @return bool - * Weather template exists in given directory. - */ - protected function templateExists($directory, $template) { - return file_exists($directory . DIRECTORY_SEPARATOR . $template . '.html.twig'); - } - - /** - * Process 'template' definition property. - * - * @param \Drupal\ui_patterns\Definition\PatternDefinition $definition - * Pattern definition array. - * - * @return array - * Processed hook definition portion. - */ - protected function processTemplateProperty(PatternDefinition $definition) { - $return = []; - - if ($definition->hasTemplate()) { - $return = ['template' => $definition->getTemplate()]; - } - return $return; - } - -} diff --git a/modules/ui_patterns_library/src/Template/TwigExtension.php b/modules/ui_patterns_library/src/Template/TwigExtension.php new file mode 100644 index 00000000..becdb5e5 --- /dev/null +++ b/modules/ui_patterns_library/src/Template/TwigExtension.php @@ -0,0 +1,61 @@ + 'component_story', + '#component' => $component_id, + '#story' => $story_id, + '#slots' => $slots, + '#props' => $props, + ]; + } + +} diff --git a/modules/ui_patterns_library/templates/patterns-meta-information.html.twig b/modules/ui_patterns_library/templates/patterns-meta-information.html.twig deleted file mode 100644 index 95dba04a..00000000 --- a/modules/ui_patterns_library/templates/patterns-meta-information.html.twig +++ /dev/null @@ -1,36 +0,0 @@ -{# -/** - * @file - * UI Pattern meta information. - */ -#} - -{% if pattern is not empty %} - - {# Pattern name and desciption. #} -

{{ pattern.label }}

-

{{ pattern.description }}

- - {# Pattern fields descriptions. #} - - - - - - - - - - - {% for field in pattern.fields %} - - - - - - - {% endfor %} - -
{{ "Field"|t }}{{ "Label"|t }}{{ "Type"|t }}{{ "Description"|t }}
{{ field.name }}{{ field.label }}{{ field.type }}{{ field.description }}
- -{% endif %} diff --git a/modules/ui_patterns_library/templates/patterns-overview-page.html.twig b/modules/ui_patterns_library/templates/patterns-overview-page.html.twig deleted file mode 100644 index 261398f0..00000000 --- a/modules/ui_patterns_library/templates/patterns-overview-page.html.twig +++ /dev/null @@ -1,48 +0,0 @@ -{# -/** - * @file - * UI Pattern library page template, override this in your theme. - */ -#} - -{% if patterns is not empty %} -

{{ "Available patterns"|t }}

- - {# List of available patterns with anchor links. #} - - -
- - {% for pattern_name, pattern in patterns %} - -
- {# Pattern name and desciption. #} - - {{ pattern.meta }} - - {# Rendered pattern preview. #} -
- {{ "Preview"|t }} - {{ pattern.rendered }} -
- - {# Link to standalone pattern preview page.#} -

- - {% trans %}View {{ pattern.label }} as stand-alone{% endtrans %} - -

-
- -
- {% endfor %} -{% endif %} - - - diff --git a/modules/ui_patterns_library/templates/patterns-single-page.html.twig b/modules/ui_patterns_library/templates/patterns-single-page.html.twig deleted file mode 100644 index 6d6294ce..00000000 --- a/modules/ui_patterns_library/templates/patterns-single-page.html.twig +++ /dev/null @@ -1,18 +0,0 @@ -{# -/** - * @file - * UI Pattern library standalone page, override this in your theme. - */ -#} - -{% if pattern is not empty %} -
- {{ pattern.meta }} - - {# Rendered pattern preview. #} -
- {{ "Preview"|t }} - {{ pattern.rendered }} -
-
-{% endif %} diff --git a/modules/ui_patterns_library/templates/patterns-variant-meta-information.html.twig b/modules/ui_patterns_library/templates/patterns-variant-meta-information.html.twig deleted file mode 100644 index f1f692d0..00000000 --- a/modules/ui_patterns_library/templates/patterns-variant-meta-information.html.twig +++ /dev/null @@ -1,25 +0,0 @@ -{# -/** - * @file - * UI Pattern variant meta information. - */ -#} - -{% if variant is not empty %} - - - - - - - - - - - - - - - -
{{ "Variant"|t }}{{ "Name"|t }}{{ "Description"|t }}
{{ variant.label }}{{ variant.name }}{{ variant.description }}
-{% endif %} diff --git a/modules/ui_patterns_library/templates/ui-patterns-component-metadata.html.twig b/modules/ui_patterns_library/templates/ui-patterns-component-metadata.html.twig new file mode 100644 index 00000000..96777e15 --- /dev/null +++ b/modules/ui_patterns_library/templates/ui-patterns-component-metadata.html.twig @@ -0,0 +1,59 @@ +{# +/** + * @file + * UI Pattern meta information. + */ +#} + +{% if component is not empty %} +

ID: {{ component.id }}

+ {% if component.description %} +

{{ component.description }}

+ {% endif %} + {% if component.tags %} +
+ {{ "Tags:"|t }} +
    + {% for tag in component.tags %} +
  • {{ tag }}
  • + {% endfor %} +
+
+ {% endif %} + + {% if component.slots or component.props %} + + + + + + + + + + + {% for slot_id, slot in component.slots %} + + + + + + + {% endfor %} + {% for prop_id, prop in component.props.properties %} + + + + + + + {% endfor %} + +
{{ "Name"|t }}{{ "Label"|t }}{{ "Type"|t }}{{ "Description"|t }}
{{ slot_id }}{{ slot.title }}slot{{ slot.description }}
{{ prop_id }}{{ prop.title }}{{ prop.ui_patterns.type_definition.pluginId }}{{ prop.description }} +
{{ prop|filter((v, k) => k != 'title')|filter((v, k) => k != 'description')
+          |filter((v, k) => k != 'examples')|filter((v, k) => k != 'default')|filter((v, k) => k != 'ui_patterns')
+          |json_encode(constant('JSON_PRETTY_PRINT') b-or constant('JSON_UNESCAPED_SLASHES')) }}
+
+ {% endif %} + +{% endif %} diff --git a/modules/ui_patterns_library/templates/ui-patterns-overview-page.html.twig b/modules/ui_patterns_library/templates/ui-patterns-overview-page.html.twig new file mode 100644 index 00000000..1f1dc86f --- /dev/null +++ b/modules/ui_patterns_library/templates/ui-patterns-overview-page.html.twig @@ -0,0 +1,69 @@ +{# +/** + * @file + * UI Pattern library page template, override this in your theme. + */ +#} + +{% if groups is not empty %} +

{{ "Available components"|t }}

+ + {% if groups|length > 1 %} + {% for group_name, components in groups %} +

{{ group_name }}

+ + {% endfor %} + {% else %} + + {% endif %} + +
+ + {% for components in groups %} + {% for component in components %} +
+

{{ component.name }}

+ {{ include('ui-patterns-component-metadata.html.twig', {'component': component}, with_context = false) }} + + {% if component.variants is defined and component.variants|length > 0 %} +
+ {% for variant_id, variant in component.variants %} +
+ {% for story_id, story in component.stories %} +
+ {{ component_story(component.id, story_id, {}, {'variant': variant_id}) }} +
+ {% endfor %} +
+ {% endfor %} +
+ {% else %} +
+ {% for story_id, story in component.stories %} +
+ {{ component_story(component.id, story_id, {}, {'variant': variant_id}) }} +
+ {% endfor %} +
+ {% endif %} +
+
+ {% endfor %} + {% endfor %} +{% endif %} diff --git a/modules/ui_patterns_library/templates/ui-patterns-single-page.html.twig b/modules/ui_patterns_library/templates/ui-patterns-single-page.html.twig new file mode 100644 index 00000000..c4626508 --- /dev/null +++ b/modules/ui_patterns_library/templates/ui-patterns-single-page.html.twig @@ -0,0 +1,42 @@ +{# +/** + * @file + * UI Pattern library standalone page, override this in your theme. + */ +#} + +{% if component is not empty %} +
+ {{ include('ui-patterns-component-metadata.html.twig', {'component': component}, with_context = false) }} + {% if component.variants is defined and component.variants|length > 0 %} +
+ {% for variant_id, variant in component.variants %} +
+

{{ variant.title }} ({{ variant_id }})

+ {% if variant.description %} +

{{ variant.description }}

+ {% endif %} + {% for story_id, story in component.stories %} +
+

{{ story.title }}

+ {% if story.description %} +

{{ story.description }}

+ {% endif %} + {{ component_story(component.id, story_id, {}, {'variant': variant_id}) }} +
+ {% endfor %} + {% endfor %} +
+ {% else %} + {% for story_id, story in component.stories %} +
+

{{ story.title }}

+ {% if story.description %} +

{{ story.description }}

+ {% endif %} + {{ component_story(component.id, story_id) }} +
+ {% endfor %} + {% endif %} +
+{% endif %} diff --git a/modules/ui_patterns_library/tests/fixtures/overview-page-patterns.yml b/modules/ui_patterns_library/tests/fixtures/overview-page-patterns.yml deleted file mode 100644 index a08b7cf4..00000000 --- a/modules/ui_patterns_library/tests/fixtures/overview-page-patterns.yml +++ /dev/null @@ -1,99 +0,0 @@ -- name: 'simple' - label: 'Simple' - description: 'A simple pattern' - has_variants: false - preview: '
Simple pattern field
' - fields: - - name: 'field' - type: 'string' - label: 'Field' - description: 'Field description' - -- name: 'with_variants' - label: 'With variants' - description: 'Pattern with variants' - has_variants: true - preview: ~ - fields: - - name: 'field' - type: 'string' - label: 'Field' - description: 'Field description' - variants: - - meta: - name: 'one' - label: 'One' - description: 'First variant' - preview: '
With variants pattern field
' - - meta: - name: 'two' - label: 'Two' - description: 'Second variant' - preview: '
With variants pattern field
' - -- name: 'with_custom_theme_hook' - theme hook: 'custom_theme_hook' - label: 'With custom theme hook' - description: 'Pattern with custom theme hook.' - has_variants: false - preview: 'With custom theme hook: Pattern field value' - fields: - - name: 'field' - type: 'string' - label: 'Field' - description: 'Field description' - -- name: 'button' - label: 'Button' - description: 'A simple button.' - has_variants: true - preview: ~ - fields: - - name: 'title' - type: 'text' - label: 'Label' - description: 'The button label' - preview: 'Submit' - - name: 'url' - type: 'text' - label: 'URL' - description: 'The button URL' - preview: 'http://example.com' - variants: - - meta: - name: 'default' - label: 'Default' - description: 'A default button, nothing to see here.' - preview: 'Submit' - - meta: - name: 'primary' - label: 'Primary' - description: 'A primary button.' - preview: 'Submit' - - meta: - name: 'danger' - label: 'Danger' - description: 'A button for dangerous operations.' - preview: 'Delete' - -- name: 'with_local_libraries' - label: 'With local libraries' - description: 'Pattern defining local libraries' - has_variants: false - preview: 'With local libraries: Pattern field value' - fields: - - name: 'field' - type: 'string' - label: 'Field' - description: 'Field description' - -- name: 'with_raw_template' - label: 'With raw template' - description: 'Pattern using raw Twig template.' - has_variants: false - preview: 'With raw template: Pattern field value' - fields: - - name: 'field' - type: 'string' - label: 'Field' - description: 'Field description' diff --git a/modules/ui_patterns_library/tests/modules/ui_patterns_library_bad_definition_test/ui_patterns_library_bad_definition_test.info.yml b/modules/ui_patterns_library/tests/modules/ui_patterns_library_bad_definition_test/ui_patterns_library_bad_definition_test.info.yml deleted file mode 100644 index 5c6900d0..00000000 --- a/modules/ui_patterns_library/tests/modules/ui_patterns_library_bad_definition_test/ui_patterns_library_bad_definition_test.info.yml +++ /dev/null @@ -1,4 +0,0 @@ -name: 'UI Patterns bad definition test' -type: module -core: 8.x -core_version_requirement: ^8 || ^9 diff --git a/modules/ui_patterns_library/tests/modules/ui_patterns_library_bad_definition_test/ui_patterns_library_bad_definition_test.ui_patterns.yml b/modules/ui_patterns_library/tests/modules/ui_patterns_library_bad_definition_test/ui_patterns_library_bad_definition_test.ui_patterns.yml deleted file mode 100644 index b411c1e6..00000000 --- a/modules/ui_patterns_library/tests/modules/ui_patterns_library_bad_definition_test/ui_patterns_library_bad_definition_test.ui_patterns.yml +++ /dev/null @@ -1,2 +0,0 @@ -bad_definition: - foo: 'bar' diff --git a/modules/ui_patterns_library/tests/modules/ui_patterns_library_module_test/templates/button/button.ui_patterns.yml b/modules/ui_patterns_library/tests/modules/ui_patterns_library_module_test/templates/button/button.ui_patterns.yml deleted file mode 100644 index 987c56a5..00000000 --- a/modules/ui_patterns_library/tests/modules/ui_patterns_library_module_test/templates/button/button.ui_patterns.yml +++ /dev/null @@ -1,24 +0,0 @@ -button: - label: Button - description: A simple button. - variants: - default: - label: Default - description: A default button, nothing to see here. - primary: - label: Primary - description: A primary button. - danger: - label: Danger - description: A button for dangerous operations. - fields: - title: - type: text - label: Label - description: The button label - preview: Submit - url: - type: text - label: URL - description: The button URL - preview: http://example.com diff --git a/modules/ui_patterns_library/tests/modules/ui_patterns_library_module_test/templates/button/pattern-button.html.twig b/modules/ui_patterns_library/tests/modules/ui_patterns_library_module_test/templates/button/pattern-button.html.twig deleted file mode 100644 index 656e5544..00000000 --- a/modules/ui_patterns_library/tests/modules/ui_patterns_library_module_test/templates/button/pattern-button.html.twig +++ /dev/null @@ -1 +0,0 @@ -{{ title }} diff --git a/modules/ui_patterns_library/tests/modules/ui_patterns_library_module_test/templates/with_local_libraries/pattern-with-local-libraries.html.twig b/modules/ui_patterns_library/tests/modules/ui_patterns_library_module_test/templates/with_local_libraries/pattern-with-local-libraries.html.twig deleted file mode 100644 index 90dac666..00000000 --- a/modules/ui_patterns_library/tests/modules/ui_patterns_library_module_test/templates/with_local_libraries/pattern-with-local-libraries.html.twig +++ /dev/null @@ -1 +0,0 @@ -With local libraries: {{ field }} \ No newline at end of file diff --git a/modules/ui_patterns_library/tests/modules/ui_patterns_library_module_test/templates/with_local_libraries/with_local_libraries.ui_patterns.yml b/modules/ui_patterns_library/tests/modules/ui_patterns_library_module_test/templates/with_local_libraries/with_local_libraries.ui_patterns.yml deleted file mode 100644 index 2ac48716..00000000 --- a/modules/ui_patterns_library/tests/modules/ui_patterns_library_module_test/templates/with_local_libraries/with_local_libraries.ui_patterns.yml +++ /dev/null @@ -1,23 +0,0 @@ -with_local_libraries: - label: 'With local libraries' - description: 'Pattern defining local libraries' - fields: - field: - type: 'string' - label: 'Field' - description: 'Field description' - preview: 'Pattern field value' - libraries: - - library_one: - css: - component: - css/library_one.css: {} - - library_two: - css: - theme: - css/library_two.css: {} - js: - js/library_two_1.js: {} - js/library_two_2.js: {} - dependencies: - - core/drupal.tabledrag diff --git a/modules/ui_patterns_library/tests/modules/ui_patterns_library_module_test/ui_patterns_library_module_test.info.yml b/modules/ui_patterns_library/tests/modules/ui_patterns_library_module_test/ui_patterns_library_module_test.info.yml deleted file mode 100644 index 8fbd6cc8..00000000 --- a/modules/ui_patterns_library/tests/modules/ui_patterns_library_module_test/ui_patterns_library_module_test.info.yml +++ /dev/null @@ -1,4 +0,0 @@ -name: 'UI Patterns library module test' -type: module -core: 8.x -core_version_requirement: ^8 || ^9 diff --git a/modules/ui_patterns_library/tests/modules/ui_patterns_library_module_test/ui_patterns_library_module_test.ui_patterns.yml b/modules/ui_patterns_library/tests/modules/ui_patterns_library_module_test/ui_patterns_library_module_test.ui_patterns.yml deleted file mode 100644 index b2d176df..00000000 --- a/modules/ui_patterns_library/tests/modules/ui_patterns_library_module_test/ui_patterns_library_module_test.ui_patterns.yml +++ /dev/null @@ -1,10 +0,0 @@ -with_raw_template: - label: 'With raw template' - description: 'Pattern using raw Twig template.' - use: "@ui_patterns_library_theme_test/raw/raw-template.twig" - fields: - field: - type: 'string' - label: 'Field' - description: 'Field description' - preview: 'Pattern field value' diff --git a/modules/ui_patterns_library/tests/modules/ui_patterns_library_theme_test/templates/custom-theme-hook.html.twig b/modules/ui_patterns_library/tests/modules/ui_patterns_library_theme_test/templates/custom-theme-hook.html.twig deleted file mode 100644 index 9b863bdf..00000000 --- a/modules/ui_patterns_library/tests/modules/ui_patterns_library_theme_test/templates/custom-theme-hook.html.twig +++ /dev/null @@ -1 +0,0 @@ -With custom theme hook: {{ field }} \ No newline at end of file diff --git a/modules/ui_patterns_library/tests/modules/ui_patterns_library_theme_test/templates/pattern-button--variant-danger.html.twig b/modules/ui_patterns_library/tests/modules/ui_patterns_library_theme_test/templates/pattern-button--variant-danger.html.twig deleted file mode 100644 index a35eda97..00000000 --- a/modules/ui_patterns_library/tests/modules/ui_patterns_library_theme_test/templates/pattern-button--variant-danger.html.twig +++ /dev/null @@ -1 +0,0 @@ -{{ 'Delete'|t }} diff --git a/modules/ui_patterns_library/tests/modules/ui_patterns_library_theme_test/templates/pattern-simple.html.twig b/modules/ui_patterns_library/tests/modules/ui_patterns_library_theme_test/templates/pattern-simple.html.twig deleted file mode 100644 index 5a03b485..00000000 --- a/modules/ui_patterns_library/tests/modules/ui_patterns_library_theme_test/templates/pattern-simple.html.twig +++ /dev/null @@ -1 +0,0 @@ -
{{ field }}
diff --git a/modules/ui_patterns_library/tests/modules/ui_patterns_library_theme_test/templates/pattern-with-variants--variant-one.html.twig b/modules/ui_patterns_library/tests/modules/ui_patterns_library_theme_test/templates/pattern-with-variants--variant-one.html.twig deleted file mode 100644 index 5ee19a3f..00000000 --- a/modules/ui_patterns_library/tests/modules/ui_patterns_library_theme_test/templates/pattern-with-variants--variant-one.html.twig +++ /dev/null @@ -1 +0,0 @@ -
{{ field }}
diff --git a/modules/ui_patterns_library/tests/modules/ui_patterns_library_theme_test/templates/pattern-with-variants--variant-two.html.twig b/modules/ui_patterns_library/tests/modules/ui_patterns_library_theme_test/templates/pattern-with-variants--variant-two.html.twig deleted file mode 100644 index e1853a3a..00000000 --- a/modules/ui_patterns_library/tests/modules/ui_patterns_library_theme_test/templates/pattern-with-variants--variant-two.html.twig +++ /dev/null @@ -1 +0,0 @@ -
{{ field }}
diff --git a/modules/ui_patterns_library/tests/modules/ui_patterns_library_theme_test/templates/raw/raw-template.twig b/modules/ui_patterns_library/tests/modules/ui_patterns_library_theme_test/templates/raw/raw-template.twig deleted file mode 100644 index 8fb4bf38..00000000 --- a/modules/ui_patterns_library/tests/modules/ui_patterns_library_theme_test/templates/raw/raw-template.twig +++ /dev/null @@ -1 +0,0 @@ -With raw template: {{ field }} \ No newline at end of file diff --git a/modules/ui_patterns_library/tests/modules/ui_patterns_library_theme_test/ui_patterns_library_theme_test.info.yml b/modules/ui_patterns_library/tests/modules/ui_patterns_library_theme_test/ui_patterns_library_theme_test.info.yml deleted file mode 100644 index d0069a21..00000000 --- a/modules/ui_patterns_library/tests/modules/ui_patterns_library_theme_test/ui_patterns_library_theme_test.info.yml +++ /dev/null @@ -1,5 +0,0 @@ -name: 'UI Patterns library theme test' -type: theme -core: 8.x -core_version_requirement: ^8 || ^9 -base theme: stark diff --git a/modules/ui_patterns_library/tests/modules/ui_patterns_library_theme_test/ui_patterns_library_theme_test.ui_patterns.yml b/modules/ui_patterns_library/tests/modules/ui_patterns_library_theme_test/ui_patterns_library_theme_test.ui_patterns.yml deleted file mode 100644 index 8d2b4ab8..00000000 --- a/modules/ui_patterns_library/tests/modules/ui_patterns_library_theme_test/ui_patterns_library_theme_test.ui_patterns.yml +++ /dev/null @@ -1,37 +0,0 @@ -simple: - label: 'Simple' - description: 'A simple pattern' - fields: - field: - type: 'string' - label: 'Field' - description: 'Field description' - preview: 'Simple pattern field' - -with_variants: - label: 'With variants' - description: 'Pattern with variants' - variants: - one: - label: 'One' - description: 'First variant' - two: - label: 'Two' - description: 'Second variant' - fields: - field: - type: 'string' - label: 'Field' - description: 'Field description' - preview: 'With variants pattern field' - -with_custom_theme_hook: - theme hook: 'custom_theme_hook' - label: 'With custom theme hook' - description: 'Pattern with custom theme hook.' - fields: - field: - type: 'string' - label: 'Field' - description: 'Field description' - preview: 'Pattern field value' diff --git a/modules/ui_patterns_library/tests/src/FunctionalJavascript/UiPatternsLibraryBadDefinitionTest.php b/modules/ui_patterns_library/tests/src/FunctionalJavascript/UiPatternsLibraryBadDefinitionTest.php deleted file mode 100644 index 3f41c4fa..00000000 --- a/modules/ui_patterns_library/tests/src/FunctionalJavascript/UiPatternsLibraryBadDefinitionTest.php +++ /dev/null @@ -1,46 +0,0 @@ -assertSession(); - - $user = $this->drupalCreateUser(['access patterns page']); - $this->drupalLogin($user); - - drupal_flush_all_caches(); - $this->drupalGet('/patterns'); - - $session->pageTextContains("Pattern 'bad_definition' is skipped because of the following validation error(s):"); - $session->pageTextContains('Validation error on "bad_definition.label": This value should not be null.'); - } - -} diff --git a/modules/ui_patterns_library/tests/src/FunctionalJavascript/UiPatternsLibraryOverviewTest.php b/modules/ui_patterns_library/tests/src/FunctionalJavascript/UiPatternsLibraryOverviewTest.php deleted file mode 100644 index 3188b00b..00000000 --- a/modules/ui_patterns_library/tests/src/FunctionalJavascript/UiPatternsLibraryOverviewTest.php +++ /dev/null @@ -1,214 +0,0 @@ -drupalCreateUser(['access patterns page']); - $this->drupalLogin($user); - } - - /** - * Tests overview page. - */ - public function testOverviewPage() { - $session = $this->assertSession(); - - $this->drupalGet('/patterns'); - - $session->elementContains('css', 'h1', 'Pattern library'); - $session->elementContains('css', 'h2', 'Available patterns'); - - foreach ($this->getExpectedPatterns() as $index => $pattern) { - - // Assert pattern anchor link. - $this->assertListLink($index + 1, $pattern['label'], $pattern['name']); - - // Assert pattern preview. - $this->assertPatternPreview($pattern); - - // Test view single page link. - $session->linkExists("View {$pattern['label']} as stand-alone"); - $link = $this->getSession()->getPage()->findLink("View {$pattern['label']} as stand-alone"); - $this->assertStringContainsString('/patterns/' . $pattern['name'], $link->getAttribute('href')); - } - } - - /** - * Tests overview page. - */ - public function testSinglePages() { - $session = $this->assertSession(); - - foreach ($this->getExpectedPatterns() as $pattern) { - $this->drupalGet('/patterns/' . $pattern['name']); - $session->elementContains('css', 'h1', $pattern['label']); - - // Assert pattern preview. - $this->assertPatternPreview($pattern); - } - } - - /** - * Test that libraries defined locally are loaded correctly. - */ - public function testLocalLibraries() { - $session = $this->assertSession(); - - $this->drupalGet('/patterns/with_local_libraries'); - $session->responseContains('href="/modules/custom/ui_patterns/modules/ui_patterns_library/tests/modules/ui_patterns_library_module_test/templates/with_local_libraries/css/library_one.css'); - $session->responseContains('href="/modules/custom/ui_patterns/modules/ui_patterns_library/tests/modules/ui_patterns_library_module_test/templates/with_local_libraries/css/library_two.css'); - $session->responseContains('src="/modules/custom/ui_patterns/modules/ui_patterns_library/tests/modules/ui_patterns_library_module_test/templates/with_local_libraries/js/library_two_1.js'); - $session->responseContains('src="/modules/custom/ui_patterns/modules/ui_patterns_library/tests/modules/ui_patterns_library_module_test/templates/with_local_libraries/js/library_two_2.js'); - $session->responseContains('src="/core/misc/tabledrag.js'); - } - - /** - * Assert pattern preview display. - * - * @param array $pattern - * Expected pattern. - */ - protected function assertPatternPreview(array $pattern) { - $session = $this->assertSession(); - - // Assert pattern title and description. - $root = '.pattern-preview__' . $pattern['name']; - $session->elementExists('css', $root); - $session->elementContains('css', "$root > h3.pattern-preview__label", $pattern['label']); - $session->elementContains('css', "$root > p.pattern-preview__description", $pattern['description']); - - // Assert metadata block. - $this->assertPatternFields($root, $pattern); - - if (!$pattern['has_variants']) { - // Make sure no variant markup exists. - $session->elementNotExists('css', "$root > fieldset.pattern-preview__preview > .pattern-preview__variants"); - - // Assert preview content when without variants. - $session->elementContains('css', "$root > fieldset.pattern-preview__preview > .pattern-preview__markup", $pattern['preview']); - } - else { - // Assert that variant markup exists. - $session->elementExists('css', "$root > fieldset.pattern-preview__preview > .pattern-preview__variants"); - - // Assert variant meta information and preview. - foreach ($pattern['variants'] as $variant) { - $this->assertPatternVariant($root, $variant); - } - } - } - - /** - * Assert pattern table fields. - * - * @param string $root - * CSS selector of element containing the table. - * @param array $pattern - * Expected pattern. - * - * @throws \Behat\Mink\Exception\ElementHtmlException - */ - protected function assertPatternFields($root, array $pattern) { - $session = $this->assertSession(); - - // Assert table header. - foreach (['Field', 'Label', 'Type', 'Description'] as $index => $item) { - $child = $index + 1; - $session->elementContains('css', "$root > table.pattern-preview__fields > thead > tr > th:nth-child($child)", $item); - } - - // Assert field table rows. - foreach ($pattern['fields'] as $index => $field) { - $child = $index + 1; - $row_root = "$root > table.pattern-preview__fields > tbody > tr:nth-child($child)"; - $session->elementContains('css', "$row_root > td:nth-child(1)", $field['name']); - $session->elementContains('css', "$row_root > td:nth-child(2)", $field['label']); - $session->elementContains('css', "$row_root > td:nth-child(3)", $field['type']); - $session->elementContains('css', "$row_root > td:nth-child(4)", $field['description']); - } - } - - /** - * Assert pattern variant metadata and preview. - * - * @param string $root - * CSS selector of element containing the table. - * @param array $variant - * Variant expected values. - */ - protected function assertPatternVariant($root, array $variant) { - $session = $this->assertSession(); - $name = $variant['meta']['name']; - - // Assert table header. - foreach (['Variant', 'Name', 'Description'] as $index => $item) { - $child = $index + 1; - $session->elementContains('css', "$root table.pattern-preview__variants--$name > thead > tr > th:nth-child($child)", $item); - } - - // Assert variant meta table rows. - $row_root = "$root table.pattern-preview__variants--$name > tbody > tr"; - $session->elementContains('css', "$row_root > td:nth-child(1)", $variant['meta']['name']); - $session->elementContains('css', "$row_root > td:nth-child(2)", $variant['meta']['label']); - $session->elementContains('css', "$row_root > td:nth-child(3)", $variant['meta']['description']); - - // Assert variant preview. - $session->elementContains('css', "$root .pattern-preview__markup--variant_$name", $variant['preview']); - } - - /** - * Assert pattern overview list link. - * - * @param int $index - * Position on list. - * @param string $label - * Pattern label. - * @param string $name - * Pattern machine name. - * - * @throws \Behat\Mink\Exception\ElementHtmlException - */ - protected function assertListLink($index, $label, $name) { - $this->assertSession()->elementContains('css', "ul > li:nth-child($index) > a", $label); - $this->assertSession()->elementAttributeContains('css', "ul > li:nth-child($index) > a", 'href', '#' . $name); - } - - /** - * Get expected patterns. - */ - protected function getExpectedPatterns() { - return Yaml::decode(file_get_contents(__DIR__ . '/../../fixtures/overview-page-patterns.yml')); - } - -} diff --git a/modules/ui_patterns_library/ui_patterns_library.info.yml b/modules/ui_patterns_library/ui_patterns_library.info.yml index 23ad2ff1..151e5c28 100644 --- a/modules/ui_patterns_library/ui_patterns_library.info.yml +++ b/modules/ui_patterns_library/ui_patterns_library.info.yml @@ -1,8 +1,7 @@ -name: UI Patterns Library +name: "UI Patterns Library" type: module -description: Exposed patterns in you modules and themes and display them in a pattern library page. -core: 8.x -core_version_requirement: ^8 || ^9 -package: User interface +description: "Browse UI components in library pages." +core_version_requirement: ^10 +package: "User interface" dependencies: - ui_patterns:ui_patterns diff --git a/modules/ui_patterns_library/ui_patterns_library.links.menu.yml b/modules/ui_patterns_library/ui_patterns_library.links.menu.yml new file mode 100644 index 00000000..49b255f8 --- /dev/null +++ b/modules/ui_patterns_library/ui_patterns_library.links.menu.yml @@ -0,0 +1,5 @@ +ui_patterns_library.overview: + title: 'UI Patterns library' + description: 'Browse components in library pages.' + parent: system.admin_reports + route_name: ui_patterns_library.overview diff --git a/modules/ui_patterns_library/ui_patterns_library.module b/modules/ui_patterns_library/ui_patterns_library.module index 79f767e9..12c5874e 100644 --- a/modules/ui_patterns_library/ui_patterns_library.module +++ b/modules/ui_patterns_library/ui_patterns_library.module @@ -10,17 +10,14 @@ */ function ui_patterns_library_theme() { return [ - 'patterns_overview_page' => [ - 'variables' => ['patterns' => NULL], + 'ui_patterns_overview_page' => [ + 'variables' => ['groups' => NULL], ], - 'patterns_single_page' => [ - 'variables' => ['pattern' => NULL], - ], - 'patterns_meta_information' => [ - 'variables' => ['pattern' => NULL], - ], - 'patterns_variant_meta_information' => [ - 'variables' => ['variant' => NULL], + 'ui_patterns_single_page' => [ + 'variables' => ['component' => NULL], ], + 'ui_patterns_component_metadata' => [ + 'variables' => ['component' => NULL], + ] ]; } diff --git a/modules/ui_patterns_library/ui_patterns_library.routing.yml b/modules/ui_patterns_library/ui_patterns_library.routing.yml index 4960f572..b233b9e9 100644 --- a/modules/ui_patterns_library/ui_patterns_library.routing.yml +++ b/modules/ui_patterns_library/ui_patterns_library.routing.yml @@ -1,14 +1,14 @@ -ui_patterns.patterns.overview: +ui_patterns_library.overview: path: '/patterns' defaults: - _controller: '\Drupal\ui_patterns_library\Controller\PatternsLibraryController::overview' - _title: 'Pattern library' + _controller: '\Drupal\ui_patterns_library\Controller\LibraryController::overview' + _title: 'Components library' requirements: _permission: 'access patterns page' -ui_patterns.patterns.single: +ui_patterns_library.single: path: '/patterns/{name}' defaults: - _controller: '\Drupal\ui_patterns_library\Controller\PatternsLibraryController::single' - _title_callback: '\Drupal\ui_patterns_library\Controller\PatternsLibraryController::title' + _controller: '\Drupal\ui_patterns_library\Controller\LibraryController::single' + _title_callback: '\Drupal\ui_patterns_library\Controller\LibraryController::title' requirements: _permission: 'access patterns page' diff --git a/modules/ui_patterns_library/ui_patterns_library.services.yml b/modules/ui_patterns_library/ui_patterns_library.services.yml index 2a8c750f..6c9176d3 100644 --- a/modules/ui_patterns_library/ui_patterns_library.services.yml +++ b/modules/ui_patterns_library/ui_patterns_library.services.yml @@ -1,5 +1,5 @@ -parameters: - ui_patterns_library.file_extensions: - - ".ui_patterns.yml" - - ".patterns.yml" - - ".pattern.yml" +services: + ui_patterns_library.twig.extension: + class: Drupal\ui_patterns_library\Template\TwigExtension + tags: + - { name: twig.extension } diff --git a/modules/ui_patterns_views/src/Plugin/UiPatterns/Source/ViewRowSource.php b/modules/ui_patterns_views/src/Plugin/UiPatterns/Source/ViewRowSource.php new file mode 100644 index 00000000..4892d5bb --- /dev/null +++ b/modules/ui_patterns_views/src/Plugin/UiPatterns/Source/ViewRowSource.php @@ -0,0 +1,41 @@ +getContextProperty('view'); + // foreach ($view->display_handler->getFieldLabels() as $name => $label) { + // $sources[] = $this->getSourceField($name, $label); + // } + return []; + } + + /** + * + */ + public function defaultConfiguration() { + return []; + } + +} diff --git a/modules/ui_patterns_views/src/Plugin/UiPatterns/Source/ViewRowsSource.php b/modules/ui_patterns_views/src/Plugin/UiPatterns/Source/ViewRowsSource.php new file mode 100644 index 00000000..aa535466 --- /dev/null +++ b/modules/ui_patterns_views/src/Plugin/UiPatterns/Source/ViewRowsSource.php @@ -0,0 +1,37 @@ +getContextProperty('view'); - foreach ($view->display_handler->getFieldLabels() as $name => $label) { - $sources[] = $this->getSourceField($name, $label); - } - return $sources; - } - -} diff --git a/modules/ui_patterns_views/src/Plugin/views/row/Component.php b/modules/ui_patterns_views/src/Plugin/views/row/Component.php new file mode 100644 index 00000000..003a6937 --- /dev/null +++ b/modules/ui_patterns_views/src/Plugin/views/row/Component.php @@ -0,0 +1,25 @@ +patternsManager = $patterns_manager; - $this->sourceManager = $source_manager; - $this->moduleHandler = $module_handler; - } - - /** - * {@inheritdoc} - */ - public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) { - return new static( - $configuration, - $plugin_id, - $plugin_definition, - $container->get('plugin.manager.ui_patterns'), - $container->get('plugin.manager.ui_patterns_source'), - $container->get('module_handler') - ); - } - - /** - * {@inheritdoc} - */ - protected function defineOptions() { - $options = parent::defineOptions(); - $options['hide_empty'] = ['default' => FALSE]; - $options['default_field_elements'] = ['default' => FALSE]; - return $options; - } - - /** - * {@inheritdoc} - */ - public function buildOptionsForm(&$form, FormStateInterface $form_state) { - parent::buildOptionsForm($form, $form_state); - - $form['default_field_elements'] = [ - '#type' => 'checkbox', - '#title' => $this->t('Provide default field wrapper elements'), - '#default_value' => $this->options['default_field_elements'], - '#description' => $this->t('If not checked, fields that are not configured to customize their HTML elements will get no wrappers at all for their field, label and field + label wrappers. You can use this to quickly reduce the amount of markup the view provides by default, at the cost of making it more difficult to apply CSS.'), - ]; - - $form['hide_empty'] = [ - '#type' => 'checkbox', - '#title' => $this->t('Hide empty fields'), - '#default_value' => $this->options['hide_empty'], - '#description' => $this->t('Do not display fields, labels or markup for fields that are empty.'), - ]; - - $context = ['view' => $this->view]; - $this->buildPatternDisplayForm($form, 'views_row', $context, $this->options); - } - - /** - * {@inheritdoc} - */ - public function submitOptionsForm(&$form, FormStateInterface $form_state) { - $settings = $form_state->getValue('row_options'); - self::processFormStateValues($settings); - $form_state->setValue('row_options', $settings); - } - - /** - * Helper function: check for all conditions that make a field visible. - * - * @param \Drupal\views\Plugin\views\field\FieldPluginBase $field - * Field object. - * @param \Drupal\Component\Render\MarkupInterface|null $field_output - * Field output. - * - * @return bool - * TRUE if a field should be visible, FALSE otherwise. - * - * @see template_preprocess_pattern_views_row() - */ - public function isFieldVisible(FieldPluginBase $field, $field_output) { - $empty_value = $field->isValueEmpty($field_output, $field->options['empty_zero']); - $hide_field = !$empty_value || (empty($field->options['hide_empty']) && empty($this->options['hide_empty'])); - $empty = empty($field->options['exclude']) && $hide_field; - return $empty && $this->hasMappingDestination('views_row', $field->field, $this->options); - } - -} diff --git a/modules/ui_patterns_views/src/Plugin/views/style/Component.php b/modules/ui_patterns_views/src/Plugin/views/style/Component.php new file mode 100755 index 00000000..64e24058 --- /dev/null +++ b/modules/ui_patterns_views/src/Plugin/views/style/Component.php @@ -0,0 +1,26 @@ +{{ title }} -

{{ description }}

\ No newline at end of file diff --git a/modules/ui_patterns_views/tests/modules/ui_patterns_views_test/templates/teaser.ui_patterns.yml b/modules/ui_patterns_views/tests/modules/ui_patterns_views_test/templates/teaser.ui_patterns.yml deleted file mode 100644 index 86f7e8a5..00000000 --- a/modules/ui_patterns_views/tests/modules/ui_patterns_views_test/templates/teaser.ui_patterns.yml +++ /dev/null @@ -1,16 +0,0 @@ -teaser: - label: "Teaser" - variants: - default: - label: "Default" - highlighted: - label: "Highlighted" - fields: - title: - type: "text" - label: "Title" - preview: "Title" - description: - type: "text" - label: "Description" - preview: "Description" diff --git a/modules/ui_patterns_views/tests/modules/ui_patterns_views_test/ui_patterns_views_test.info.yml b/modules/ui_patterns_views/tests/modules/ui_patterns_views_test/ui_patterns_views_test.info.yml deleted file mode 100644 index e6954e14..00000000 --- a/modules/ui_patterns_views/tests/modules/ui_patterns_views_test/ui_patterns_views_test.info.yml +++ /dev/null @@ -1,20 +0,0 @@ -name: 'UI Patterns Views Test' -type: module -description: 'Test module for UI Patterns.' -core: 8.x -core_version_requirement: ^8 || ^9 -hidden: true -package: 'User interface' -dependencies: -- ui_patterns -- ui_patterns_views -- ui_patterns_library -- views_ui -- node -- text - -config_devel: - install: - - field.field.node.article.body - - node.type.article - - views.view.articles diff --git a/modules/ui_patterns_views/tests/src/FunctionalJavascript/UiPatternsViewsRenderTest.php b/modules/ui_patterns_views/tests/src/FunctionalJavascript/UiPatternsViewsRenderTest.php deleted file mode 100644 index cea28e8f..00000000 --- a/modules/ui_patterns_views/tests/src/FunctionalJavascript/UiPatternsViewsRenderTest.php +++ /dev/null @@ -1,80 +0,0 @@ -assertSession(); - - $this->enableTwigDebugMode(); - - $user = $this->drupalCreateUser([], NULL, TRUE); - $this->drupalLogin($user); - - $this->drupalCreateNode([ - 'title' => 'Test article', - 'type' => 'article', - ]); - - $this->drupalGet('/articles'); - - // Assert correct variant suggestions. - $suggestions = [ - 'pattern-teaser--variant-default--views-row--articles--page-1.html.twig', - 'pattern-teaser--variant-default--views-row--articles.html.twig', - 'pattern-teaser--variant-default--views-row.html.twig', - - 'pattern-teaser--views-row--articles--page-1.html.twig', - 'pattern-teaser--views-row--articles.html.twig', - 'pattern-teaser--views-row.html.twig', - - 'pattern-teaser--variant-default.html.twig', - 'pattern-teaser.html.twig', - ]; - foreach ($suggestions as $suggestion) { - $assert_session->responseContains($suggestion); - } - - // Test field content is rendered in field group pattern. - $assert_session->elementContains('css', 'h3', 'Test article'); - } - -} diff --git a/modules/ui_patterns_views/tests/src/FunctionalJavascript/UiPatternsViewsSettingsTest.php b/modules/ui_patterns_views/tests/src/FunctionalJavascript/UiPatternsViewsSettingsTest.php deleted file mode 100644 index c0162029..00000000 --- a/modules/ui_patterns_views/tests/src/FunctionalJavascript/UiPatternsViewsSettingsTest.php +++ /dev/null @@ -1,82 +0,0 @@ -getSession()->getPage(); - $assert_session = $this->assertSession(); - - $user = $this->drupalCreateUser([], NULL, TRUE); - $this->drupalLogin($user); - - // Visit Articles views setting page. - $this->drupalGet('/admin/structure/views/view/articles'); - - // Access row style settings. - $page->clickLink('Change settings for this style'); - $assert_session->assertWaitOnAjaxRequest(); - - // Configure row style. - $page->selectFieldOption('Variant', 'Highlighted'); - $page->selectFieldOption('Destination for Content: Title', 'Description'); - - // Submit row style settings. - $page->find('css', '.ui-dialog-buttonpane .form-actions')->pressButton('Apply'); - $assert_session->assertWaitOnAjaxRequest(); - - // Save view. - $page->find('css', '#edit-actions')->pressButton('Save'); - - $view = View::load('articles'); - $settings = $view->getDisplay('default')['display_options']['row']['options']; - - // Assert settings values. - $this->assertEquals($settings['pattern'], 'teaser'); - $this->assertEquals($settings['pattern_variant'], 'highlighted'); - - // Assert mappings. - $this->assertNotEmpty($settings['pattern_mapping'], "Pattern mapping is empty."); - - $mapping = $settings['pattern_mapping']; - $this->assertArrayHasKey('views_row:title', $mapping, 'Mapping not found.'); - $this->assertEquals($mapping['views_row:title']['destination'], 'description', "Mapping not valid."); - } - -} diff --git a/modules/ui_patterns_views/ui_patterns_views.info.yml b/modules/ui_patterns_views/ui_patterns_views.info.yml index f1297fdd..be4033ab 100644 --- a/modules/ui_patterns_views/ui_patterns_views.info.yml +++ b/modules/ui_patterns_views/ui_patterns_views.info.yml @@ -1,9 +1,8 @@ -name: UI Patterns Views +name: "UI Patterns Views" type: module -description: Use patterns as Views templates. -core: 8.x -core_version_requirement: ^8 || ^9 -package: User interface +description: "Use UI components with Views rows and styles plugins." +core_version_requirement: ^10 +package: "User interface" dependencies: - drupal:views - ui_patterns:ui_patterns diff --git a/modules/ui_patterns_views/ui_patterns_views.module b/modules/ui_patterns_views/ui_patterns_views.module deleted file mode 100644 index 58f0b042..00000000 --- a/modules/ui_patterns_views/ui_patterns_views.module +++ /dev/null @@ -1,113 +0,0 @@ - [ - 'variables' => ['view' => NULL, 'options' => [], 'row' => NULL], - ], - ]; -} - -/** - * Preprocess hook. - * - * @param array $variables - * Theme variables. - */ -function template_preprocess_pattern_views_row(array &$variables) { - /** @var \Drupal\views\ResultRow $row */ - /** @var \Drupal\views\ViewExecutable $view */ - /** @var \Drupal\ui_patterns_views\Plugin\views\row\Pattern $row_plugin */ - - $fields = []; - $view = $variables['view']; - $row_plugin = $view->rowPlugin; - $options = $variables['options']; - $row = $variables['row']; - - foreach ($options['pattern_mapping'] as $mapping) { - $field_name = $mapping['source']; - $field = $view->field[$field_name]; - $field_output = $view->style_plugin->getField($row->index, $field_name); - if ($row_plugin->isFieldVisible($field, $field_output)) { - $destination = $row_plugin->getMappingDestination('views_row', $field_name, $options); - $fields[$destination][] = $field_output; - } - } - - $variables['pattern'] = []; - if ($view->preview && !isset($view->element['#embed'])) { - \Drupal::messenger()->addWarning(t("Pattern Views row plugin does not support preview.")); - $variables['pattern'] = ['#type' => 'status_messages']; - } - elseif (!empty($fields)) { - $variables['pattern'] = [ - '#type' => 'pattern', - '#id' => $options['pattern'], - '#fields' => $fields, - '#multiple_sources' => TRUE, - '#variant' => $options['pattern_variant'] ?? '', - ]; - - // Allow default context values to not override those exposed elsewhere. - $variables['pattern']['#context']['type'] = 'views_row'; - $variables['pattern']['#context']['view_name'] = $view->storage->id(); - $variables['pattern']['#context']['display'] = $view->current_display; - $variables['pattern']['#context']['view'] = $view; - $variables['pattern']['#context']['row'] = $row; - } -} - -/** - * Implements hook_ui_patterns_suggestions_alter(). - */ -function ui_patterns_views_ui_patterns_suggestions_alter(array &$suggestions, array $variables, PatternContext $context) { - if ($context->isOfType('views_row')) { - $hook = $variables['theme_hook_original']; - $variant = $variables["variant"] ?? ''; - $view_name = $context->getProperty('view_name'); - $display = $context->getProperty('display'); - - $suggestions[] = $hook . '__views_row'; - $suggestions[] = $hook . '__views_row__' . $view_name; - $suggestions[] = $hook . '__views_row__' . $view_name . '__' . $display; - - if (!empty($variant)) { - $suggestions[] = $hook . '__variant_' . $variant . '__views_row'; - $suggestions[] = $hook . '__variant_' . $variant . '__views_row__' . $view_name; - $suggestions[] = $hook . '__variant_' . $variant . '__views_row__' . $view_name . '__' . $display; - } - } -} - -/** - * Implements hook_ui_patterns_destination_suggestions_alter(). - */ -function ui_patterns_views_ui_patterns_destination_suggestions_alter(array &$suggestions, array $variables, PatternContext $context) { - if ($context->isOfType('views_row')) { - $hook = $variables['theme_hook_original']; - $variant = $variables["variant"] ?? ''; - $view_name = $context->getProperty('view_name'); - $display = $context->getProperty('display'); - $pattern = $context->getProperty('pattern'); - $field = $context->getProperty('field'); - - $suggestions[] = $hook . '__views_row__' . $view_name . '__' . $pattern . '__' . $field; - $suggestions[] = $hook . '__views_row__' . $view_name . '__' . $display . '__' . $pattern . '__' . $field; - - if (!empty($variant)) { - $suggestions[] = $hook . '__variant_' . $variant . '__views_row__' . $view_name . '__' . $pattern . '__' . $field; - $suggestions[] = $hook . '__variant_' . $variant . '__views_row__' . $view_name . '__' . $display . '__' . $pattern . '__' . $field; - } - } -} diff --git a/phpunit.xml.dist b/phpunit.xml.dist deleted file mode 100644 index 5322204e..00000000 --- a/phpunit.xml.dist +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - - - - - - - ./tests/ - ./modules/*/tests/ - - - diff --git a/runner.yml.dist b/runner.yml.dist deleted file mode 100644 index 465d1e8c..00000000 --- a/runner.yml.dist +++ /dev/null @@ -1,34 +0,0 @@ -drupal: - root: "build" - base_url: "http://nginx" - database: - host: "mariadb" - port: "3306" - name: "drupal" - user: "drupal" - password: "drupal" - post_install: - - "./vendor/bin/drush en -y ui_patterns ui_patterns_library ui_patterns_ds ui_patterns_field_group ui_patterns_layouts ui_patterns_views" - - "./vendor/bin/drush en -y coffee config_devel" - - "./vendor/bin/drush en -y page_manager paragraphs panels" - - "./vendor/bin/drush cr" - settings: - settings: - file_scan_ignore_directories: - - "vendor" - - "${drupal.root}" - -selenium: - host: "http://selenium:4444" - browser: "chrome" - -commands: - drupal:site-setup: - - { task: "run", command: "drupal:symlink-project" } - # Generate settings.testing.php, it will be used when running functional tests. - - { task: "process-php", type: "write", config: "drupal.settings", source: "${drupal.root}/sites/default/default.settings.php", destination: "${drupal.root}/sites/default/settings.testing.php", override: true } - - { task: "run", command: "drupal:drush-setup" } - - { task: "run", command: "drupal:settings-setup" } - - { task: "run", command: "setup:phpunit" } - setup:phpunit: - - { task: "process", source: "phpunit.xml.dist", destination: "phpunit.xml" } diff --git a/src/Annotation/PropType.php b/src/Annotation/PropType.php new file mode 100644 index 00000000..14146257 --- /dev/null +++ b/src/Annotation/PropType.php @@ -0,0 +1,53 @@ +definition); - } - - /** - * {@inheritdoc} - */ - #[\ReturnTypeWillChange] - public function offsetGet($offset) { - return $this->definition[$offset] ?? NULL; - } - - /** - * {@inheritdoc} - */ - #[\ReturnTypeWillChange] - public function offsetSet($offset, $value) { - $this->definition[$offset] = $value; - } - - /** - * {@inheritdoc} - */ - #[\ReturnTypeWillChange] - public function offsetUnset($offset) { - unset($this->definition[$offset]); - } - -} diff --git a/src/Definition/PatternDefinition.php b/src/Definition/PatternDefinition.php deleted file mode 100644 index a9e279fb..00000000 --- a/src/Definition/PatternDefinition.php +++ /dev/null @@ -1,649 +0,0 @@ - NULL, - 'label' => NULL, - 'description' => NULL, - 'base path' => NULL, - 'file name' => NULL, - 'use' => NULL, - 'theme hook' => NULL, - 'custom theme hook' => FALSE, - 'template' => NULL, - 'libraries' => [], - 'fields' => [], - 'variants' => [], - 'tags' => [], - 'additional' => [], - 'deriver' => NULL, - 'provider' => NULL, - 'class' => NULL, - ]; - - /** - * PatternDefinition constructor. - */ - public function __construct(array $definition = []) { - foreach ($definition as $name => $value) { - if (array_key_exists($name, $this->definition)) { - $this->definition[$name] = $value; - } - else { - $this->definition['additional'][$name] = $value; - } - } - - $this->id = $this->definition['id']; - $this->setFields($this->definition['fields']); - $this->setVariants($this->definition['variants']); - $this->setThemeHook(self::PATTERN_PREFIX . $this->id()); - - if (!empty($definition['theme hook'])) { - $this->setThemeHook($definition['theme hook']); - $this->definition['custom theme hook'] = TRUE; - } - - if (!$this->hasTemplate()) { - $this->setTemplate(str_replace('_', '-', $this->getThemeHook())); - } - } - - /** - * Return array definition. - * - * @return array - * Array definition. - */ - public function toArray() { - $definition = $this->definition; - foreach ($this->getFields() as $field) { - $definition['fields'][$field->getName()] = $field->toArray(); - } - foreach ($this->getVariants() as $variant) { - $definition['variants'][$variant->getName()] = $variant->toArray(); - } - - return $definition; - } - - /** - * Getter. - * - * @return mixed - * Property value. - */ - public function getLabel() { - return $this->definition['label']; - } - - /** - * Setter. - * - * @param mixed $label - * Property value. - * - * @return $this - */ - public function setLabel($label) { - $this->definition['label'] = $label; - return $this; - } - - /** - * Getter. - * - * @return mixed - * Property value. - */ - public function getBasePath() { - return $this->definition['base path']; - } - - /** - * Setter. - * - * @param mixed $basePath - * Property value. - * - * @return $this - */ - public function setBasePath($basePath) { - $this->definition['base path'] = $basePath; - return $this; - } - - /** - * Getter. - * - * @return mixed - * Property value. - */ - public function getFileName() { - return $this->definition['file name']; - } - - /** - * Setter. - * - * @param mixed $fileName - * Property value. - * - * @return $this - */ - public function setFileName($fileName) { - $this->definition['file name'] = $fileName; - return $this; - } - - /** - * Get Provider property. - * - * @return string - * Property value. - */ - public function getProvider() { - return $this->definition['provider']; - } - - /** - * Setter. - * - * @param mixed $provider - * Property value. - * - * @return $this - */ - public function setProvider($provider) { - $this->definition['provider'] = $provider; - return $this; - } - - /** - * Getter. - * - * @return PatternDefinitionField[] - * Property value. - */ - public function getFields() { - return $this->definition['fields']; - } - - /** - * Get field as options. - * - * @return array - * Fields as select options. - */ - public function getFieldsAsOptions() { - $options = []; - foreach ($this->getFields() as $field) { - $options[$field->getName()] = $field->getLabel(); - } - return $options; - } - - /** - * Setter. - * - * @param array $fields - * Property value. - * - * @return $this - */ - public function setFields(array $fields) { - foreach ($fields as $name => $value) { - $field = $this->getFieldDefinition($name, $value); - $this->definition['fields'][$field->getName()] = $field; - } - return $this; - } - - /** - * Check whereas pattern has variants. - * - * @return bool - * Whereas pattern has variants. - */ - public function hasVariants() { - return !empty($this->definition['variants']); - } - - /** - * Getter. - * - * @return \Drupal\ui_patterns\Definition\PatternDefinitionVariant[] - * Property value. - */ - public function getVariants() { - return $this->definition['variants']; - } - - /** - * Get field as options. - * - * @return array - * Variants as select options. - */ - public function getVariantsAsOptions() { - $options = []; - foreach ($this->getVariants() as $field) { - $options[$field->getName()] = $field->getLabel(); - } - return $options; - } - - /** - * Setter. - * - * @param array $variants - * Property value. - * - * @return $this - */ - public function setVariants(array $variants) { - foreach ($variants as $name => $value) { - $variant = $this->getVariantDefinition($name, $value); - $this->definition['variants'][$variant->getName()] = $variant; - } - return $this; - } - - /** - * Get field. - * - * @param string $name - * Field name. - * - * @return PatternDefinitionField|null - * Definition field. - */ - public function getField($name) { - return $this->hasField($name) ? $this->definition['fields'][$name] : NULL; - } - - /** - * Check whereas field exists. - * - * @param string $name - * Field name. - * - * @return bool - * Whereas field exists - */ - public function hasField($name) { - return isset($this->definition['fields'][$name]); - } - - /** - * Set field. - * - * @param string $name - * Field name. - * @param string $label - * Field label. - * - * @return $this - */ - public function setField($name, $label) { - $this->definition['fields'][$name] = $this->getFieldDefinition($name, $label); - return $this; - } - - /** - * Get variant. - * - * @param string $name - * Field name. - * - * @return PatternDefinitionField|null - * Definition field. - */ - public function getVariant($name) { - return $this->hasVariant($name) ? $this->definition['variants'][$name] : NULL; - } - - /** - * Check whereas variant exists. - * - * @param string $name - * Variant name. - * - * @return bool - * Whereas variant exists - */ - public function hasVariant($name) { - return isset($this->definition['variants'][$name]); - } - - /** - * Set variant. - * - * @param string $name - * Variant name. - * @param string $label - * Variant label. - * - * @return $this - */ - public function setVariant($name, $label) { - $this->definition['variants'][$name] = $this->getVariantDefinition($name, $label); - return $this; - } - - /** - * Getter. - * - * @return string - * Property value. - */ - public function getThemeHook() { - return $this->definition['theme hook']; - } - - /** - * Setter. - * - * @param string $theme_hook - * Property value. - * - * @return $this - */ - public function setThemeHook($theme_hook) { - $this->definition['theme hook'] = $theme_hook; - return $this; - } - - /** - * Getter. - * - * @return string - * Property value. - */ - public function getDescription() { - return $this->definition['description']; - } - - /** - * Setter. - * - * @param string $description - * Property value. - * - * @return $this - */ - public function setDescription($description) { - $this->definition['description'] = $description; - return $this; - } - - /** - * Getter. - * - * @return bool - * Whereas definition uses the "use:" property. - */ - public function hasUse() { - return !empty($this->definition['use']); - } - - /** - * Getter. - * - * @return string - * Property value. - */ - public function getUse() { - return $this->definition['use']; - } - - /** - * Setter. - * - * @param string $use - * Property value. - * - * @return $this - */ - public function setUse($use) { - $this->definition['use'] = $use; - return $this; - } - - /** - * Getter. - * - * @return array - * Property value. - */ - public function getTags() { - return $this->definition['tags']; - } - - /** - * Setter. - * - * @param array $tags - * Property value. - * - * @return $this - */ - public function setTags(array $tags) { - $this->definition['tags'] = $tags; - return $this; - } - - /** - * Getter. - * - * @return mixed - * Property value. - */ - public function hasCustomThemeHook() { - return $this->definition['custom theme hook']; - } - - /** - * Getter. - * - * @return mixed - * Property value. - */ - public function getTemplate() { - return $this->definition['template']; - } - - /** - * Setter. - * - * @param mixed $template - * Property value. - * - * @return $this - */ - public function setTemplate($template) { - $this->definition['template'] = $template; - return $this; - } - - /** - * Getter. - * - * @return bool - * Whereas has template. - */ - public function hasTemplate() { - return !empty($this->definition['template']); - } - - /** - * Getter. - * - * @return mixed - * Property value. - */ - public function getLibraries() { - return $this->definition['libraries']; - } - - /** - * Getter. - * - * @return mixed - * Property value. - */ - public function getLibrariesNames() { - $libraries = []; - foreach ($this->getLibraries() as $library) { - if (is_array($library)) { - $libraries[] = self::LIBRARY_PREFIX . '/' . $this->id() . '.' . key($library); - } - else { - $libraries[] = $library; - } - } - return $libraries; - } - - /** - * Setter. - * - * @param mixed $libraries - * Property value. - * - * @return $this - */ - public function setLibraries($libraries) { - $this->definition['libraries'] = $libraries; - return $this; - } - - /** - * Get Deriver property. - * - * @return mixed - * Property value. - */ - public function getDeriver() { - return $this->definition['deriver']; - } - - /** - * Get Additional property. - * - * @return array - * Property value. - */ - public function getAdditional() { - return $this->definition['additional']; - } - - /** - * Get Class property. - * - * @return string - * Property value. - */ - public function getClass() { - return $this->definition['class']; - } - - /** - * Set Class property. - * - * @param string $class - * Property value. - * - * @return $this - */ - public function setClass($class) { - parent::setClass($class); - $this->definition['class'] = $class; - return $this; - } - - /** - * Set Additional property. - * - * @param array $additional - * Property value. - * - * @return $this - */ - public function setAdditional(array $additional) { - $this->definition['additional'] = $additional; - return $this; - } - - /** - * Set Deriver property. - * - * @param mixed $deriver - * Property value. - * - * @return $this - */ - public function setDeriver($deriver) { - $this->definition['deriver'] = $deriver; - return $this; - } - - /** - * Factory method: create a new field definition. - * - * @param string $name - * Field name. - * @param string $value - * Field value. - * - * @return \Drupal\ui_patterns\Definition\PatternDefinitionField - * Definition instance. - */ - public function getFieldDefinition($name, $value) { - return new PatternDefinitionField($name, $value); - } - - /** - * Factory method: create a new variant definition. - * - * @param string $name - * Variant name. - * @param string $value - * Variant value. - * - * @return \Drupal\ui_patterns\Definition\PatternDefinitionVariant - * Definition instance. - */ - public function getVariantDefinition($name, $value) { - return new PatternDefinitionVariant($name, $value); - } - -} diff --git a/src/Definition/PatternDefinitionField.php b/src/Definition/PatternDefinitionField.php deleted file mode 100644 index 75183b4a..00000000 --- a/src/Definition/PatternDefinitionField.php +++ /dev/null @@ -1,165 +0,0 @@ - NULL, - 'label' => NULL, - 'description' => NULL, - 'type' => NULL, - 'preview' => NULL, - 'escape' => TRUE, - ]; - - /** - * PatternDefinitionField constructor. - */ - public function __construct($name, $value) { - if (is_scalar($value)) { - $this->definition['name'] = is_numeric($name) ? $value : $name; - $this->definition['label'] = $value; - } - else { - $this->definition['name'] = !isset($value['name']) ? $name : $value['name']; - $this->definition['label'] = $value['label']; - $this->definition = $value + $this->definition; - } - } - - /** - * Return array definition. - * - * @return array - * Array definition. - */ - public function toArray() { - return $this->definition; - } - - /** - * Get Name property. - * - * @return mixed - * Property value. - */ - public function getName() { - return $this->definition['name']; - } - - /** - * Get Label property. - * - * @return mixed - * Property value. - */ - public function getLabel() { - return $this->definition['label']; - } - - /** - * Get Description property. - * - * @return string - * Property value. - */ - public function getDescription() { - return $this->definition['description']; - } - - /** - * Set Description property. - * - * @param string $description - * Property value. - * - * @return $this - */ - public function setDescription($description) { - $this->definition['description'] = $description; - return $this; - } - - /** - * Get Type property. - * - * @return string - * Property value. - */ - public function getType() { - return $this->definition['type']; - } - - /** - * Set Type property. - * - * @param string $type - * Property value. - * - * @return $this - */ - public function setType($type) { - $this->definition['type'] = $type; - return $this; - } - - /** - * Get Preview property. - * - * @return mixed - * Property value. - */ - public function getPreview() { - return $this->definition['preview']; - } - - /** - * Set Preview property. - * - * @param mixed $preview - * Property value. - * - * @return $this - */ - public function setPreview($preview) { - $this->definition['preview'] = $preview; - return $this; - } - - /** - * Get Escape property. - * - * @return bool - * Property value. - */ - public function getEscape() { - return $this->definition['escape']; - } - - /** - * Set Escape property. - * - * @param bool $escape - * Property value. - * - * @return $this - */ - public function setEscape($escape) { - $this->definition['escape'] = $escape; - return $this; - } - -} diff --git a/src/Definition/PatternDefinitionVariant.php b/src/Definition/PatternDefinitionVariant.php deleted file mode 100644 index b17cabda..00000000 --- a/src/Definition/PatternDefinitionVariant.php +++ /dev/null @@ -1,93 +0,0 @@ - NULL, - 'label' => NULL, - 'description' => NULL, - ]; - - /** - * PatternDefinitionVariant constructor. - */ - public function __construct($name, $value) { - if (is_scalar($value)) { - $this->definition['name'] = is_numeric($name) ? $value : $name; - $this->definition['label'] = $value; - } - else { - $this->definition['name'] = !isset($value['name']) ? $name : $value['name']; - $this->definition['label'] = $value['label']; - $this->definition = $value + $this->definition; - } - } - - /** - * Return array definition. - * - * @return array - * Array definition. - */ - public function toArray() { - return $this->definition; - } - - /** - * Get Name property. - * - * @return mixed - * Property value. - */ - public function getName() { - return $this->definition['name']; - } - - /** - * Get Label property. - * - * @return mixed - * Property value. - */ - public function getLabel() { - return $this->definition['label']; - } - - /** - * Get Description property. - * - * @return string - * Property value. - */ - public function getDescription() { - return $this->definition['description']; - } - - /** - * Set Description property. - * - * @param string $description - * Property value. - * - * @return $this - */ - public function setDescription($description) { - $this->definition['description'] = $description; - return $this; - } - -} diff --git a/src/Definition/PatternSourceField.php b/src/Definition/PatternSourceField.php deleted file mode 100644 index 16cfd825..00000000 --- a/src/Definition/PatternSourceField.php +++ /dev/null @@ -1,154 +0,0 @@ -fieldName = $field_name; - $this->fieldLabel = $field_label; - $this->pluginId = $plugin_id; - $this->pluginLabel = $plugin_label; - } - - /** - * Get FieldName property. - * - * @return string - * Property value. - */ - public function getFieldName() { - return $this->fieldName; - } - - /** - * Set FieldName property. - * - * @param string $fieldName - * Property value. - * - * @return $this - */ - public function setFieldName($fieldName) { - $this->fieldName = $fieldName; - return $this; - } - - /** - * Get FieldLabel property. - * - * @return string - * Property value. - */ - public function getFieldLabel() { - return $this->fieldLabel; - } - - /** - * Set FieldLabel property. - * - * @param string $fieldLabel - * Property value. - * - * @return $this - */ - public function setFieldLabel($fieldLabel) { - $this->fieldLabel = $fieldLabel; - return $this; - } - - /** - * Get Plugin property. - * - * @return string - * Property value. - */ - public function getPluginId() { - return $this->pluginId; - } - - /** - * Set Plugin property. - * - * @param string $pluginId - * Property value. - * - * @return $this - */ - public function setPluginId($pluginId) { - $this->pluginId = $pluginId; - return $this; - } - - /** - * Get PluginLabel property. - * - * @return string - * Property value. - */ - public function getPluginLabel() { - return $this->pluginLabel; - } - - /** - * Set PluginLabel property. - * - * @param string $pluginLabel - * Property value. - * - * @return $this - */ - public function setPluginLabel($pluginLabel) { - $this->pluginLabel = $pluginLabel; - return $this; - } - - /** - * Get unique field key. - * - * @return string - * Field key. - */ - public function getFieldKey() { - return $this->getPluginId() . self::FIELD_KEY_SEPARATOR . $this->getFieldName(); - } - -} diff --git a/src/Element/ComponentElement.php b/src/Element/ComponentElement.php new file mode 100644 index 00000000..11971dc4 --- /dev/null +++ b/src/Element/ComponentElement.php @@ -0,0 +1,85 @@ + $this->doTrustedCallback( + $callback, + [$carry], + '%s is not trusted', + ), + $props + ); + $inline_template = $this->generateComponentTemplate( + $element['#component'], + $element['#slots'], + $element['#slotsAlter'], + $props, + ); + $element['inline-template'] = [ + '#type' => 'inline_template', + '#template' => $inline_template, + '#context' => $props, + ]; + return $element; + } + + /** + * {@inheritdoc} + * + * Related SDC issue: https://www.drupal.org/project/drupal/issues/3391702 + */ + private function generateComponentTemplate( + string $id, + array $slots, + array $slots_alter_callbacks, + array &$context, + ): string { + $template = '{# This template was dynamically generated by sdc #}' . PHP_EOL; + $template .= sprintf('{%% embed \'%s\' %%}', $id); + $template .= PHP_EOL; + foreach ($slots as $slot_name => $slot_value) { + if (!Utilities::isRenderArray($slot_value) && \is_scalar($slot_value)) { + $slot_value = [ + "#plain_text" => (string) $slot_value, + ]; + } + $context[$slot_name] = array_reduce( + $slots_alter_callbacks, + fn(array $carry, callable $callback) => $this->doTrustedCallback( + $callback, + [$carry, $context], + '%s is not trusted', + ), + $slot_value + ); + $template .= " {% block $slot_name %}" . PHP_EOL + . " {{ $slot_name }}" . PHP_EOL + . " {% endblock %}" . PHP_EOL; + } + $template .= '{% endembed %}' . PHP_EOL; + return $template; + } + +} diff --git a/src/Element/Pattern.php b/src/Element/Pattern.php deleted file mode 100644 index e04ea99a..00000000 --- a/src/Element/Pattern.php +++ /dev/null @@ -1,238 +0,0 @@ - FALSE, - '#multiple_sources' => FALSE, - '#pre_render' => [ - [$class, 'processContext'], - [$class, 'processRenderArray'], - [$class, 'processLibraries'], - [$class, 'processMultipleSources'], - [$class, 'processFields'], - [$class, 'ensureVariant'], - [$class, 'processUse'], - ], - ]; - } - - /** - * Process render array. - * - * @param array $element - * Render array. - * - * @return array - * Render array. - */ - public static function processRenderArray(array $element) { - $element['#theme'] = UiPatterns::getPatternDefinition($element['#id'])->getThemeHook(); - - if (isset($element['#attributes']) && !empty($element['#attributes']) && is_array($element['#attributes'])) { - $element['#attributes'] = new Attribute($element['#attributes']); - } - else { - $element['#attributes'] = new Attribute(); - } - - unset($element['#type']); - return $element; - } - - /** - * Process libraries. - * - * @param array $element - * Render array. - * - * @return array - * Render array. - */ - public static function processLibraries(array $element) { - foreach (UiPatterns::getPatternDefinition($element['#id'])->getLibrariesNames() as $library) { - $element['#attached']['library'][] = $library; - } - - return $element; - } - - /** - * Process fields. - * - * @param array $element - * Render array. - * - * @return array - * Render array. - */ - public static function processFields(array $element) { - // Make sure we don't render anything in case fields are empty. - if (self::hasFields($element)) { - $fields = $element['#fields']; - unset($element['#fields']); - - foreach ($fields as $name => $field) { - $key = '#' . $name; - $element[$key] = $field; - } - } - else { - $element['#markup'] = ''; - } - - return $element; - } - - /** - * Make sure that we never pass through a value that is not a string. - * - * This would prevent accidental assignments of a render array as variant - * which would break hook_ui_patterns_suggestions_alter(). - * - * @param array $element - * Render array. - * - * @return array - * Render array. - */ - public static function ensureVariant(array $element) { - if (!isset($element['#variant']) || !is_string($element['#variant'])) { - $element['#variant'] = ''; - } - - return $element; - } - - /** - * Process use property. - * - * @param array $element - * Render array. - * - * @return array - * Render array. - */ - public static function processUse(array $element) { - $definition = UiPatterns::getPatternDefinition($element['#id']); - if ($definition->hasUse()) { - $element['#use'] = $definition->getUse(); - } - - return $element; - } - - /** - * Process fields. - * - * @param array $element - * Render array. - * - * @return array - * Render array. - */ - public static function processMultipleSources(array $element) { - // Make sure we don't render anything in case fields are empty. - if (self::hasFields($element) && self::hasMultipleSources($element)) { - foreach ($element['#fields'] as $name => $field) { - // This guarantees backward compatibility: single sources be simple. - $element['#fields'][$name] = reset($field); - if (count($field) > 1) { - /** @var \Drupal\ui_patterns\Element\PatternContext $context */ - $context = $element['#context']; - $context->setProperty('pattern', $element['#id']); - $context->setProperty('field', $name); - - // Render multiple sources with "patterns_destination" template. - $element['#fields'][$name] = [ - '#sources' => $field, - '#context' => $context, - '#theme' => 'patterns_destination', - ]; - } - } - } - return $element; - } - - /** - * Process context. - * - * @param array $element - * Render array. - * - * @return array - * Render array. - * - * @throws \Drupal\ui_patterns\Exception\PatternRenderException - * Throws an exception if no context type is specified. - */ - public static function processContext(array $element) { - - if (self::hasValidContext($element)) { - $context = $element['#context']; - $element['#context'] = new PatternContext($context['type'], $element['#context']); - } - else { - $element['#context'] = new PatternContext('empty'); - } - - return $element; - } - - /** - * Whereas pattern has field or not. - * - * @param array $element - * Render array. - * - * @return bool - * TRUE or FALSE. - */ - public static function hasFields(array $element) { - return isset($element['#fields']) && !empty($element['#fields']) && is_array($element['#fields']); - } - - /** - * Whereas pattern fields can accept multiple sources. - * - * @param array $element - * Render array. - * - * @return bool - * TRUE or FALSE. - */ - public static function hasMultipleSources(array $element) { - return isset($element['#multiple_sources']) && $element['#multiple_sources'] === TRUE; - } - - /** - * Whereas pattern has a valid context, i.e. context "type" is set. - * - * @param array $element - * Render array. - * - * @return bool - * TRUE or FALSE. - */ - public static function hasValidContext(array $element) { - return isset($element['#context']) && is_array($element['#context']) && !empty($element['#context']['type']); - } - -} diff --git a/src/Element/PatternContext.php b/src/Element/PatternContext.php deleted file mode 100644 index aa987501..00000000 --- a/src/Element/PatternContext.php +++ /dev/null @@ -1,87 +0,0 @@ -type = $type; - unset($values['type']); - foreach ($values as $name => $value) { - $this->setProperty($name, $value); - } - } - - /** - * Get pattern context property. - * - * @return mixed - * Property value. - */ - public function getProperty($name) { - return $this->properties[$name] ?? NULL; - } - - /** - * Set pattern context property. - * - * @param string $name - * Property name. - * @param mixed $value - * Property value. - */ - public function setProperty($name, $value) { - $this->properties[$name] = $value; - } - - /** - * Check whereas the current context is of a given type. - * - * @param string $type - * Type string. - * - * @return bool - * Whereas the current context is of a given type. - */ - public function isOfType($type) { - return $this->type == $type; - } - - /** - * Get context type. - * - * @return string - * Context type. - */ - public function getType() { - return $this->type; - } - -} diff --git a/src/Element/PatternPreview.php b/src/Element/PatternPreview.php deleted file mode 100644 index cbd15d9e..00000000 --- a/src/Element/PatternPreview.php +++ /dev/null @@ -1,85 +0,0 @@ -getFields() as $field) { - $preview = $field->getPreview(); - // Some fields are used as Twig array keys and don't need escaping. - if ($field->getEscape()) { - // The examples are not user submitted and are safe markup. - $preview = self::getPreviewMarkup($preview); - } - - $fields[$field->getName()] = $preview; - } - - if (isset($definition['additional']['attributes'])) { - $fields['attributes'] = $definition['extra']['attributes']; - } - $element['#fields'] = $fields; - - return parent::processFields($element); - } - - /** - * Make previews markup safe. - * - * @param string|string[] $preview - * The preview, may be a string or an array. - * - * @return array|\Drupal\Component\Render\MarkupInterface|string - * Preview safe markup. - */ - public static function getPreviewMarkup($preview) { - if (is_array($preview)) { - $rendered = []; - // If preview is a render array add hashes to keys. - $hash_keys = array_key_exists('theme', $preview) || array_key_exists('type', $preview); - foreach ($preview as $key => $value) { - $key = $hash_keys ? '#' . $key : $key; - if (is_array($value)) { - // Process array values recursively. - $value = self::getPreviewMarkup($value); - } - $rendered[$key] = $value; - } - - return $rendered; - } - - return Markup::create($preview); - } - - /** - * {@inheritdoc} - */ - public static function processContext(array $element) { - $element['#context'] = new PatternContext('preview'); - - return $element; - } - -} diff --git a/src/Exception/PatternDefinitionException.php b/src/Exception/PatternDefinitionException.php deleted file mode 100644 index 33f77978..00000000 --- a/src/Exception/PatternDefinitionException.php +++ /dev/null @@ -1,14 +0,0 @@ - 'select', - '#empty_value' => '_none', - '#title' => $this->t('Pattern'), - '#options' => $this->patternsManager->getPatternsOptions(), - '#default_value' => $configuration['pattern'] ?? NULL, - '#required' => TRUE, - '#attributes' => ['id' => 'patterns-select'], - ]; - $form['variants'] = ['#type' => 'container']; - - /** @var \Drupal\ui_patterns\Definition\PatternDefinition $definition */ - foreach ($this->patternsManager->getDefinitions() as $pattern_id => $definition) { - if ($definition->hasVariants()) { - $form['variants'][$pattern_id] = [ - '#type' => 'select', - '#title' => $this->t('Variant'), - '#options' => $definition->getVariantsAsOptions(), - '#default_value' => $configuration['pattern_variant'] ?? NULL, - '#weight' => 0, - '#states' => [ - 'visible' => [ - 'select[id="patterns-select"]' => ['value' => $pattern_id], - ], - ], - ]; - } - $form['pattern_mapping'][$pattern_id] = [ - '#type' => 'container', - '#weight' => 1, - '#states' => [ - 'visible' => [ - 'select[id="patterns-select"]' => ['value' => $pattern_id], - ], - ], - 'settings' => $this->getMappingForm($pattern_id, $tag, $context, $configuration), - ]; - } - - $this->moduleHandler->alter('ui_patterns_display_settings_form', $form, $configuration); - } - - /** - * Get mapping form. - * - * @param string $pattern_id - * Pattern ID for which to print the mapping form for. - * @param string $tag - * Source field plugin tag. - * @param array $context - * Plugin context. - * @param array $configuration - * Default configuration coming form the host form. - * - * @return array - * Mapping form. - */ - public function getMappingForm($pattern_id, $tag, array $context, array $configuration) { - /** @var \Drupal\ui_patterns\Definition\PatternDefinition $pattern */ - $pattern = $this->patternsManager->getDefinition($pattern_id); - - $elements = [ - '#type' => 'table', - '#header' => [ - $this->t('Source'), - $this->t('Plugin'), - $this->t('Destination'), - $this->t('Weight'), - ], - ]; - $elements['#tabledrag'][] = [ - 'action' => 'order', - 'relationship' => 'sibling', - 'group' => 'field-weight', - ]; - - $destinations = ['_hidden' => $this->t('- Hidden -')] + $pattern->getFieldsAsOptions(); - - $fields = []; - foreach ($this->sourceManager->getFieldsByTag($tag, $context) as $field_name => $field) { - $weight = (int) $this->getDefaultValue($configuration, $field_name, 'weight'); - $fields[$field_name] = [ - 'info' => [ - '#plain_text' => $field->getFieldLabel(), - ], - 'plugin' => [ - '#plain_text' => $field->getPluginLabel(), - ], - 'destination' => [ - '#type' => 'select', - '#title' => $this->t('Destination for @field', ['@field' => $field->getFieldLabel()]), - '#title_display' => 'invisible', - '#default_value' => $this->getDefaultValue($configuration, $field_name, 'destination'), - '#options' => $destinations, - ], - 'weight' => [ - '#type' => 'weight', - '#default_value' => $weight, - '#delta' => 20, - '#title' => $this->t('Weight for @field field', ['@field' => $field->getFieldLabel()]), - '#title_display' => 'invisible', - '#attributes' => [ - 'class' => ['field-weight'], - ], - ], - '#attributes' => [ - 'class' => ['draggable'], - ], - '#weight' => $weight, - ]; - } - - uasort($fields, [SortArray::class, 'sortByWeightProperty']); - return array_merge($elements, $fields); - } - - /** - * Normalize settings coming from a form submission. - * - * @param array $settings - * Pattern display form values array. - */ - public static function processFormStateValues(array &$settings) { - if (isset($settings['variants']) && isset($settings['variants'][$settings['pattern']])) { - $settings['pattern_variant'] = $settings['variants'][$settings['pattern']]; - unset($settings['variants']); - } - - // Normalize only when necessary. - if (isset($settings['pattern_mapping'][$settings['pattern']]['settings'])) { - $settings['pattern_mapping'] = $settings['pattern_mapping'][$settings['pattern']]['settings']; - - // Process fields and filter out the hidden ones. - foreach ($settings['pattern_mapping'] as $key => $setting) { - if ($setting['destination'] == '_hidden') { - unset($settings['pattern_mapping'][$key]); - } - else { - [$plugin, $source] = explode(PatternSourceBase::DERIVATIVE_SEPARATOR, $key, 2); - $settings['pattern_mapping'][$key]['plugin'] = $plugin; - $settings['pattern_mapping'][$key]['source'] = $source; - } - } - - // Normalize weights. - $weight = 0; - uasort($settings['pattern_mapping'], [ - SortArray::class, - 'sortByWeightElement', - ]); - foreach ($settings['pattern_mapping'] as $key => $setting) { - $settings['pattern_mapping'][$key]['weight'] = $weight++; - } - } - } - - /** - * Helper function: return mapping destination given plugin id and field name. - * - * @param string $plugin - * Current plugin ID. - * @param string $source - * Source field name. - * @param array $settings - * Setting array. - * - * @return string|null - * Destination field or NULL if none found. - */ - public function getMappingDestination($plugin, $source, array $settings) { - $mapping_id = $plugin . PatternSourceBase::DERIVATIVE_SEPARATOR . $source; - if (isset($settings['pattern_mapping'][$mapping_id])) { - return $settings['pattern_mapping'][$mapping_id]['destination']; - } - return NULL; - } - - /** - * Helper function: check if given source field has mapping destination. - * - * @param string $plugin - * Current plugin ID. - * @param string $source - * Source field name. - * @param array $settings - * Setting array. - * - * @return bool - * TRUE if source has destination field, FALSE otherwise. - */ - public function hasMappingDestination($plugin, $source, array $settings) { - return $this->getMappingDestination($plugin, $source, $settings) !== NULL; - } - - /** - * Helper function: get default value. - * - * @param array $configuration - * Configuration. - * @param string $field_name - * Field name. - * @param string $value - * Value name. - * - * @return string - * Field property value. - */ - protected function getDefaultValue(array $configuration, $field_name, $value) { - if (isset($configuration['pattern_mapping'][$field_name][$value])) { - return $configuration['pattern_mapping'][$field_name][$value]; - } - return NULL; - } - -} diff --git a/src/Form/UiPatternsFormBuilderTrait.php b/src/Form/UiPatternsFormBuilderTrait.php new file mode 100644 index 00000000..9551a0d5 --- /dev/null +++ b/src/Form/UiPatternsFormBuilderTrait.php @@ -0,0 +1,142 @@ + "", + "variant_id" => "", + "slots" => [], + "props" => [], + ]; + } + + /** + * Build the complete form. + * + * @param Drupal\sdc\Plugin\Component $component + * The component plugin. + */ + protected function buildComponentForm(FormStateInterface $form_state, Component $component, array $context): array { + return [ + "component_id" => $this->buildComponentSelectorForm(), + "variant_id" => $this->buildVariantSelectorForm($form_state, $component), + "slots" => $this->buildSlotsForm($form_state, $component, $context), + "forms" => $this->buildPropsForm($form_state, $component, $context), + ]; + } + + /** + * Build components selector widget. + */ + protected function buildComponentSelectorForm(): array { + $components = \Drupal::service("plugin.manager.sdc")->getDefinitions(); + // @todo getGroupedDefinitions? + $options = []; + foreach ($components as $component_id => $component) { + $options[$component_id] = $component["name"]; + } + return [ + "#type" => "select", + "#title" => t("Component"), + "#options" => $options, + ]; + } + + /** + * + */ + protected function buildVariantSelectorForm(FormStateInterface $form_state, Component $component): array { + $definition = $component->getPluginDefinition(); + if (!isset($definition["variants"])) { + return []; + } + $options = []; + foreach ($definition["variants"] as $variant_id => $variant) { + $options[$variant_id] = $variant["title"]; + } + return [ + "#type" => "select", + "#title" => t("Variant"), + "#options" => $options, + ]; + } + + /** + * + */ + protected function buildSlotsForm(FormStateInterface $form_state, Component $component, array $context): array { + return []; + } + + /** + * + */ + protected function buildPropsForm(FormStateInterface $form_state, Component $component, array $context): array { + $sub_sources_form_value = $context['form_values']; + $form = []; + $sub_sources = []; + foreach ($component->metadata->schema['properties'] as $prop_id => $prop) { + $sources = $prop['ui_patterns']['source']; + if (count($sources) == 1) { + /** @var \Drupal\ui_patterns\SourcePluginBase $default_source */ + $default_source = current($sources); + $configuration = $default_source->getConfiguration(); + if (isset($sub_sources_form_value[$prop_id])) { + $configuration['form_value'] = $sub_sources_form_value[$prop_id]; + $default_source->setConfiguration($configuration); + } + $form[$prop_id] = $default_source->buildConfigurationForm($form, $form_state); + $sub_sources[$prop_id] = $default_source; + } + elseif (count($sources) > 1) { + $options = []; + foreach ($sources as $source) { + $options[$source->getPluginId()] = $source->label(); + } + $form[$prop_id] = [ + "#type" => "select", + "#title" => $prop["title"], + "#options" => $options, + ]; + // @todo dynamically load source form on select. + // @todo $sub_sources? + } + } + if (!$form_state->has('sub_sources')) { + $form_state->set('sub_sources', $sub_sources); + } + return $form; + } + + /** + * + */ + protected function submitComponentForm($form, FormStateInterface $form_state, array $context):array { + $sub_sources = $form_state->get('sub_sources'); + $sub_values = []; + foreach ($sub_sources as $prop_id => $sub_source) { + $sub_source->submitConfigurationForm($form['ui_patterns'][$prop_id], $form_state); + $sub_values[$prop_id] = $sub_source->getConfiguration()['form_value']; + } + return $sub_values; + } + +} diff --git a/src/Plugin/Deriver/AbstractPatternsDeriver.php b/src/Plugin/Deriver/AbstractPatternsDeriver.php deleted file mode 100644 index f8e84b94..00000000 --- a/src/Plugin/Deriver/AbstractPatternsDeriver.php +++ /dev/null @@ -1,112 +0,0 @@ -basePluginId = $base_plugin_id; - $this->typedDataManager = $typed_data_manager; - $this->messenger = $messenger; - } - - /** - * {@inheritdoc} - */ - public static function create(ContainerInterface $container, $base_plugin_id) { - return new static( - $base_plugin_id, - $container->get('typed_data_manager'), - $container->get('messenger') - ); - } - - /** - * {@inheritdoc} - */ - public function getDerivativeDefinitions($base_plugin_definition) { - foreach ($this->getPatterns() as $pattern) { - $pattern->setDeriver($base_plugin_definition['deriver']); - $pattern->setClass($base_plugin_definition['class']); - if ($this->isValidPatternDefinition($pattern)) { - $this->derivatives[$pattern->id()] = $pattern; - } - } - return $this->derivatives; - } - - /** - * Get pattern data object. - * - * @param array $definition - * Pattern definition array. - * - * @return \Drupal\ui_patterns\Definition\PatternDefinition - * Pattern definition object. - */ - protected function getPatternDefinition(array $definition = []) { - return new PatternDefinition($definition); - } - - /** - * Validate pattern definition. - * - * @param \Drupal\ui_patterns\Definition\PatternDefinition $definition - * Pattern definition. - * - * @return bool - * Whereas current pattern definition is valid or not. - */ - protected function isValidPatternDefinition(PatternDefinition $definition) { - $data_definition = PatternDataDefinition::create(); - $violations = $this->typedDataManager->create($data_definition, $definition->toArray())->validate(); - if ($violations->count()) { - /** @var \Symfony\Component\Validator\ConstraintViolation $violation */ - $this->messenger->addError($this->t("Pattern ':id' is skipped because of the following validation error(s):", [':id' => $definition->id()])); - foreach ($violations as $violation) { - $message = $this->t('Validation error on ":id.:property": :message', [ - ':id' => $definition->id(), - ':property' => $violation->getPropertyPath(), - ':message' => $violation->getMessage(), - ]); - $this->messenger->addError($message); - } - return FALSE; - } - return TRUE; - } - -} diff --git a/src/Plugin/Deriver/AbstractYamlPatternsDeriver.php b/src/Plugin/Deriver/AbstractYamlPatternsDeriver.php deleted file mode 100644 index 98db5032..00000000 --- a/src/Plugin/Deriver/AbstractYamlPatternsDeriver.php +++ /dev/null @@ -1,91 +0,0 @@ -fileSystem = $file_system; - } - - /** - * {@inheritdoc} - */ - public static function create(ContainerInterface $container, $base_plugin_id) { - return new static( - $base_plugin_id, - $container->get('typed_data_manager'), - $container->get('messenger'), - $container->get('file_system') - ); - } - - /** - * {@inheritdoc} - */ - public function fileScanDirectory($directory) { - if (!is_dir($directory)) { - return []; - } - $options = ['nomask' => $this->getNoMask()]; - $extensions = $this->getFileExtensions(); - $extensions = array_map('preg_quote', $extensions); - $extensions = implode('|', $extensions); - $files = $this->fileSystem->scanDirectory($directory, "/{$extensions}$/", $options); - // In different file systems order of files in a folder can be different - // that can break tests. So let's sort them alphabetically manually. - ksort($files); - return $files; - } - - /** - * Returns a regular expression for directories to be excluded in a file scan. - * - * @return string - * Regular expression. - */ - protected function getNoMask() { - $ignore = Settings::get('file_scan_ignore_directories', []); - // We add 'tests' directory to the ones found in settings. - $ignore[] = 'tests'; - array_walk($ignore, function (&$value) { - $value = preg_quote($value, '/'); - }); - return '/^' . implode('|', $ignore) . '$/'; - } - -} diff --git a/src/Plugin/Deriver/PatternsDeriverInterface.php b/src/Plugin/Deriver/PatternsDeriverInterface.php deleted file mode 100644 index bf3f4aff..00000000 --- a/src/Plugin/Deriver/PatternsDeriverInterface.php +++ /dev/null @@ -1,20 +0,0 @@ -root = $root; - $this->moduleHandler = $module_handler; - } - - /** - * {@inheritdoc} - */ - public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) { - return new static( - $configuration, - $plugin_id, - $plugin_definition, - $container->get('app.root'), - $container->get('module_handler') - ); - } - - /** - * {@inheritdoc} - */ - public function getThemeImplementation() { - $definition = $this->getPluginDefinition(); - $item = []; - $item += $this->processVariables($definition); - $item += $this->processUseProperty($definition); - return [ - $definition['theme hook'] => $item, - ]; - } - - /** - * {@inheritdoc} - */ - public function getLibraryDefinitions() { - // @codingStandardsIgnoreStart - $libraries = []; - $definition = $this->getPluginDefinition(); - - // Get only locally defined libraries. - $items = array_filter($definition['libraries'], function ($library) { - return is_array($library); - }); - - // Attach pattern base path to assets. - if (!empty($definition['base path'])) { - $base_path = str_replace($this->root, '', $definition['base path']); - $this->processLibraries($items, $base_path); - } - - // Produce final libraries array. - $id = $definition['id']; - array_walk($items, function ($value) use (&$libraries, $id) { - $libraries[$id . '.' . key($value)] = reset($value); - }); - - // @codingStandardsIgnoreEnd - return $libraries; - } - - /** - * Process libraries. - * - * @param array|string $libraries - * List of dependencies or "dependencies:" root property. - * @param string $base_path - * Pattern base path. - * @param string $parent - * Item parent set in previous recursive iteration, if any. - */ - protected function processLibraries(&$libraries, $base_path, $parent = '') { - if (!is_string($libraries)) { - $parents = ['js', 'base', 'layout', 'component', 'state', 'theme']; - $_libraries = $libraries; - foreach ($_libraries as $name => $values) { - $is_asset = in_array($parent, $parents, TRUE); - $is_external = isset($values['type']) && $values['type'] == 'external'; - if ($is_asset && !$is_external) { - $libraries[$base_path . DIRECTORY_SEPARATOR . $name] = $values; - unset($libraries[$name]); - } - elseif (!$is_asset) { - $this->processLibraries($libraries[$name], $base_path, $name); - } - } - } - } - - /** - * Process 'use' definition property. - * - * @param \Drupal\ui_patterns\Definition\PatternDefinition $definition - * Pattern definition array. - * - * @return array - * Processed hook definition portion. - */ - protected function processUseProperty(PatternDefinition $definition) { - $return = []; - if ($definition->hasUse()) { - $return = [ - 'path' => $this->moduleHandler->getModule('ui_patterns')->getPath() . '/templates', - 'template' => 'patterns-use-wrapper', - ]; - } - return $return; - } - - /** - * Process theme variables. - * - * @param \Drupal\ui_patterns\Definition\PatternDefinition $definition - * Pattern definition array. - * - * @return array - * Processed hook definition portion. - */ - protected function processVariables(PatternDefinition $definition) { - $return = []; - foreach ($definition->getFields() as $field) { - $return['variables'][$field->getName()] = NULL; - } - $return['variables']['attributes'] = []; - $return['variables']['context'] = []; - $return['variables']['variant'] = ''; - $return['variables']['use'] = ''; - return $return; - } - -} diff --git a/src/Plugin/PatternInterface.php b/src/Plugin/PatternInterface.php deleted file mode 100644 index df5ceff8..00000000 --- a/src/Plugin/PatternInterface.php +++ /dev/null @@ -1,32 +0,0 @@ -pluginDefinition['id'], $this->pluginDefinition['label']); - } - - /** - * {@inheritdoc} - */ - public function getConfiguration() { - return $this->configuration; - } - - /** - * {@inheritdoc} - */ - public function setConfiguration(array $configuration) { - $this->configuration = $configuration; - } - - /** - * {@inheritdoc} - */ - public function defaultConfiguration() { - return [ - 'context' => [], - ]; - } - - /** - * {@inheritdoc} - */ - public function getContextProperty($name) { - $configuration = $this->getConfiguration(); - if (isset($configuration['context'][$name]) && !empty($configuration['context'][$name])) { - return $configuration['context'][$name]; - } - $reflection = new \ReflectionClass($this); - $message = sprintf("Context property '%s' from %s is missing or empty.", $name, $reflection->name); - throw new PluginException($message); - } - -} diff --git a/src/Plugin/PatternSourceInterface.php b/src/Plugin/PatternSourceInterface.php deleted file mode 100644 index a98016cc..00000000 --- a/src/Plugin/PatternSourceInterface.php +++ /dev/null @@ -1,42 +0,0 @@ -get('module_handler'), + $container->get('entity_type.manager'), + ); + /** @var \Drupal\Core\StringTranslation\TranslationInterface $translation */ + $translation = $container->get('string_translation'); + $plugin->setStringTranslation($translation); + $plugin->breadcrumbManager = $container->get('breadcrumb'); + $plugin->routeMatch = $container->get('current_route_match'); + return $plugin; + } + + /** + * + */ + public function getData(): mixed { + $breadcrumb = $this->breadcrumbManager->build($this->routeMatch); + $renderable = $breadcrumb->toRenderable(); + if (isset($renderable["#cache"])) { + $this->cacheArray = $renderable["#cache"]; + } + $links = []; + foreach ($breadcrumb->getLinks() as $link) { + $links[] = [ + "title" => $link->getText(), + "url" => $link->getUrl()->toString(), + ]; + } + return $links; + + return []; + } + + /** + * + */ + public function defaultConfiguration() { + return []; + } + +} diff --git a/src/Plugin/UiPatterns/Source/ColorWidget.php b/src/Plugin/UiPatterns/Source/ColorWidget.php new file mode 100644 index 00000000..3c2cadd3 --- /dev/null +++ b/src/Plugin/UiPatterns/Source/ColorWidget.php @@ -0,0 +1,49 @@ + 'color', + '#title' => $this->propDefinition['title'], + '#default_value' => $this->configuration['form_value'], + ]; + } + + /** + * + */ + public function defaultConfiguration() { + return []; + } + +} diff --git a/src/Plugin/UiPatterns/Source/FieldSource.php b/src/Plugin/UiPatterns/Source/FieldSource.php deleted file mode 100644 index 7ff73013..00000000 --- a/src/Plugin/UiPatterns/Source/FieldSource.php +++ /dev/null @@ -1,70 +0,0 @@ -entityFieldManager = $entity_field_manager; - } - - /** - * {@inheritdoc} - */ - public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) { - return new static( - $configuration, - $plugin_id, - $plugin_definition, - $container->get('entity_field.manager') - ); - } - - /** - * {@inheritdoc} - */ - public function getSourceFields() { - $sources = []; - $fields = $this->entityFieldManager->getFieldDefinitions($this->getContextProperty('entity_type'), $this->getContextProperty('entity_bundle')); - - /** @var \Drupal\Core\Field\FieldDefinitionInterface $field */ - foreach ($fields as $field) { - if (!$this->getContextProperty('limit')) { - $sources[] = $this->getSourceField($field->getName(), $field->getLabel()); - } - elseif (in_array($field->getName(), $this->getContextProperty('limit'))) { - $sources[] = $this->getSourceField($field->getName(), $field->getLabel()); - } - } - return $sources; - } - -} diff --git a/src/Plugin/UiPatterns/Source/MachineNameWidget.php b/src/Plugin/UiPatterns/Source/MachineNameWidget.php new file mode 100644 index 00000000..94e84865 --- /dev/null +++ b/src/Plugin/UiPatterns/Source/MachineNameWidget.php @@ -0,0 +1,49 @@ + 'machine_name', + '#title' => $this->propDefinition['title'], + '#default_value' => $this->configuration['form_value'], + ]; + } + + /** + * + */ + public function defaultConfiguration() { + return []; + } + +} diff --git a/src/Plugin/UiPatterns/Source/MenuSource.php b/src/Plugin/UiPatterns/Source/MenuSource.php new file mode 100644 index 00000000..d58a0263 --- /dev/null +++ b/src/Plugin/UiPatterns/Source/MenuSource.php @@ -0,0 +1,72 @@ + 'select', + '#title' => 'Menu', + '#options' => $this->getMenuList(), + '#default_value' => \array_key_exists("menu", $value) ? $value["menu"] : "", + ]; + $options = range(0, $this->menuLinkTree->maxDepth()); + unset($options[0]); + $form['level'] = [ + '#type' => 'select', + '#title' => $this->t('Initial visibility level'), + '#default_value' => \array_key_exists("level", $value) ? $value["level"] : 1, + '#options' => $options, + '#required' => TRUE, + ]; + $options[0] = $this->t('Unlimited'); + $form['depth'] = [ + '#type' => 'select', + '#title' => $this->t('Number of levels to display'), + '#default_value' => \array_key_exists("depth", $value) ? $value["depth"] : 0, + '#options' => $options, + '#description' => $this->t('This maximum number includes the initial level and the final display is dependant of the pattern template.'), + '#required' => TRUE, + ]; + + return $form; + + } + + /** + * + */ + public function defaultConfiguration() { + return []; + } + +} diff --git a/src/Plugin/UiPatterns/Source/NumberWidget.php b/src/Plugin/UiPatterns/Source/NumberWidget.php new file mode 100644 index 00000000..a5a4252f --- /dev/null +++ b/src/Plugin/UiPatterns/Source/NumberWidget.php @@ -0,0 +1,49 @@ + 'number', + '#title' => $this->propDefinition['title'], + '#default_value' => $this->configuration['form_value'], + ]; + } + + /** + * + */ + public function defaultConfiguration() { + return []; + } + +} diff --git a/src/Plugin/UiPatterns/Source/SelectWidget.php b/src/Plugin/UiPatterns/Source/SelectWidget.php new file mode 100644 index 00000000..e3e84d7e --- /dev/null +++ b/src/Plugin/UiPatterns/Source/SelectWidget.php @@ -0,0 +1,54 @@ + 'select', + '#title' => $this->propDefinition['title'], + '#default_value' => $this->configuration['form_value'], + "#options" => $options, + ]; + } + + /** + * + */ + public function defaultConfiguration() { + return []; + } + +} diff --git a/src/Plugin/UiPatterns/Source/SiteNameSource.php b/src/Plugin/UiPatterns/Source/SiteNameSource.php new file mode 100644 index 00000000..682fc70f --- /dev/null +++ b/src/Plugin/UiPatterns/Source/SiteNameSource.php @@ -0,0 +1,37 @@ + get('name'); + } + + /** + * + */ + public function defaultConfiguration() { + return []; + } + +} diff --git a/src/Plugin/UiPatterns/Source/TextfieldWidget.php b/src/Plugin/UiPatterns/Source/TextfieldWidget.php new file mode 100644 index 00000000..110fe0e3 --- /dev/null +++ b/src/Plugin/UiPatterns/Source/TextfieldWidget.php @@ -0,0 +1,49 @@ + 'textfield', + '#title' => $this->propDefinition['title'], + '#default_value' => $this->configuration['form_value'], + ]; + } + + /** + * + */ + public function defaultConfiguration() { + return []; + } + +} diff --git a/src/Plugin/UiPatterns/Source/UrlWidget.php b/src/Plugin/UiPatterns/Source/UrlWidget.php new file mode 100644 index 00000000..d140506f --- /dev/null +++ b/src/Plugin/UiPatterns/Source/UrlWidget.php @@ -0,0 +1,49 @@ + 'url', + '#title' => $this->propDefinition['title'], + '#default_value' => $this->configuration['form_value'], + ]; + } + + /** + * + */ + public function defaultConfiguration() { + return []; + } + +} diff --git a/src/PropTypeInterface.php b/src/PropTypeInterface.php new file mode 100644 index 00000000..09b3db06 --- /dev/null +++ b/src/PropTypeInterface.php @@ -0,0 +1,26 @@ +pluginDefinition['label']; + } + + /** + * + */ + public function getSchema():array { + return (array) $this->pluginDefinition['schema']; + } + +} diff --git a/src/PropTypePluginManager.php b/src/PropTypePluginManager.php new file mode 100644 index 00000000..4c29e339 --- /dev/null +++ b/src/PropTypePluginManager.php @@ -0,0 +1,86 @@ +alterInfo('prop_type_info'); + $this->setCacheBackend($cache_backend, 'ui_patterns_prop_type_plugins'); + } + + /** + * + */ + public function getPropTypePlugin(array $prop_schema): ?PropTypeInterface { + $definition = $this->getPropTypeDefinition($prop_schema); + if ($definition !== NULL) { + return $this->createInstance($definition['id'], []); + } + return $this->createInstance('unknown_prop_type', []); + } + + /** + * + */ + public function getSortedDefinitions() { + $definitions = $this->getDefinitions(); + usort($definitions, function ($a, $b) { + return $a['priority'] ?? 1 > $b['priority'] ?? 1; + }); + return $definitions; + } + + /** + * + */ + public function getPropTypeDefinition(array $prop_schema): ?array { + if (isset($prop_schema['$ref']) && str_contains($prop_schema['$ref'], "ui-patterns://")) { + $prop_type_id = str_replace("ui-patterns://", "", $prop_schema['$ref']); + return $this->getDefinition($prop_type_id); + } + $definitions = $this->getSortedDefinitions(); + $compatibilityChecker = new SchemaCompatibilityChecker(); + foreach ($definitions as $definition) { + if ($compatibilityChecker->isCompatible($prop_schema, $definition['schema'])) { + return $definition; + } + } + return NULL; + } + + /** + * + */ + public function getFallbackPluginId($plugin_id, array $configuration = []) { + return 'unknown_prop_type'; + } + +} diff --git a/src/Sdc/ComponentPluginManagerDecorator.php b/src/Sdc/ComponentPluginManagerDecorator.php new file mode 100644 index 00000000..4ac91ff8 --- /dev/null +++ b/src/Sdc/ComponentPluginManagerDecorator.php @@ -0,0 +1,120 @@ +setCacheBackend($cacheBackend, $this->getCacheKey()); + } + + /** + * + */ + public function createInstance($plugin_id, array $configuration = []): Component { + if (parent::hasDefinition($plugin_id)) { + return parent::createInstance($plugin_id, $configuration); + } + else { + return $this->parentSdcPluginManager->createInstance($plugin_id, $configuration); + } + } + + /** + * Returns the cache key for the decorated service. + * + * @return string + * The cache key. + */ + abstract protected function getCacheKey(); + + /** + * {@inheritdoc} + */ + public function find(string $component_id): Component { + if (parent::hasDefinition($component_id)) { + return parent::find($component_id); + } + return $this->parentSdcPluginManager->find($component_id); + } + + /** + * {@inheritdoc} + */ + public function getAllComponents(): array { + $original_components = $this->parentSdcPluginManager->getAllComponents(); + return array_merge($original_components, parent::getAllComponents()); + } + + /** + * {@inheritdoc} + */ + public function getDefinitions() { + $decorated_definitions = parent::getDefinitions(); + $original_definitions = $this->parentSdcPluginManager->getDefinitions(); + return array_merge($original_definitions, $decorated_definitions); + } + + /** + * {@inheritdoc} + */ + public function getDefinition($plugin_id, $exception_on_invalid = TRUE) { + $original_definition = parent::getDefinition($plugin_id, FALSE); + return $original_definition ?? $this->parentSdcPluginManager->getDefinition($plugin_id, $exception_on_invalid); + } + +} diff --git a/src/Sdc/UiPatternsSdcPluginManager.php b/src/Sdc/UiPatternsSdcPluginManager.php new file mode 100644 index 00000000..b5b123ac --- /dev/null +++ b/src/Sdc/UiPatternsSdcPluginManager.php @@ -0,0 +1,109 @@ + $definition) { + if (!isset($definition['props'])) { + continue; + } + if (!isset($definition['props']['properties'])) { + continue; + } + foreach ($definition['props']['properties'] as $prop_id => $prop) { + $prop_type = $this->propTypePluginManager->getPropTypePlugin($prop); + $sources = $this->sourcePluginManager->getSourcePlugins($prop_type->getPluginId(), $prop_id, $prop); + $prop['ui_patterns']['type_definition'] = $prop_type; + $prop['ui_patterns']['source'] = $sources; + $definition['props']['properties'][$prop_id] = $prop; + } + $definitions[$component_id] = $definition; + } + } + + /** + * Do we move this method to ui_patterns_legacy? + */ + public function getNamespacedId(string $component_id): string { + $parts = explode(":", $component_id); + if (count(array_filter($parts)) === 2) { + // Already namespaced. + return $component_id; + } + if (count(array_filter($parts)) > 2) { + // Unexpected situation. + return $component_id; + } + $components = $this->getAllComponents(); + // @todo Search first in current active theme, then parents themes, then modules. + foreach ($components as $component) { + if ($component->getPluginDefinition()["machineName"] === $component_id) { + return $component->getPluginId(); + } + } + return $component_id; + } + + /** + * {@inheritdoc} + */ + public function getGroupedDefinitions(?array $definitions = NULL): array { + $definitions = $definitions ?: $this->getDefinitions(); + $groups = []; + foreach ($definitions as $id => $definition) { + $group = $definition["group"] ?? "Other"; + $groups[$group][$id] = $definition; + } + return $groups; + } + + /** + * Stories slots have no "#" prefix in render arrays. Let's add them. + * A bit like UI Patterns 1.x's PatternPreview::getPreviewMarkup() + * This method belongs here because used by both ui_patterns_library and + * ui_patterns_legacy. + */ + public static function processStoriesSlots(array $slots): array { + foreach ($slots as $slot_id => $slot) { + if (!is_array($slot)) { + continue; + } + if (array_is_list($slot)) { + $slots[$slot_id] = self::processStoriesSlots($slot); + } + $slot_keys = array_keys($slot); + $render_keys = ["theme", "type", "markup", "plain_text"]; + if (count(array_intersect($slot_keys, $render_keys)) > 0) { + foreach ($slot as $key => $value) { + if (is_array($value)) { + $value = self::processStoriesSlots($value); + } + $slots[$slot_id]["#" . $key] = $value; + unset($slots[$slot_id][$key]); + } + } + } + return $slots; + } + +} diff --git a/src/SourceInterface.php b/src/SourceInterface.php new file mode 100644 index 00000000..5a7113b0 --- /dev/null +++ b/src/SourceInterface.php @@ -0,0 +1,35 @@ +setConfiguration($configuration); + } + + protected array $propDefinition; + + protected $propId; + + /** + * {@inheritdoc} + */ + public function label(): string { + // Cast the label to a string since it is a TranslatableMarkup object. + return (string) $this->pluginDefinition['label']; + } + + /** + * + */ + public function buildConfigurationForm( + array $form, + FormStateInterface $form_state + ) { + return []; + } + + /** + * + */ + public function validateConfigurationForm( + array &$form, + FormStateInterface $form_state + ) { + + } + + /** + * + */ + public function submitConfigurationForm( + array &$form, + FormStateInterface $form_state + ) { + $parents = $form['#parents']; + unset($parents[0]); + $value = $form_state->getValue($parents); + $this->configuration['form_value'] = $value; + } + + /** + * + */ + public function getConfiguration() { + return $this->configuration; + } + + /** + * + */ + public function setConfiguration(array $configuration) { + if (isset($configuration['prop_definition'])) { + $this->propDefinition = $configuration['prop_definition']; + } + if (isset($configuration['prop_id'])) { + $this->propId = $configuration['prop_id']; + } + $this->configuration = $configuration; + } + + /** + * + */ + abstract public function defaultConfiguration(); + + /** + * + */ + public function getPropId(): string { + return $this->propId; + } + + /** + * + */ + public function getPropDefinition(): array { + return $this->propDefinition; + } + +} diff --git a/src/SourcePluginManager.php b/src/SourcePluginManager.php new file mode 100644 index 00000000..2bfe443c --- /dev/null +++ b/src/SourcePluginManager.php @@ -0,0 +1,71 @@ +alterInfo('ui_patterns_source_info'); + $this->setCacheBackend($cache_backend, 'ui_patterns_source_plugins'); + } + + /** + * + */ + public function getSourceDefinitions($prop_type_id) { + $definitions = $this->getDefinitions(); + $sources = []; + foreach ($definitions as $definition) { + if (isset($definition['prop_types']) && in_array( + $prop_type_id, + $definition['prop_types'] + )) { + $sources[] = $definition; + } + } + return $sources; + } + + /** + * + */ + public function getSourcePlugins($prop_type_id, $prop_id, $prop_definition):array { + $definitions = $this->getSourceDefinitions($prop_type_id); + $sources = []; + foreach ($definitions as $definition) { + $sources[] = $this->createInstance( + $definition['id'], + [ + 'prop_id' => $prop_id, + 'prop_definition' => $prop_definition, + ] + ); + } + return $sources; + } + +} diff --git a/src/StreamWrapper/UiPatternsPropTypeStreamWrapper.php b/src/StreamWrapper/UiPatternsPropTypeStreamWrapper.php new file mode 100644 index 00000000..41fcb0a7 --- /dev/null +++ b/src/StreamWrapper/UiPatternsPropTypeStreamWrapper.php @@ -0,0 +1,54 @@ +getDefinition($plugin_id); + $stream = fopen('php://memory', 'r+'); + fwrite($stream, json_encode($plugin['schema'])); + rewind($stream); + $this->handle = $stream; + return $stream; + } + + /** + * + */ + public function getDirectoryPath() { + return NULL; + } + + /** + * + */ + public function getName() { + return 'ui_patterns'; + } + + /** + * + */ + public function getDescription() { + return 'ui_patterns'; + } + + /** + * + */ + public function getExternalUrl() { + return NULL; + } + +} diff --git a/src/Template/AttributesFilterTrait.php b/src/Template/AttributesFilterTrait.php new file mode 100644 index 00000000..49c2668c --- /dev/null +++ b/src/Template/AttributesFilterTrait.php @@ -0,0 +1,117 @@ +arrayIsList($element)) { + foreach ($element as $index => $item) { + if (!\is_array($item)) { + continue; + } + $element[$index] = $this->addClass($item, ...$classes); + } + return $element; + } + $attributes = new Attribute($element['#attributes'] ?? []); + $attributes->addClass(...$classes); + $element['#attributes'] = $attributes->toArray(); + + // Make sure element gets rendered again. + unset($element['#printed']); + + return $element; + } + + /** + * Set attribute on a given element. + * + * @param mixed $element + * A render array. + * @param string $name + * The attribute name. + * @param mixed $value + * (optional) The attribute value. + * + * @return mixed + * The element with the given sanitized attribute's value or the unchanged + * element if passed value is not an array. + * + * @see Drupal\Core\Template\TwigExtension::setAttribute() + */ + public function setAttribute(mixed $element, string $name, mixed $value = NULL): mixed { + if (!\is_array($element)) { + return $element; + } + if ($this->arrayIsList($element)) { + foreach ($element as $index => $item) { + if (!\is_array($item)) { + continue; + } + $element[$index] = $this->setAttribute($item, $name, $value); + } + return $element; + } + $element['#attributes'] = AttributeHelper::mergeCollections( + $element['#attributes'] ?? [], + new Attribute([$name => $value]) + ); + + // Make sure element gets rendered again. + unset($element['#printed']); + + return $element; + } + + /** + * Checks whether a given array is a list. + * + * Same as array_is_list() but compatible with PHP8. + * + * @param array $array + * The array being evaluated. + * + * @return bool + * Returns true if array is a list, false otherwise. + * + * @see https://www.php.net/manual/en/function.array-is-list.php#126794 + */ + private function arrayIsList(array $array): bool { + $i = -1; + foreach ($array as $k => $v) { + ++$i; + if ($k !== $i) { + return FALSE; + } + } + return TRUE; + } + +} diff --git a/src/Template/TwigExtension.php b/src/Template/TwigExtension.php index b98cf4cb..d946895b 100644 --- a/src/Template/TwigExtension.php +++ b/src/Template/TwigExtension.php @@ -2,12 +2,18 @@ namespace Drupal\ui_patterns\Template; +use Twig\Extension\AbstractExtension; +use Twig\TwigFilter; +use Twig\TwigFunction; + /** * Twig extension providing UI Patterns-specific functionalities. * * @package Drupal\ui_patterns\Template */ -class TwigExtension extends \Twig_Extension { +class TwigExtension extends AbstractExtension { + + use AttributesFilterTrait; /** * {@inheritdoc} @@ -21,59 +27,44 @@ public function getName() { */ public function getFunctions() { return [ - new \Twig_SimpleFunction('pattern', [ + new TwigFunction('component', [ $this, - 'renderPattern', - ]), - new \Twig_SimpleFunction('pattern_preview', [ - $this, - 'renderPatternPreview', + 'renderComponent', ]), ]; } /** - * Render given pattern. - * - * @param string $id - * Pattern ID. - * @param array $fields - * Pattern fields. - * @param string $variant - * Variant name. - * - * @return array - * Pattern render array. - * - * @see \Drupal\ui_patterns\Element\Pattern + * {@inheritdoc} */ - public function renderPattern($id, array $fields = [], $variant = "") { + public function getFilters() { return [ - '#type' => 'pattern', - '#id' => $id, - '#fields' => $fields, - '#variant' => $variant, + new TwigFilter('add_class', [$this, 'addClass']), + new TwigFilter('set_attribute', [$this, 'setAttribute']), ]; } /** - * Render given pattern. + * Render given component. * - * @param string $id - * Pattern ID. - * @param string $variant - * Variant name. + * @param string $component_id + * Component ID. + * @param array $slots + * Pattern slots. + * @param array $props + * Pattern props. * * @return array * Pattern render array. * - * @see \Drupal\ui_patterns\Element\Pattern + * @see \Drupal\sdc\Element\ComponentElement */ - public function renderPatternPreview($id, $variant = "") { + public function renderComponent(string $component_id, array $slots = [], array $props = []) { return [ - '#type' => 'pattern_preview', - '#id' => $id, - '#variant' => $variant, + '#type' => 'component', + '#component' => $component_id, + '#slots' => $slots, + '#props' => $props, ]; } diff --git a/src/TypedData/PatternDataDefinition.php b/src/TypedData/PatternDataDefinition.php deleted file mode 100644 index 8270128a..00000000 --- a/src/TypedData/PatternDataDefinition.php +++ /dev/null @@ -1,96 +0,0 @@ -setMainPropertyName('id') - ->setPropertyDefinition('id', $this->getMachineNameDefinition()->setRequired(TRUE)) - ->setPropertyDefinition('label', DataDefinition::create('string')->setRequired(TRUE)) - ->setPropertyDefinition('base path', DataDefinition::create('string')->setRequired(TRUE)) - ->setPropertyDefinition('file name', DataDefinition::create('string')->setRequired(TRUE)) - ->setPropertyDefinition('provider', DataDefinition::create('string')->setRequired(TRUE)) - ->setPropertyDefinition('fields', $this->getFieldsDefinition()) - ->setPropertyDefinition('variants', $this->getVariantsDefinition()) - ->setPropertyDefinition('theme hook', DataDefinition::create('string')->setRequired(TRUE)) - ->setPropertyDefinition('description', DataDefinition::create('string')) - ->setPropertyDefinition('use', DataDefinition::create('string')) - ->setPropertyDefinition('tags', ListDataDefinition::create('string')) - ->setPropertyDefinition('custom theme hook', DataDefinition::create('boolean')) - ->setPropertyDefinition('template', DataDefinition::create('string')) - ->setPropertyDefinition('libraries', DataDefinition::create('any')); - return $this->propertyDefinitions; - } - - /** - * Get valid machine name definition. - * - * @return \Drupal\Core\TypedData\DataDefinition - * Data definition instance. - */ - protected function getMachineNameDefinition() { - return DataDefinition::create('string') - ->addConstraint('Regex', sprintf(self::MACHINE_NAME, implode('|', $this->reserved))); - } - - /** - * Get definition for 'field' property. - * - * @return \Drupal\Core\TypedData\ListDataDefinition - * Data definition instance. - */ - protected function getFieldsDefinition() { - return new ListDataDefinition([], MapDataDefinition::create() - ->setPropertyDefinition('name', $this->getMachineNameDefinition()->setRequired(TRUE)) - ->setPropertyDefinition('label', DataDefinition::create('string')->setRequired(TRUE)) - ->setPropertyDefinition('type', $this->getMachineNameDefinition()) - ->setPropertyDefinition('description', DataDefinition::create('string')) - ->setPropertyDefinition('preview', DataDefinition::create('any'))); - } - - /** - * Get definition for 'variant' property. - * - * @return \Drupal\Core\TypedData\ListDataDefinition - * Data definition instance. - */ - protected function getVariantsDefinition() { - return new ListDataDefinition([], MapDataDefinition::create() - ->setPropertyDefinition('name', $this->getMachineNameDefinition()->setRequired(TRUE)) - ->setPropertyDefinition('label', DataDefinition::create('string')->setRequired(TRUE)) - ->setPropertyDefinition('description', DataDefinition::create('string'))); - } - -} diff --git a/src/UiPatterns.php b/src/UiPatterns.php deleted file mode 100644 index c480b712..00000000 --- a/src/UiPatterns.php +++ /dev/null @@ -1,55 +0,0 @@ -getDefinition($id); - } - - /** - * Get pattern definitions. - * - * @return \Drupal\ui_patterns\Definition\PatternDefinition[] - * Pattern object instance. - */ - public static function getPatternDefinitions() { - return \Drupal::service('plugin.manager.ui_patterns')->getDefinitions(); - } - -} diff --git a/src/UiPatternsManager.php b/src/UiPatternsManager.php deleted file mode 100644 index 08d4ff6b..00000000 --- a/src/UiPatternsManager.php +++ /dev/null @@ -1,110 +0,0 @@ -moduleHandler = $module_handler; - $this->themeHandler = $theme_handler; - $this->alterInfo('ui_patterns_info'); - $this->setCacheBackend($cache_backend, 'ui_patterns', ['ui_patterns']); - } - - /** - * Get pattern objects. - * - * @return \Drupal\ui_patterns\Plugin\PatternBase[] - * Pattern objects. - */ - public function getPatterns() { - $patterns = []; - foreach ($this->getDefinitions() as $definition) { - $patterns[] = $this->getFactory()->createInstance($definition->id()); - } - return $patterns; - } - - /** - * Return pattern definitions. - * - * @return \Drupal\ui_patterns\Definition\PatternDefinition[] - * Pattern definitions. - */ - public function getDefinitions() { - $definitions = $this->getCachedDefinitions(); - if (!isset($definitions)) { - // Remove derivative id from pattern definitions keys. - // @todo make sure validation takes care of ensuring ids are unique. - $definitions = []; - foreach ($this->findDefinitions() as $id => $definition) { - $definitions[$definition['id']] = $definition; - unset($definitions[$id]); - } - $this->setCachedDefinitions($definitions); - } - return $definitions; - } - - /** - * {@inheritdoc} - */ - public function getPatternsOptions() { - return array_map(function ($option) { - return $option['label']; - }, $this->getDefinitions()); - } - - /** - * {@inheritdoc} - */ - public function isPatternHook($hook) { - // Improve performance on not cached pages. - if (empty($this->patternHooks)) { - foreach ($this->getDefinitions() as $definition) { - $this->patternHooks[$definition->getThemeHook()] = $definition->getThemeHook(); - } - } - return !empty($this->patternHooks[$hook]); - } - - /** - * {@inheritdoc} - */ - protected function providerExists($provider) { - return $this->moduleHandler->moduleExists($provider) || $this->themeHandler->themeExists($provider); - } - -} diff --git a/src/UiPatternsSourceManager.php b/src/UiPatternsSourceManager.php deleted file mode 100644 index d9cccc6a..00000000 --- a/src/UiPatternsSourceManager.php +++ /dev/null @@ -1,62 +0,0 @@ -alterInfo('ui_patterns_ui_patterns_source_info'); - $this->setCacheBackend($cache_backend, 'ui_patterns_ui_patterns_source_plugins'); - } - - /** - * Filter definitions by given tag. - * - * @param string $tag - * Tag used on plugin annotation. - * - * @return array - * List of definitions tagged with given tag. - */ - public function getDefinitionsByTag($tag) { - return array_filter($this->getDefinitions(), function ($definition) use ($tag) { - return in_array($tag, $definition['tags']); - }); - } - - /** - * Get field source definitions by specified tags. - * - * @param string $tag - * Field source tag. - * @param array $context - * Plugin context. - * - * @return \Drupal\ui_patterns\Definition\PatternSourceField[] - * List of source fields. - */ - public function getFieldsByTag($tag, array $context) { - /** @var \Drupal\ui_patterns\Plugin\PatternSourceInterface $plugin */ - $fields = []; - foreach ($this->getDefinitionsByTag($tag) as $id => $definition) { - $plugin = $this->createInstance($id, ['context' => $context]); - foreach ($plugin->getSourceFields() as $field) { - $fields[$field->getFieldKey()] = $field; - } - } - - return $fields; - } - -} diff --git a/src/Utils/SchemaCompatibilityChecker.php b/src/Utils/SchemaCompatibilityChecker.php new file mode 100644 index 00000000..0bb5937d --- /dev/null +++ b/src/Utils/SchemaCompatibilityChecker.php @@ -0,0 +1,391 @@ +canonicalize($checked_schema); + $reference_schema = $this->canonicalize($reference_schema); + if ($this->isSame($checked_schema, $reference_schema)) { + return TRUE; + } + if (isset($checked_schema["type"]) && isset($reference_schema["type"])) { + return $this->isTypeCompatible($checked_schema, $reference_schema); + } + if (isset($checked_schema["anyOf"]) || isset($reference_schema["anyOf"])) { + return $this->isAnyOfCompatible($checked_schema, $reference_schema); + } + return FALSE; + } + + /** + * + */ + protected function isSame($checked_schema, $reference_schema): bool { + return (serialize($checked_schema) === serialize($reference_schema)); + } + + /** + * + */ + protected function isTypeCompatible($checked_schema, $reference_schema): bool { + if (is_array($checked_schema["type"]) || is_array($checked_schema["type"])) { + // Because of self::resolveMultipleTypes() we are not supposed to meet this + // situation. + return FALSE; + } + if ($checked_schema["type"] !== $reference_schema["type"]) { + // Integers are numbers, but numbers are not always integer. + if (!($checked_schema["type"] === "integer" && $reference_schema["type"] === "number")) { + return FALSE; + } + } + // Now we know $checked_schema and $reference_schema have the same type. + // So, testing $checked_schema type is enough. + return match ($checked_schema["type"]) { + 'null' => TRUE, + 'boolean' => TRUE, + 'object' => $this->isObjectCompatible($checked_schema, $reference_schema), + 'array' => $this->isArrayCompatible($checked_schema, $reference_schema), + 'number' => $this->isNumberCompatible($checked_schema, $reference_schema), + 'integer' => $this->isIntegerCompatible($checked_schema, $reference_schema), + 'string' => $this->isStringCompatible($checked_schema, $reference_schema), + }; + } + + /** + * + */ + protected function isAnyOfCompatible($checked_schema, $reference_schema): bool { + if (isset($reference_schema["anyOf"])) { + foreach ($reference_schema["anyOf"] as $schema) { + if ($this->isCompatible($checked_schema, $schema)) { + return TRUE; + } + } + } + if (isset($checked_schema["anyOf"])) { + foreach ($checked_schema["anyOf"] as $schema) { + if ($this->isCompatible($schema, $reference_schema)) { + return TRUE; + } + } + } + return FALSE; + } + + /** + * + */ + protected function isObjectCompatible(array $checked_schema, array $reference_schema): bool { + // FALSE if at least one of those tests is FALSE. + if (!isset($checked_schema["properties"]) && isset($reference_schema["properties"])) { + return FALSE; + } + if (!isset($checked_schema["patternProperties"]) && isset($reference_schema["patternProperties"])) { + return FALSE; + } + if (isset($checked_schema["properties"]) && isset($reference_schema["properties"])) { + // @todo + } + if (isset($checked_schema["patternProperties"]) && isset($reference_schema["patternProperties"])) { + // @todo + } + return TRUE; + } + + /** + * Check if different arrays are compatible. + */ + protected function isArrayCompatible(array $checked_schema, array $reference_schema): bool { + // FALSE if at least one of those tests is FALSE. + if (!isset($checked_schema["items"]) && isset($reference_schema["items"])) { + return FALSE; + } + // https://json-schema.org/understanding-json-schema/reference/array#items + if (isset($checked_schema["items"]) && isset($reference_schema["items"])) { + if (!$this->isCompatible($checked_schema["items"], $reference_schema["items"])) { + return FALSE; + } + } + // @todo https://json-schema.org/understanding-json-schema/reference/array#contains + // @todo https://json-schema.org/understanding-json-schema/reference/array#mincontains-maxcontains + // @todo https://json-schema.org/understanding-json-schema/reference/array#length + // @todo https://json-schema.org/understanding-json-schema/reference/array#uniqueness + return TRUE; + } + + /** + * Check if different numbers are compatible. + */ + protected function isNumberCompatible(array $checked_schema, array $reference_schema): bool { + if ($reference_schema["type"] === "integer") { + // Integers are always numbers, but numbers are not always integer. + return FALSE; + } + return $this->isNumericCompatible($checked_schema, $reference_schema); + } + + /** + * Check if different integers are compatible. + */ + protected function isIntegerCompatible(array $checked_schema, array $reference_schema): bool { + return $this->isNumericCompatible($checked_schema, $reference_schema); + } + + /** + * Rules shared by numbers and integers. + */ + protected function isNumericCompatible(array $checked_schema, array $reference_schema): bool { + // FALSE if at least one of those tests is FALSE. + if (array_key_exists("enum", $reference_schema)) { + if (!$this->isEnumCompatible($checked_schema, $reference_schema)) { + return FALSE; + } + } + // @todo https://json-schema.org/understanding-json-schema/reference/numeric#multiples + // @todo https://json-schema.org/understanding-json-schema/reference/numeric#range + return TRUE; + } + + /** + * Check if different strings are compatible. + */ + protected function isStringCompatible(array $checked_schema, array $reference_schema): bool { + // FALSE if at least one of those tests is FALSE. + if (array_key_exists("format", $reference_schema)) { + if (!$this->isStringFormatCompatible($checked_schema, $reference_schema)) { + return FALSE; + } + } + if (array_key_exists("enum", $reference_schema)) { + if (!$this->isEnumCompatible($checked_schema, $reference_schema)) { + return FALSE; + } + } + if (array_key_exists("minLength", $reference_schema)) { + if (!$this->isMinLengthCompatible($checked_schema, $reference_schema)) { + return FALSE; + } + } + if (array_key_exists("maxLength", $reference_schema)) { + if (!$this->isMaxLengthCompatible($checked_schema, $reference_schema)) { + return FALSE; + } + } + if (array_key_exists("pattern", $reference_schema)) { + if (!$this->isStringPatternCompatible($checked_schema, $reference_schema)) { + return FALSE; + } + } + return TRUE; + } + + /** + * See: https://json-schema.org/understanding-json-schema/reference/string#regexp. + */ + protected function isStringPatternCompatible(array $checked_schema, array $reference_schema): bool { + if (!array_key_exists("pattern", $checked_schema)) { + return FALSE; + } + return FALSE; + // Is checked schema pattern and sub pattern of reference schema? + $example = $this->generateExampleFromPattern($checked_schema["pattern"]); + $result = preg_match("/" . $reference_schema["pattern"] . "/", $example); + if ($result === 1) { + return TRUE; + } + return FALSE; + } + + /** + * + */ + protected function generateExampleFromPattern(string $pattern): string { + // ilario-pierbattista/reverse-regex. + return $result; + } + + /** + * See: https://json-schema.org/understanding-json-schema/reference/string#format. + */ + protected function isStringFormatCompatible(array $checked_schema, array $reference_schema): bool { + if (!array_key_exists("format", $checked_schema)) { + return FALSE; + } + $checked_format = $checked_schema["format"]; + $reference_format = $reference_schema["format"]; + if ($checked_format == $reference_format) { + return TRUE; + } + // Ex: an uri is also a valid uri-reference + // Ex: an uri-reference is also a valid iri-reference. + $compatibility_map = [ + "uri" => [ + "uri-reference", + "iri-reference", + "iri", + ], + "iri" => [ + "iri-reference", + ], + "uri-reference" => [ + "iri-reference", + ], + "email" => [ + "idn-email", + ], + // @todo add others formats. + ]; + if (array_key_exists($checked_format, $compatibility_map)) { + return in_array($reference_format, $compatibility_map[$checked_format]); + } + return FALSE; + } + + /** + * + */ + protected function isMinLengthCompatible(array $checked_schema, array $reference_schema): bool { + if (!array_key_exists("minLength", $checked_schema)) { + return FALSE; + } + return ($checked_schema["minLength"] >= $reference_schema["minLength"]); + } + + /** + * + */ + protected function isMaxLengthCompatible(array $checked_schema, array $reference_schema): bool { + if (!array_key_exists("maxLength", $checked_schema)) { + return FALSE; + } + return ($checked_schema["maxLength"] <= $reference_schema["maxLength"]); + } + + /** + * + */ + protected function isEnumCompatible(array $checked_schema, array $reference_schema): bool { + if (!array_key_exists("enum", $checked_schema)) { + return FALSE; + } + if (empty($reference_schema["enum"])) { + return TRUE; + } + if (count($checked_schema["enum"]) === count($reference_schema["enum"])) { + $diff = array_diff($checked_schema["enum"], $reference_schema["enum"]); + return ($diff === []); + } + if (count($checked_schema["enum"]) > count($reference_schema["enum"])) { + return FALSE; + } + if (count($checked_schema["enum"]) < count($reference_schema["enum"])) { + $diff = array_diff($reference_schema["enum"], $checked_schema["enum"]); + return (count($diff) >= 0); + } + return FALSE; + } + + /** + * @todo Make it public and unit testable independently? + */ + protected function canonicalize(array $schema): array { + $schema = $this->keepOnlyUsefulProperties($schema); + if (array_key_exists("type", $schema)) { + $schema = $this->canonicalizeType($schema); + } + if (array_key_exists("anyOf", $schema)) { + foreach ($schema["anyOf"] as $index => $sub_schema) { + $schema["anyOf"][$index] = $this->canonicalize($sub_schema); + } + } + ksort($schema); + return $schema; + } + + /** + * + */ + protected function canonicalizeType(array $schema): array { + if (!isset($schema["type"])) { + return $schema; + } + if (is_array($schema["type"])) { + $schema = $this->resolveMultipleTypes($schema); + return $this->canonicalize($schema); + } + if ($schema["type"] === "object" && isset($schema["properties"])) { + foreach ($schema["properties"] as $property_id => $property) { + $schema["properties"][$property_id] = $this->canonicalize($property); + } + } + if ($schema["type"] === "array" && isset($schema["items"])) { + $schema["items"] = $this->canonicalize($schema["items"]); + } + return $schema; + } + + /** + * + */ + protected function resolveMultipleTypes(array $schema): array { + if (!is_array($schema["type"])) { + return $schema; + } + $schemas = [ + "anyOf" => [], + ]; + foreach ($schema["type"] as $index => $type) { + $sub_schema = $schema; + $sub_schema["type"] = $type; + $schemas["anyOf"][$index] = $sub_schema; + } + return $schemas; + } + + /** + * + */ + protected function keepOnlyUsefulProperties(array $schema): array { + $keys = [ + "anyOf", "allOf", "oneOf", "not", "enum", "type", '$ref', "constant", + ]; + $keys_by_type = [ + "string" => ["minLength", "maxLength", "pattern", "format"], + "number" => ["minimum", "maximum", "exclusiveMinimum", "exclusiveMaximum", "multipleOf"], + "integer" => ["minimum", "maximum", "exclusiveMinimum", "exclusiveMaximum", "multipleOf"], + "boolean" => [], + "null" => [], + "array" => ["minItems", "maxItems", "items", "additionalItems", "uniqueItems"], + "object" => ["properties", "additionalProperties", "required", "minProperties", "maxProperties", "dependencies", "patternProperties"], + ]; + if (isset($schema["type"]) && is_string($schema["type"])) { + $type = $schema["type"]; + if (array_key_exists($type, $keys_by_type)) { + $keys = array_merge($keys, $keys_by_type[$type]); + } + } + return array_intersect_key($schema, array_flip($keys)); + } + +} diff --git a/templates/patterns-destination.html.twig b/templates/patterns-destination.html.twig deleted file mode 100644 index 5a53e11f..00000000 --- a/templates/patterns-destination.html.twig +++ /dev/null @@ -1,11 +0,0 @@ -{# -/** - * @file - * Render destination in case of multiple sources mapped to it. - */ -#} -{% if sources is not empty %} - {% for source in sources %} - {{ source }} - {% endfor %} -{% endif %} diff --git a/templates/patterns-use-wrapper.html.twig b/templates/patterns-use-wrapper.html.twig deleted file mode 100644 index 9cb6d41d..00000000 --- a/templates/patterns-use-wrapper.html.twig +++ /dev/null @@ -1,9 +0,0 @@ -{# -/** - * @file - * Simple wrapper used when an external Twig template is specified using the 'use:' property. - * - * @see \Drupal\ui_patterns\Element\Pattern::processUse(). - */ -#} -{% include use %} diff --git a/tests/README.md b/tests/README.md deleted file mode 100644 index 26a16505..00000000 --- a/tests/README.md +++ /dev/null @@ -1 +0,0 @@ -Please refer to the tests documentation [here](http://ui-patterns.readthedocs.io/en/8.x-1.x/content/tests.html). diff --git a/tests/modules/ui_patterns_field_source_test/src/Plugin/UiPatterns/Source/TestSource.php b/tests/modules/ui_patterns_field_source_test/src/Plugin/UiPatterns/Source/TestSource.php deleted file mode 100644 index 004bb9bc..00000000 --- a/tests/modules/ui_patterns_field_source_test/src/Plugin/UiPatterns/Source/TestSource.php +++ /dev/null @@ -1,34 +0,0 @@ -getSourceField('field_1', 'Field 1'), - $this->getSourceField('field_2', 'Field 2'), - $this->getSourceField('field_3', 'Field 3'), - $this->getSourceField('field_4', 'Field 4'), - $this->getSourceField('field_5', 'Field 5'), - ]; - } - -} diff --git a/tests/modules/ui_patterns_field_source_test/ui_patterns_field_source_test.info.yml b/tests/modules/ui_patterns_field_source_test/ui_patterns_field_source_test.info.yml deleted file mode 100644 index 7f770e42..00000000 --- a/tests/modules/ui_patterns_field_source_test/ui_patterns_field_source_test.info.yml +++ /dev/null @@ -1,4 +0,0 @@ -name: 'UI Patterns field source test' -type: module -core: 8.x -core_version_requirement: ^8 || ^9 diff --git a/tests/modules/ui_patterns_render_test/templates/foo-bar.ui_patterns.yml b/tests/modules/ui_patterns_render_test/templates/foo-bar.ui_patterns.yml deleted file mode 100644 index 94addb38..00000000 --- a/tests/modules/ui_patterns_render_test/templates/foo-bar.ui_patterns.yml +++ /dev/null @@ -1,5 +0,0 @@ -foo-bar: - label: Foo Bar - variants: - default: - label: Default diff --git a/tests/modules/ui_patterns_render_test/templates/foo.ui_patterns.yml b/tests/modules/ui_patterns_render_test/templates/foo.ui_patterns.yml deleted file mode 100644 index c699a913..00000000 --- a/tests/modules/ui_patterns_render_test/templates/foo.ui_patterns.yml +++ /dev/null @@ -1,5 +0,0 @@ -foo: - label: Foo - variants: - default: - label: Default diff --git a/tests/modules/ui_patterns_render_test/templates/pattern-foo-bar.html.twig b/tests/modules/ui_patterns_render_test/templates/pattern-foo-bar.html.twig deleted file mode 100644 index 76c7ac2d..00000000 --- a/tests/modules/ui_patterns_render_test/templates/pattern-foo-bar.html.twig +++ /dev/null @@ -1 +0,0 @@ -Foo Bar diff --git a/tests/modules/ui_patterns_render_test/ui_patterns_render_test.info.yml b/tests/modules/ui_patterns_render_test/ui_patterns_render_test.info.yml deleted file mode 100644 index df40108b..00000000 --- a/tests/modules/ui_patterns_render_test/ui_patterns_render_test.info.yml +++ /dev/null @@ -1,4 +0,0 @@ -name: 'UI Patterns Render Test' -type: module -core: 8.x -core_version_requirement: ^8 || ^9 diff --git a/tests/modules/ui_patterns_test/components/README.md b/tests/modules/ui_patterns_test/components/README.md new file mode 100644 index 00000000..c1f8b88a --- /dev/null +++ b/tests/modules/ui_patterns_test/components/README.md @@ -0,0 +1,77 @@ +# alert + +A medium complexity component for usual testing, using component() Twig function. + +❌ Not a valid SDC component because of the component() Twig function. + +# blockquote + +A simple component for basic testing. +No props. No variants. + +✅ Valid SDC component. The additions can be ignored. + +I was forced to write this anyway, because the component come from a module: + +``` +props: + type: object + properties: {} +``` + +# button + +A lot of variants. Props (with implicit typing) & slots. +With explicit template path (local, without namespace, the filename from UI Patterns 1.x) + +❌ Not a valid SDC component, because of the explicit template path. + +SDC do a fatal error: + +> Drupal\sdc\Exception\InvalidComponentException: Unable to find the Twig template for the component "ui_patterns_test:button". + +# card + +A complex component with a template by variant. A story calling another component ("button"). + +Props with implicit typing and default values. + +✅ Valid SDC component. The additions can be ignored. + +# close_button + +Here in order to test the component() Twig function in `alert`. + +Only props, no slots. + +There was initially an issue about the use of "\_" and "-" in component ID and template filename. See issue: [SDC should use dashes in file names](https://www.drupal.org/project/drupal/issues/3379527), but we renamed the template. + +✅ Valid SDC component. The additions can be ignored. + +# figure + +1 prop (with explicit typing) and slots. No variants. + +Replace `replaced_figure` + +❓ Not sure if the explicit typing will make this component a valid SDC component. + +# my-widget + +A more "traditional" SDC example with JSON schema examples instead of stories, and Twig blocks for slots. + +2 variants. + +✅ Valid SDC component. The additions can be ignored. + +# progress + +With explicit template path (in a subfolder, without namespace, expected filename) + +❌ Not a valid SDC component, because of explicit template path. + +# replaced_figure + +To test replacement mechanism + +✅ Valid SDC component. diff --git a/tests/modules/ui_patterns_test/components/alert/alert.component.yml b/tests/modules/ui_patterns_test/components/alert/alert.component.yml new file mode 100644 index 00000000..438a7713 --- /dev/null +++ b/tests/modules/ui_patterns_test/components/alert/alert.component.yml @@ -0,0 +1,45 @@ +$schema: https://git.drupalcode.org/project/sdc/-/raw/1.x/src/metadata.schema.json +name: "Alert" +description: "Provide contextual feedback messages for typical user actions with the handful of available and flexible alert messages." +links: + - "https://getbootstrap.com/docs/5.3/components/alerts/" +variants: + primary: + title: "Primary" + secondary: + title: "Secondary" + success: + title: "Success" + danger: + title: "Danger" + warning: + title: "Warning" + info: + title: "Info" + light: + title: "Light" + dark: + title: "Dark" +props: + type: object + properties: + dismissible: + title: "Dismissible?" + description: "It is possible to dismiss any alert inline." + type: "boolean" +slots: + heading: + title: "Heading" + description: "The alert heading. Optional." + message: + title: "Message" + description: "The alert message." +stories: + preview: + title: "Preview" + description: "The default preview from UI Patterns 1.x" + props: + dismissible: True + slots: + heading: "Well done!" + message: "A simple alert. Check it out!" diff --git a/tests/modules/ui_patterns_test/components/alert/alert.twig b/tests/modules/ui_patterns_test/components/alert/alert.twig new file mode 100644 index 00000000..45a9e07a --- /dev/null +++ b/tests/modules/ui_patterns_test/components/alert/alert.twig @@ -0,0 +1,22 @@ +{% if variant and variant|lower != 'default' %} + {% set attributes = attributes.addClass('alert-' ~ variant|lower|replace({'_': '-'})) %} +{% endif %} + +{% if dismissible %} + {% set attributes = attributes.addClass(['alert-dismissible', 'fade', 'show']) %} +{% endif %} + + + {% if heading %} +

{{ heading }}

+ {% endif %} + {{ message|add_class('alert-link') }} + {% if dismissible %} + {{ component('ui_patterns_test:close_button', {}, { + attributes: create_attribute({ + 'data-bs-dismiss': 'alert' + }), + aria_label: 'Close'|t, + }) }} + {% endif %} +
diff --git a/tests/modules/ui_patterns_test/components/blockquote/blockquote.component.yml b/tests/modules/ui_patterns_test/components/blockquote/blockquote.component.yml new file mode 100644 index 00000000..94089024 --- /dev/null +++ b/tests/modules/ui_patterns_test/components/blockquote/blockquote.component.yml @@ -0,0 +1,37 @@ +$schema: https://git.drupalcode.org/project/sdc/-/raw/1.x/src/metadata.schema.json +name: Blockquote +description: "For quoting blocks of content from another source within your document." +links: + - "https://getbootstrap.com/docs/5.3/content/typography/#blockquotes" +group: "Typography" +props: + type: object + properties: + dummy_prop: + title: "Dummy Prop" + description: "The attributes to customize the figcaption tag." + type: "object" +slots: + content: + title: "Content" + description: "The quote." + footer: + title: "Footer" + description: "For identifying the source. Wrap the name of the source work in ." +stories: + preview: + title: "Preview" + description: "The default preview from UI Patterns 1.x" + slots: + content: + type: "html_tag" + tag: "p" + value: "A well-known quote, contained in a blockquote element." + footer: + - type: "markup" + markup: "Someone famous in " + - type: "html_tag" + tag: "cite" + value: "Source Title" + attributes: + title: "Source Title" diff --git a/tests/modules/ui_patterns_test/components/blockquote/blockquote.twig b/tests/modules/ui_patterns_test/components/blockquote/blockquote.twig new file mode 100644 index 00000000..5721b60a --- /dev/null +++ b/tests/modules/ui_patterns_test/components/blockquote/blockquote.twig @@ -0,0 +1,14 @@ +{% if footer %} + +
+ {{ content }} +
+ + +{% else %} + + {{ content }} + +{% endif %} diff --git a/tests/modules/ui_patterns_test/components/button/button.component.yml b/tests/modules/ui_patterns_test/components/button/button.component.yml new file mode 100644 index 00000000..9bc44c32 --- /dev/null +++ b/tests/modules/ui_patterns_test/components/button/button.component.yml @@ -0,0 +1,137 @@ +$schema: https://git.drupalcode.org/project/sdc/-/raw/1.x/src/metadata.schema.json +name: "Button" +description: "For actions in forms, dialogs, and more with support for multiple sizes, states, and more." +links: + - "https://getbootstrap.com/docs/5.3/components/buttons/" +group: "Button" +template: pattern-button.html.twig +variants: + default: + title: "Default" + description: "No 'btn' class added." + primary__sm: + title: "Primary small" + secondary__sm: + title: "Secondary small" + success__sm: + title: "Success small" + danger__sm: + title: "Danger small" + warning__sm: + title: "Warning small" + info__sm: + title: "Info small" + light__sm: + title: "Light small" + dark__sm: + title: "Dark small" + link__sm: + title: "Link small" + primary: + title: "Primary" + secondary: + title: "Secondary" + success: + title: "Success" + danger: + title: "Danger" + warning: + title: "Warning" + info: + title: "Info" + light: + title: "Light" + dark: + title: "Dark" + link: + title: "Link" + primary__lg: + title: "Primary large" + secondary__lg: + title: "Secondary large" + success__lg: + title: "Success large" + danger__lg: + title: "Danger large" + warning__lg: + title: "Warning large" + info__lg: + title: "Info large" + light__lg: + title: "Light large" + dark__lg: + title: "Dark large" + link__lg: + title: "Link large" + outline_primary__sm: + title: "Outline Primary small" + outline_secondary__sm: + title: "Outline Secondary small" + outline_success__sm: + title: "Outline Success small" + outline_danger__sm: + title: "Outline Danger small" + outline_warning__sm: + title: "Outline Warning small" + outline_info__sm: + title: "Outline Info small" + outline_light__sm: + title: "Outline Light small" + outline_dark__sm: + title: "Outline Dark small" + outline_primary: + title: "Outline Primary" + outline_secondary: + title: "Outline Secondary" + outline_success: + title: "Outline Success" + outline_danger: + title: "Outline Danger" + outline_warning: + title: "Outline Warning" + outline_info: + title: "Outline Info" + outline_light: + title: "Outline Light" + outline_dark: + title: "Outline Dark" + outline_primary__lg: + title: "Outline Primary large" + outline_secondary__lg: + title: "Outline Secondary large" + outline_success__lg: + title: "Outline Success large" + outline_danger__lg: + title: "Outline Danger large" + outline_warning__lg: + title: "Outline Warning large" + outline_info__lg: + title: "Outline Info large" + outline_light__lg: + title: "Outline Light large" + outline_dark__lg: + title: "Outline Dark large" +props: + type: object + properties: + disabled: + title: "Disabled?" + description: "Is the button disabled?" + type: boolean + url: + title: "URL" + type: string + format: uri-reference +slots: + label: + title: "Label" + description: "The button label." +stories: + preview: + title: "Preview" + description: "The default preview from UI Patterns 1.x" + props: + disabled: false + url: "https://example.com" + slots: + label: "Submit" diff --git a/modules/ui_patterns_library/tests/modules/ui_patterns_library_module_test/templates/with_local_libraries/js/library_two_2.js b/tests/modules/ui_patterns_test/components/button/button.twig similarity index 100% rename from modules/ui_patterns_library/tests/modules/ui_patterns_library_module_test/templates/with_local_libraries/js/library_two_2.js rename to tests/modules/ui_patterns_test/components/button/button.twig diff --git a/tests/modules/ui_patterns_test/components/button/pattern-button.html.twig b/tests/modules/ui_patterns_test/components/button/pattern-button.html.twig new file mode 100644 index 00000000..5fc3ddb9 --- /dev/null +++ b/tests/modules/ui_patterns_test/components/button/pattern-button.html.twig @@ -0,0 +1,31 @@ +{% if variant and variant|lower != 'default' and variant|lower != 'dropdown_item' %} + {% set variants = variant|split('__')|map(v => v|lower|replace({(v): 'btn-' ~ v})|replace({'_': '-'})) %} + {% set attributes = attributes.addClass(variants) %} + {% set attributes = attributes.addClass('btn') %} +{% endif %} + +{% set attributes = (variant|lower == 'dropdown_item') ? attributes.addClass('dropdown-item') : attributes %} + +{% if label_visually_hidden %} + {% set label %} + + {{ label }} + + {% endset %} +{% endif %} + +{% if url or attributes.href %} + {% set url = url|default(attributes.href) %} + {% set attributes = attributes.setAttribute('href', url) %} + {% if disabled %} + {% set attributes = attributes.setAttribute('href', false).setAttribute('tabindex', '-1').setAttribute('aria-disabled', 'true').addClass('disabled') %} + {% endif %} + + {{ label }} +{% else %} + {% if disabled %} + {% set attributes = attributes.setAttribute('disabled', '') %} + {% endif %} + + {{ label }} +{% endif %} diff --git a/tests/modules/ui_patterns_test/components/card/card--horizontal.twig b/tests/modules/ui_patterns_test/components/card/card--horizontal.twig new file mode 100644 index 00000000..9e579b35 --- /dev/null +++ b/tests/modules/ui_patterns_test/components/card/card--horizontal.twig @@ -0,0 +1,20 @@ + +
+
+ {{ image|add_class('img-fluid rounded-start') }} +
+
+ {% if header %} +
+ {{ header }} +
+ {% endif %} + {{ content }} + {% if footer %} + + {% endif %} +
+
+ diff --git a/tests/modules/ui_patterns_test/components/card/card.component.yml b/tests/modules/ui_patterns_test/components/card/card.component.yml new file mode 100644 index 00000000..4783fdfe --- /dev/null +++ b/tests/modules/ui_patterns_test/components/card/card.component.yml @@ -0,0 +1,75 @@ +$schema: https://git.drupalcode.org/project/sdc/-/raw/1.x/src/metadata.schema.json +name: "Card" +description: "A card is a flexible and extensible content container. It includes options for headers and footers, a wide variety of content, contextual background colors, and powerful display options." +links: + - "https://getbootstrap.com/docs/5.3/components/card/" +group: "Card" +variants: + default: + title: "Default" + horizontal: + title: "Horizontal" +props: + type: object + properties: + image_position: + title: "Image position" + description: "Only for default variant." + type: "string" + enum: + - top + - bottom + image_col_classes: + title: "Image column classes" + description: "Only for horizontal variant." + type: "string" + default: "col-md-4" + content_col_classes: + title: "Content column classes" + description: "Only for horizontal variant." + type: "string" + default: "col-md-8" +slots: + image: + title: "Image" + header: + title: "Header" + content: + title: "Content" + footer: + title: "Footer" +stories: + preview: + title: "Preview" + description: "The default preview from UI Patterns 1.x" + props: + image_position: "top" + image_col_classes: "col-md-4" + content_col_classes: "col-md-8" + slots: + image: + theme: "image" + uri: "data:image/svg+xml;base64,PHN2ZyBzdHlsZT0iZm9udC1zaXplOiAxLjEyNXJlbTsgdGV4dC1hbmNob3I6IG1pZGRsZTsgdXNlci1zZWxlY3Q6IG5vbmU7IiB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIyNTQiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgcHJlc2VydmVBc3BlY3RSYXRpbz0ieE1pZFlNaWQgc2xpY2UiIGZvY3VzYWJsZT0iZmFsc2UiIHJvbGU9ImltZyIgYXJpYS1sYWJlbD0iUGxhY2Vob2xkZXI6IEltYWdlIGNhcCI+PHRpdGxlPlBsYWNlaG9sZGVyPC90aXRsZT48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjODY4ZTk2Ij48L3JlY3Q+PHRleHQgeD0iNTAlIiB5PSI1MCUiIGZpbGw9IiNkZWUyZTYiIGR5PSIuM2VtIj5JbWFnZSBjYXA8L3RleHQ+PC9zdmc+Cg==" + alt: "© 2017 John Smith photography" + header: "Featured" + content: + - type: "component" + component: "ui_patterns_test:button" + props: + variant: "primary" + slots: + label: "Go somewhere" + - type: "html_tag" + tag: "a" + value: "Card link" + attributes: + href: "#" + - type: "html_tag" + tag: "a" + value: "Another link" + attributes: + href: "#" + footer: + type: "html_tag" + tag: "span" + value: "2 days ago" diff --git a/tests/modules/ui_patterns_test/components/card/card.twig b/tests/modules/ui_patterns_test/components/card/card.twig new file mode 100644 index 00000000..e7e33a55 --- /dev/null +++ b/tests/modules/ui_patterns_test/components/card/card.twig @@ -0,0 +1,19 @@ + + {% if image and image_position != 'bottom' %} + {{ image|add_class('card-img-top') }} + {% endif %} + {% if header %} +
+ {{ header }} +
+ {% endif %} + {{ content }} + {% if footer %} + + {% endif %} + {% if image and image_position == 'bottom' %} + {{ image|add_class('card-img-bottom') }} + {% endif %} + diff --git a/tests/modules/ui_patterns_test/components/close_button/close_button.component.yml b/tests/modules/ui_patterns_test/components/close_button/close_button.component.yml new file mode 100644 index 00000000..a2f5abe6 --- /dev/null +++ b/tests/modules/ui_patterns_test/components/close_button/close_button.component.yml @@ -0,0 +1,29 @@ +$schema: https://git.drupalcode.org/project/sdc/-/raw/1.x/src/metadata.schema.json +name: "Close button" +description: "A generic close button for dismissing content like modals and alerts." +links: + - "https://getbootstrap.com/docs/5.3/components/close-button" +group: "Button" +variants: + default: + title: "Default" + white: + title: "White (deprecated)" +props: + type: object + properties: + disabled: + title: "Disabled?" + description: "Is the button disabled?" + type: "boolean" + aria_label: + title: "Aria label" + description: "Name of the close button for assistive technology." + type: "string" +stories: + preview: + title: "Preview" + description: "The default preview from UI Patterns 1.x" + props: + disabled: false + aria_label: "Close" diff --git a/tests/modules/ui_patterns_test/components/close_button/close_button.twig b/tests/modules/ui_patterns_test/components/close_button/close_button.twig new file mode 100644 index 00000000..d1350276 --- /dev/null +++ b/tests/modules/ui_patterns_test/components/close_button/close_button.twig @@ -0,0 +1,11 @@ +{% if variant and variant|lower != 'default' %} + {% set attributes = attributes.addClass('btn-close-' ~ variant) %} +{% endif %} +{% set attributes = attributes.addClass('btn-close') %} + +{% set attributes = attributes.setAttribute('aria-label', aria_label|default('Close'|t)) %} +{% if disabled %} + {% set attributes = attributes.setAttribute('disabled', disabled) %} +{% endif %} + + diff --git a/tests/modules/ui_patterns_test/components/figure/figure.component.yml b/tests/modules/ui_patterns_test/components/figure/figure.component.yml new file mode 100644 index 00000000..097957e3 --- /dev/null +++ b/tests/modules/ui_patterns_test/components/figure/figure.component.yml @@ -0,0 +1,33 @@ +$schema: https://git.drupalcode.org/project/sdc/-/raw/1.x/src/metadata.schema.json +name: "Figure" +description: "Used to display a piece of self-contained content (illustrations, diagrams, photos, code, etc) along with an optional caption. This content can be removed from the document without affecting the meaning of the document." +links: + - "https://getbootstrap.com/docs/5.3/content/figures/" +replaces: "ui_patterns_tests:replaced_figure" +props: + type: object + properties: + figcaption_attributes: + title: "Figcaption attributes" + description: "The attributes to customize the figcaption tag." + "$ref": "ui-patterns://attributes" +slots: + image: + title: "Image" + description: "The content of the figure." + caption: + title: "Caption" + description: "The caption that appears under the content." +stories: + preview: + title: "Preview" + description: "The default preview from UI Patterns 1.x" + props: + figcaption_attributes: + class: ["text-end"] + slots: + image: + theme: "image" + uri: "data:image/svg+xml;base64,PHN2ZyBzdHlsZT0iZm9udC1zaXplOiAxLjEyNXJlbTsgdGV4dC1hbmNob3I6IG1pZGRsZTsgdXNlci1zZWxlY3Q6IG5vbmU7IiB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIyNTQiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgcHJlc2VydmVBc3BlY3RSYXRpbz0ieE1pZFlNaWQgc2xpY2UiIGZvY3VzYWJsZT0iZmFsc2UiIHJvbGU9ImltZyIgYXJpYS1sYWJlbD0iUGxhY2Vob2xkZXI6IEltYWdlIGNhcCI+PHRpdGxlPlBsYWNlaG9sZGVyPC90aXRsZT48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjODY4ZTk2Ij48L3JlY3Q+PHRleHQgeD0iNTAlIiB5PSI1MCUiIGZpbGw9IiNkZWUyZTYiIGR5PSIuM2VtIj5JbWFnZSBjYXA8L3RleHQ+PC9zdmc+Cg==" + alt: "© 2017 John Smith photography" + caption: "A caption for the above image." diff --git a/tests/modules/ui_patterns_test/components/figure/figure.twig b/tests/modules/ui_patterns_test/components/figure/figure.twig new file mode 100644 index 00000000..21dd87df --- /dev/null +++ b/tests/modules/ui_patterns_test/components/figure/figure.twig @@ -0,0 +1,6 @@ + + {{ image|add_class('figure-img') }} + + {{ caption }} + + diff --git a/tests/modules/ui_patterns_test/components/my-widget/my-widget.component.yml b/tests/modules/ui_patterns_test/components/my-widget/my-widget.component.yml new file mode 100644 index 00000000..4fe76844 --- /dev/null +++ b/tests/modules/ui_patterns_test/components/my-widget/my-widget.component.yml @@ -0,0 +1,47 @@ +$schema: https://git.drupalcode.org/project/sdc/-/raw/1.x/src/metadata.schema.json +name: Widget +description: Widget Test with title and a CTA link +libraryOverrides: + dependencies: + - core/drupal +variants: + default: + label: Default + other: + label: other +props: + type: object + properties: + heading: + title: Heading + description: The title for the banner text. + type: string + default: "Asdf" + examples: + - Join us at The Conference + ctaText: + title: CTA Text + type: string + examples: + - Click me! + ctaHref: + title: CTA Href + type: string + examples: + - "https://www.example.org" + ctaTarget: + title: CTA Target + type: string + enum: + - "" + - _blank + image: + title: Media Image + description: Background image for the banner. + type: string +slots: + widget_body: + title: Body + description: The content of the banner. + examples: + -

Foo is NOT bar.

diff --git a/tests/modules/ui_patterns_test/components/my-widget/my-widget.css b/tests/modules/ui_patterns_test/components/my-widget/my-widget.css new file mode 100644 index 00000000..ea702632 --- /dev/null +++ b/tests/modules/ui_patterns_test/components/my-widget/my-widget.css @@ -0,0 +1,24 @@ +.component--my-widget { + position: relative; + width: 80%; + max-width: 1200px; + padding: 2em; + color: white; + background: black; + background-size: cover; +} +.component--my-widget--header { + display: flex; + align-items: center; + justify-content: space-between; +} +.component--my-widget--header h3 { + margin: 0; + font-size: 2em; +} +.component--my-widget--body > * { + margin-bottom: 0; +} +.component--my-widget--body > :first-child { + margin-top: 1em; +} diff --git a/tests/modules/ui_patterns_test/components/my-widget/my-widget.twig b/tests/modules/ui_patterns_test/components/my-widget/my-widget.twig new file mode 100644 index 00000000..4d44e888 --- /dev/null +++ b/tests/modules/ui_patterns_test/components/my-widget/my-widget.twig @@ -0,0 +1,16 @@ +{# Prepare presentational attributes #} +{% if image is not empty %} + {% set attributes = attributes.setAttribute('style', 'background-image: linear-gradient(to right, black, black, rgba(0, 0, 0, 70%), transparent), url("' ~ image ~ '");') %} +{% endif %} + +{# Markup for the component #} +
+
+

Heading: {{ heading }}

+
+
+ {% block widget_body %} + {{ widget_body }} + {% endblock %} +
+
diff --git a/tests/modules/ui_patterns_test/components/progress/path/to/template/progress.twig b/tests/modules/ui_patterns_test/components/progress/path/to/template/progress.twig new file mode 100644 index 00000000..b7bc09f6 --- /dev/null +++ b/tests/modules/ui_patterns_test/components/progress/path/to/template/progress.twig @@ -0,0 +1,31 @@ +{% if variant and variant|lower != 'default' %} + {% set variants = variant|split('__')|map(v => v|lower|replace({(v): 'progress-bar-' ~ v})|replace({'_': '-'})) %} + {% set attributes = attributes.addClass(variants) %} +{% endif %} + +{% set wrapper_attributes = create_attribute() %} +{# Handle wrapper ID. #} +{% if attributes.hasAttribute('id') %} + {% set wrapper_attributes = wrapper_attributes.setAttribute('id', attributes.offsetGet('id')) %} + {% set attributes = attributes.removeAttribute('id') %} +{% endif %} + +{% set wrapper_attributes = bar_height ? wrapper_attributes.setAttribute('style', 'height:' ~ bar_height ~ 'px') : wrapper_attributes %} +{% set percent = percent|default(0) %} +{% set min = min|default(0) %} +{% set max = max|default(100) %} +{% set width = (percent * 100) // max %} + + + + {{ label }} + + diff --git a/tests/modules/ui_patterns_test/components/progress/progress.component.yml b/tests/modules/ui_patterns_test/components/progress/progress.component.yml new file mode 100644 index 00000000..b565e160 --- /dev/null +++ b/tests/modules/ui_patterns_test/components/progress/progress.component.yml @@ -0,0 +1,50 @@ +$schema: https://git.drupalcode.org/project/sdc/-/raw/1.x/src/metadata.schema.json +name: "Progress" +description: "The progress element displays an indicator showing the completion progress of a task, typically in the form of a bar. Progress components are built with two HTML elements, some CSS to set the width, and a few attributes. Bootstrap does not use the HTML5 element, ensuring you can stack progress bars, animate them, and place text labels over them." +links: + - "https://getbootstrap.com/docs/5.3/components/progress/" +template: path/to/template/progress.twig +variants: + default: + title: "Default" + striped: + title: "Striped" + striped__animated: + title: "Animated stripes" +props: + type: object + properties: + aria_label: + title: "Aria label" + description: "Name of the progress bar for assistive technology." + type: "string" + percent: + title: "Total progress (%)" + description: "Width of the progress element representing total progress (25%, 50%, etc.)." + type: "integer" + min: + title: "Minimum value" + description: "Minimum value of the progress element (default is 0). Used for an aria attribute." + type: "integer" + max: + title: "Maximum value" + description: "Maximum value of the progress element (default is 100). Used for an aria attribute." + type: "integer" + bar_height: + title: "Height" + description: "Height of progress element in pixels (px). Leave empty for default height." + type: "integer" +slots: + label: + title: "Label" + description: "Text shown inside the progress bar." +stories: + preview: + title: "Preview" + description: "The default preview from UI Patterns 1.x" + props: + percent: 50 + min: 0 + max: 100 + slots: + label: "Label" diff --git a/tests/modules/ui_patterns_render_test/templates/pattern-foo.html.twig b/tests/modules/ui_patterns_test/components/progress/progress.twig similarity index 100% rename from tests/modules/ui_patterns_render_test/templates/pattern-foo.html.twig rename to tests/modules/ui_patterns_test/components/progress/progress.twig diff --git a/tests/modules/ui_patterns_test/components/prop_types_tests/prop_types_tests.component.yml b/tests/modules/ui_patterns_test/components/prop_types_tests/prop_types_tests.component.yml new file mode 100644 index 00000000..33b1b241 --- /dev/null +++ b/tests/modules/ui_patterns_test/components/prop_types_tests/prop_types_tests.component.yml @@ -0,0 +1,102 @@ +$schema: https://git.drupalcode.org/project/sdc/-/raw/1.x/src/metadata.schema.json +name: "Prop types tests" +description: "A card is a flexible and extensible content container. It includes options for headers and footers, a wide variety of content, contextual background colors, and powerful display options." +props: + type: object + properties: + string: + title: "String" + type: "string" + string_enum: + title: "String with enum" + type: "string" + enum: + - top + - bottom + string_length: + title: "String with length" + type: "string" + maxLength: 10 + boolean: + title: "Boolean" + type: "boolean" + integer: + title: "Integer" + type: "integer" + number_1: + title: "Number" + type: "number" + number_2: + title: "Number with min max" + type: "number" + minimum: 2 + maximum: 10 + attributes: + title: "Attributes" + type: object + patternProperties: + ".+": + anyOf: + - type: + - string + - number + - type: array + items: + anyOf: + - type: number + - type: string + url_1: + title: "Url (explicit typing)" + $ref: "ui-patterns://url" + url_2: + title: "Url (implicit typing, exact type)" + type: "string" + format: "iri-reference" + url_3: + title: "Url (implicit typing, other type)" + description: Test format comaptibility + type: "string" + format: "uri" + color: + title: "Color RGB (6 or 3 hex)" + type: "string" + pattern: "^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$" + color_6: + title: "Color RGB (6 hex)" + description: Test Regexp comaptibility API + type: "string" + pattern: "^#[A-Fa-f0-9]{6}$" + color_3: + title: "Color RGB (3 hex)" + description: Test Regexp comaptibility API + type: "string" + pattern: "^#[A-Fa-f0-9]{3}$" + object: + title: "Undefined (Empty object)" + type: "object" + array: + title: "Empty array" + type: "array" + links: + title: "Links" + type: array + items: + type: object + properties: + title: { type: string } + attributes: { $ref: "ui-patterns://attributes" } + below: { $ref: "ui-patterns://links" } + links_2: + title: "Links with extra property" + type: array + items: + type: object + properties: + title: { type: string } + attributes: { $ref: "ui-patterns://attributes" } + below: { $ref: "ui-patterns://links" } + extra: { type: string } + machine_name: + title: "Machine name" + type: string + pattern: '^[A-Za-z]+\w*$' diff --git a/tests/modules/ui_patterns_test/components/prop_types_tests/prop_types_tests.twig b/tests/modules/ui_patterns_test/components/prop_types_tests/prop_types_tests.twig new file mode 100644 index 00000000..8511efcc --- /dev/null +++ b/tests/modules/ui_patterns_test/components/prop_types_tests/prop_types_tests.twig @@ -0,0 +1 @@ +

Empty template

diff --git a/tests/modules/ui_patterns_test/components/replaced_figure/replaced_figure.component.yml b/tests/modules/ui_patterns_test/components/replaced_figure/replaced_figure.component.yml new file mode 100644 index 00000000..d4ea2091 --- /dev/null +++ b/tests/modules/ui_patterns_test/components/replaced_figure/replaced_figure.component.yml @@ -0,0 +1,13 @@ +$schema: https://git.drupalcode.org/project/sdc/-/raw/1.x/src/metadata.schema.json +name: "Figure without caption (replaced by the 'real' Figure)" +props: + type: object + properties: + figcaption_attributes: + title: "Figcaption attributes" + description: "The attributes to customize the figcaption tag." + "$ref": "ui-patterns://attributes" +slots: + image: + title: "Image" + description: "The content of the figure." diff --git a/tests/modules/ui_patterns_test/components/replaced_figure/replaced_figure.twig b/tests/modules/ui_patterns_test/components/replaced_figure/replaced_figure.twig new file mode 100644 index 00000000..5c9670dc --- /dev/null +++ b/tests/modules/ui_patterns_test/components/replaced_figure/replaced_figure.twig @@ -0,0 +1,3 @@ + + {{ image|add_class('figure-img') }} + diff --git a/tests/modules/ui_patterns_test/src/Plugin/UiPatterns/Source/Foo.php b/tests/modules/ui_patterns_test/src/Plugin/UiPatterns/Source/Foo.php new file mode 100644 index 00000000..61e42bf7 --- /dev/null +++ b/tests/modules/ui_patterns_test/src/Plugin/UiPatterns/Source/Foo.php @@ -0,0 +1,29 @@ +assertSession(); - - $this->enableTwigDebugMode(); - - $user = $this->drupalCreateUser([], NULL, TRUE); - $this->drupalLogin($user); - - $this->drupalGet('/patterns'); - - // Assert correct variant suggestions. - $suggestions = [ - 'pattern-foo--variant-default--preview.html.twig', - 'pattern-foo--variant-default.html.twig', - 'pattern-foo--preview.html.twig', - 'pattern-foo.html.twig', - 'pattern-foo-bar--variant-default--preview.html.twig', - 'pattern-foo-bar--variant-default.html.twig', - 'pattern-foo-bar--preview.html.twig', - 'pattern-foo-bar.html.twig', - ]; - foreach ($suggestions as $suggestion) { - $assert_session->responseContains($suggestion); - } - } - -} diff --git a/tests/src/Kernel/AbstractUiPatternsTest.php b/tests/src/Kernel/AbstractUiPatternsTest.php deleted file mode 100644 index 24ecfe72..00000000 --- a/tests/src/Kernel/AbstractUiPatternsTest.php +++ /dev/null @@ -1,40 +0,0 @@ -getFixturePath() . '/' . $filepath)); - } - -} diff --git a/tests/src/Kernel/Plugin/Deriver/YamlDeriverTest.php b/tests/src/Kernel/Plugin/Deriver/YamlDeriverTest.php deleted file mode 100644 index a7bb4053..00000000 --- a/tests/src/Kernel/Plugin/Deriver/YamlDeriverTest.php +++ /dev/null @@ -1,50 +0,0 @@ -container->get('theme_installer')->install([$default_theme]); - $this->container->get('config.factory')->getEditable('system.theme')->set('default', $default_theme)->save(); - } - - /** - * Test get derivative definitions. - * - * @covers ::getDerivativeDefinitions - */ - public function testGetDerivativeDefinitions() { - UiPatterns::getManager()->clearCachedDefinitions(); - foreach (UiPatterns::getManager()->getDefinitions() as $definition) { - $this->assertNotEmpty($definition->id(), 'Pattern definition id is empty'); - $this->assertNotEmpty($definition->getProvider(), 'Pattern definition provider is empty'); - $this->assertNotEmpty($definition->getBasePath(), 'Pattern definition base path is empty'); - } - } - -} diff --git a/tests/src/Kernel/Plugin/PatternBaseTest.php b/tests/src/Kernel/Plugin/PatternBaseTest.php deleted file mode 100644 index 5298b389..00000000 --- a/tests/src/Kernel/Plugin/PatternBaseTest.php +++ /dev/null @@ -1,62 +0,0 @@ -getUiPatternBaseMock($actual); - /** @var \Drupal\ui_patterns\Plugin\PatternBase $pattern */ - $libraries = $pattern->getLibraryDefinitions(); - $this->assertEquals($expected, $libraries); - } - - /** - * Data provider for rendering tests. - * - * The actual data is read from fixtures stored in a YAML configuration. - * - * @return array - * A set of dump data for testing. - */ - public function hookLibraryInfoBuildDataProvider() { - return $this->getFixtureContent('libraries.yml'); - } - - /** - * Get PatternBase mock. - * - * @param array $plugin_definition - * Plugin definition. - * @param array $methods - * List of methods to mock. - * - * @return \PHPUnit\Framework\MockObject\MockObject - * Mock object. - */ - protected function getUiPatternBaseMock(array $plugin_definition = [], array $methods = []) { - return $this->getMockForAbstractClass(PatternBase::class, [ - [], - 'plugin_id', - $plugin_definition, - \Drupal::service('app.root'), - \Drupal::service('module_handler'), - ], '', TRUE, TRUE, TRUE, $methods); - } - -} diff --git a/tests/src/Kernel/PropTypePluginManagerTest.php b/tests/src/Kernel/PropTypePluginManagerTest.php new file mode 100644 index 00000000..2ce78e06 --- /dev/null +++ b/tests/src/Kernel/PropTypePluginManagerTest.php @@ -0,0 +1,37 @@ +getPropTypePlugin(['type' => 'string']); + self::assertInstanceOf(StringPropType::class, $plugin_type); + } + +} diff --git a/tests/src/Kernel/SourcePluginManagerTest.php b/tests/src/Kernel/SourcePluginManagerTest.php new file mode 100644 index 00000000..68f94c75 --- /dev/null +++ b/tests/src/Kernel/SourcePluginManagerTest.php @@ -0,0 +1,44 @@ +getSourcePlugins('string', 'test', ['title' => 'test title']); + /** @var SourcePluginBase $source */ + foreach ($sources as $source) { + self::assertNotNull($source); + self::assertInstanceOf(SourcePluginBase::class, $source); + self::assertNotNull($source->getPropId()); + self::assertNotNull($source->getPropDefinition()); + } + self::assertGreaterThan(1, $sources); + } + +} diff --git a/tests/src/Kernel/TypedData/PatternDataDefinitionTest.php b/tests/src/Kernel/TypedData/PatternDataDefinitionTest.php deleted file mode 100644 index ebc90cc1..00000000 --- a/tests/src/Kernel/TypedData/PatternDataDefinitionTest.php +++ /dev/null @@ -1,41 +0,0 @@ -create($definition, $data)->validate(); - - $actual = []; - foreach ($violations as $violation) { - $actual[] = $violation->getPropertyPath() . ': ' . $violation->getMessage(); - } - $this->assertEquals($expected, $actual); - } - - /** - * Return validation data. - * - * @return array - * Pattern validation data. - */ - public function validationProvider() { - return $this->getFixtureContent('validation.yml'); - } - -} diff --git a/tests/src/Kernel/UiPatternsManagerTest.php b/tests/src/Kernel/UiPatternsManagerTest.php deleted file mode 100644 index a7f3198f..00000000 --- a/tests/src/Kernel/UiPatternsManagerTest.php +++ /dev/null @@ -1,49 +0,0 @@ -container->get('theme_installer')->install([$default_theme]); - $this->container->get('config.factory')->getEditable('system.theme')->set('default', $default_theme)->save(); - } - - /** - * Test UiPatternsManager::getPatternDefinition. - * - * @covers ::getPatterns - */ - public function testGetPattern() { - $manager = UiPatterns::getManager(); - $definitions = $manager->getDefinitions(); - - foreach ($manager->getPatterns() as $pattern) { - $this->assertEquals($definitions[$pattern->getPluginId()]->id(), $pattern->getBaseId()); - } - } - -} diff --git a/tests/src/Kernel/UiPatternsPreviewTest.php b/tests/src/Kernel/UiPatternsPreviewTest.php deleted file mode 100644 index affbfae1..00000000 --- a/tests/src/Kernel/UiPatternsPreviewTest.php +++ /dev/null @@ -1,51 +0,0 @@ -renderRoot($render); - $this->assertEquals($expected, $render["#context"]->getType()); - } - - /** - * Data provider for Process Context tests. - * - * The actual data is read from fixtures stored in a YAML configuration. - * - * @return array - * A set of dump data for testing. - */ - public function processContextDataProvider() { - return $this->getFixtureContent('preview_process.yml'); - } - -} diff --git a/tests/src/Kernel/UiPatternsSourceManagerTest.php b/tests/src/Kernel/UiPatternsSourceManagerTest.php deleted file mode 100644 index dd218a56..00000000 --- a/tests/src/Kernel/UiPatternsSourceManagerTest.php +++ /dev/null @@ -1,49 +0,0 @@ -getDefinitions(); - $this->assertNotEmpty($definitions); - $this->assertArrayHasKey('test_source', $definitions); - - $expected = [ - ['name' => 'field_1', 'label' => 'Field 1'], - ['name' => 'field_2', 'label' => 'Field 2'], - ['name' => 'field_3', 'label' => 'Field 3'], - ['name' => 'field_4', 'label' => 'Field 4'], - ['name' => 'field_5', 'label' => 'Field 5'], - ]; - - /** @var \Drupal\ui_patterns\Plugin\PatternSourceBase $plugin */ - $plugin = $plugin_manager->createInstance('test_source'); - foreach ($plugin->getSourceFields() as $key => $field) { - $this->assertEquals($expected[$key]['name'], $field->getFieldName()); - $this->assertEquals($expected[$key]['label'], $field->getFieldLabel()); - } - } - -} diff --git a/tests/src/Traits/RenderTrait.php b/tests/src/Traits/RenderTrait.php deleted file mode 100644 index 1ad8a160..00000000 --- a/tests/src/Traits/RenderTrait.php +++ /dev/null @@ -1,28 +0,0 @@ -container->get('renderer')->renderRoot($elements); - } - -} diff --git a/tests/src/Traits/TwigDebugTrait.php b/tests/src/Traits/TwigDebugTrait.php deleted file mode 100644 index d37416e2..00000000 --- a/tests/src/Traits/TwigDebugTrait.php +++ /dev/null @@ -1,22 +0,0 @@ -container->getParameter('twig.config'); - $parameters['debug'] = TRUE; - $this->setContainerParameter('twig.config', $parameters); - $this->rebuildContainer(); - $this->resetAll(); - } - -} diff --git a/tests/src/Unit/AbstractUiPatternsTest.php b/tests/src/Unit/AbstractUiPatternsTest.php deleted file mode 100644 index 0667fe3f..00000000 --- a/tests/src/Unit/AbstractUiPatternsTest.php +++ /dev/null @@ -1,26 +0,0 @@ - $value]); - $this->assertEquals(call_user_func([$pattern_definition, $getter]), $value); - } - - /** - * Test field singleton. - * - * @dataProvider definitionGettersProvider - */ - public function testFields() { - $fields = [ - 'name' => [ - 'name' => 'name', - 'label' => 'Label', - ], - ]; - $pattern_definition = new PatternDefinition(); - $pattern_definition->setFields($fields); - $this->assertEquals( - [ - $fields['name']['label'], - $fields['name']['name'], - NULL, - NULL, - NULL, - ], - [ - $pattern_definition->getField('name')->getLabel(), - $pattern_definition->getField('name')->getName(), - $pattern_definition->getField('name')->getType(), - $pattern_definition->getField('name')->getDescription(), - $pattern_definition->getField('name')->getPreview(), - ]); - - $pattern_definition->getField('name')->setType('type'); - $pattern_definition->getField('name')->setPreview('preview'); - $pattern_definition->getField('name')->setDescription('description'); - - $this->assertEquals( - [ - 'type', - 'description', - 'preview', - ], - [ - $pattern_definition->getField('name')->getType(), - $pattern_definition->getField('name')->getDescription(), - $pattern_definition->getField('name')->getPreview(), - ]); - } - - /** - * Test fields processing. - * - * @dataProvider fieldsProcessingProvider - */ - public function testFieldsProcessing($actual, $expected) { - $pattern_definition = new PatternDefinition(); - $data = $pattern_definition->setFields($actual)->toArray(); - $this->assertEquals($expected, $data['fields']); - } - - /** - * Provider. - * - * @return array - * Data. - */ - public function fieldsProcessingProvider() { - return Yaml::decode(file_get_contents($this->getFixturePath() . '/definition/fields_processing.yml')); - } - - /** - * Test fields processing. - * - * @dataProvider variantsProcessingProvider - */ - public function testVariantsProcessing($actual, $expected) { - $pattern_definition = new PatternDefinition(); - $data = $pattern_definition->setVariants($actual)->toArray(); - $this->assertEquals($expected, $data['variants']); - } - - /** - * Provider. - * - * @return array - * Data. - */ - public function variantsProcessingProvider() { - return Yaml::decode(file_get_contents($this->getFixturePath() . '/definition/variants_processing.yml')); - } - - /** - * Provider. - * - * @return array - * Data. - */ - public function definitionGettersProvider() { - return [ - ['getProvider', 'provider', 'my_module'], - ['id', 'id', 'pattern_id'], - ['getLabel', 'label', 'Pattern label'], - ['getDescription', 'description', 'Pattern description.'], - ['getUse', 'use', 'template.twig'], - ['hasCustomThemeHook', 'custom theme hook', FALSE], - ['getThemeHook', 'theme hook', 'eme hook: custom_my_theme_hook'], - ['getTemplate', 'template', 'my-template.html.twig'], - ['getFileName', 'file name', '/path/to/filename.ui_patterns.yml'], - ['getClass', 'class', '\Drupal\ui_patterns\MyClass'], - ['getBasePath', 'base path', '/path/to'], - ['getTags', 'tags', ['a', 'b']], - ]; - } - -} diff --git a/tests/src/Unit/Element/PatternPreviewTest.php b/tests/src/Unit/Element/PatternPreviewTest.php deleted file mode 100644 index 3bd41623..00000000 --- a/tests/src/Unit/Element/PatternPreviewTest.php +++ /dev/null @@ -1,29 +0,0 @@ -getFixturePath() . '/preview_markup.yml')); - foreach ($assertions as $assertion) { - $result = PatternPreview::getPreviewMarkup($assertion['actual']); - $this->assertEquals($assertion['expected'], $result); - } - } - -} diff --git a/tests/src/Unit/SchemaCompatibilityCheckerTest.php b/tests/src/Unit/SchemaCompatibilityCheckerTest.php new file mode 100644 index 00000000..19e07c2f --- /dev/null +++ b/tests/src/Unit/SchemaCompatibilityCheckerTest.php @@ -0,0 +1,49 @@ +isCompatible($checked_schema, $reference_schema)); + } + + public function provideIsCompatibleData() { + $data = []; + $sources = Yaml::decode(file_get_contents(__DIR__ . "/schema_compatibility_checker_data.yml")); + foreach ($sources as $source) { + foreach ($source["tests"] as $test) { + $data[] = [ + $source["label"] . ": " . $test["label"] . " is " . ($test["result"] ? "OK" : "KO"), + $test["schema"], + $source["schema"], + (bool) $test["result"], + ]; + } + }; + return $data; + } + +} diff --git a/tests/src/Unit/schema_compatibility_checker_data.yml b/tests/src/Unit/schema_compatibility_checker_data.yml new file mode 100644 index 00000000..837ee0f1 --- /dev/null +++ b/tests/src/Unit/schema_compatibility_checker_data.yml @@ -0,0 +1,227 @@ +# String prop type +- label: "String prop type" + schema: + type: string + tests: + - label: "String" + schema: + type: string + result: true + - label: "String with a format" + schema: + type: string + format: whatever + result: true + - label: "String with max length" + schema: + type: string + maxLength: 10 + result: true + +# Other string tests +- label: "String with max length" + schema: + type: string + maxLength: 10 + tests: + - label: "No length" + schema: + type: string + result: false + - label: "Same max length" + schema: + type: string + maxLength: 10 + result: true + - label: "Smaller max length" + schema: + type: string + maxLength: 5 + result: true + - label: "Larger max length" + schema: + type: string + maxLength: 20 + result: false +- label: "String with min length" + schema: + type: string + minLength: 10 + tests: + - label: "No length" + schema: + type: string + result: false + - label: "Same min length" + schema: + type: string + minLength: 10 + result: true + - label: "Smaller min length" + schema: + type: string + minLength: 5 + result: false + - label: "Larger min length" + schema: + type: string + minLength: 20 + result: true + +# Enum prop type +- label: "Enum prop type" + schema: + type: [string, number, integer] + enum: [] + tests: + - label: "Empty enum" + schema: + type: string + enum: [] + result: true + - label: "With strings" + schema: + type: string + enum: + - foo + - bar + - baz + result: true + - label: "With numbers" + schema: + type: number + enum: + - 4 + - 5 + - 8 + result: true + +# Other enum tests +- label: "Enum with 2 strings" + schema: + type: string + enum: + - foo + - bar + tests: + - label: "Same enum" + schema: + type: string + enum: + - foo + - bar + result: true + - label: "Enum with an addition" + schema: + type: string + enum: + - foo + - bar + - baz + result: false + - label: "Enum with a removal" + schema: + type: string + enum: + - foo + result: true + +# Number prop type +- label: "Number prop type" + schema: + type: number + tests: + - label: "Number" + schema: + type: number + result: true + - label: "Integer" + schema: + type: integer + result: true + +# Url prop type +- label: "URL prop type" + schema: + type: string + format: iri-reference + tests: + - label: "IRI Reference" + schema: + type: string + format: iri-reference + result: true + - label: "URI reference" + schema: + type: string + format: uri-reference + result: true + - label: "IRI" + schema: + type: string + format: iri + result: true + - label: "URI" + schema: + type: string + format: uri + result: true + +# Color prop type +- label: "Color prop type" + schema: + type: string + pattern: "^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$" + tests: + - label: "Color (6 or 3 hex)" + schema: + type: string + pattern: "^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$" + result: true +# - label: "Color (6)" +# schema: +# type: string +# pattern: "^#([A-Fa-f0-9]{6})$" +# result: true +# - label: "Color (3)" +# schema: +# type: string +# pattern: "^#([A-Fa-f0-9]{3})$" +# result: true + +# Links prop type +- label: "Links prop type" + schema: + type: array + items: + type: object + properties: + title: { type: string } + attributes: { $ref: "ui-patterns://attributes" } + below: { $ref: "ui-patterns://links" } + tests: + - label: "Links" + schema: + type: array + items: + type: object + properties: + title: { type: string } + attributes: { $ref: "ui-patterns://attributes" } + below: { $ref: "ui-patterns://links" } + result: true + - label: "Links with extra property on" + schema: + type: array + items: + type: object + properties: + title: { type: string } + attributes: { $ref: "ui-patterns://attributes" } + below: { $ref: "ui-patterns://links" } + extra: { type: string } + result: true + - label: "Simple array" + schema: + type: array + result: false diff --git a/tests/src/fixtures/definition/fields_processing.yml b/tests/src/fixtures/definition/fields_processing.yml deleted file mode 100644 index 97219e18..00000000 --- a/tests/src/fixtures/definition/fields_processing.yml +++ /dev/null @@ -1,58 +0,0 @@ -- actual: - - field1 - - field2 - expected: - field1: - name: field1 - label: field1 - description: ~ - type: ~ - preview: ~ - escape: TRUE - field2: - name: field2 - label: field2 - description: ~ - type: ~ - preview: ~ - escape: TRUE -- actual: - field1: Field 1 - field2: Field 2 - expected: - field1: - name: field1 - label: Field 1 - description: ~ - type: ~ - preview: ~ - escape: TRUE - field2: - name: field2 - label: Field 2 - description: ~ - type: ~ - preview: ~ - escape: TRUE -- actual: - field1: - label: field1 - expected: - field1: - name: field1 - label: field1 - description: ~ - type: ~ - preview: ~ - escape: TRUE -- actual: - - name: field1 - label: Field 1 - expected: - field1: - name: field1 - label: Field 1 - description: ~ - type: ~ - preview: ~ - escape: TRUE diff --git a/tests/src/fixtures/definition/variants_processing.yml b/tests/src/fixtures/definition/variants_processing.yml deleted file mode 100644 index d70600f2..00000000 --- a/tests/src/fixtures/definition/variants_processing.yml +++ /dev/null @@ -1,40 +0,0 @@ -- actual: - - variant1 - - variant2 - expected: - variant1: - name: variant1 - label: variant1 - description: ~ - variant2: - name: variant2 - label: variant2 - description: ~ -- actual: - variant1: Variant 1 - variant2: Variant 2 - expected: - variant1: - name: variant1 - label: Variant 1 - description: ~ - variant2: - name: variant2 - label: Variant 2 - description: ~ -- actual: - variant1: - label: variant1 - expected: - variant1: - name: variant1 - label: variant1 - description: ~ -- actual: - - name: variant1 - label: Variant 1 - expected: - variant1: - name: variant1 - label: Variant 1 - description: ~ diff --git a/tests/src/fixtures/libraries.yml b/tests/src/fixtures/libraries.yml deleted file mode 100644 index 19acdd1e..00000000 --- a/tests/src/fixtures/libraries.yml +++ /dev/null @@ -1,105 +0,0 @@ -- actual: - id: pattern_name - base path: /pattern/base/path - libraries: - - drupal/library_one - - drupal/library_two - - library_one: - css: - component: - library_one.css: {} - library_two.css: {} - library_three.css: {} - theme: - library_one.css: {} - library_two.css: {} - js: - library_one.js: {} - - library_two: - css: - component: - library_one.css: {} - js: - library_two.js: {} - expected: - pattern_name.library_one: - css: - component: - /pattern/base/path/library_one.css: {} - /pattern/base/path/library_two.css: {} - /pattern/base/path/library_three.css: {} - theme: - /pattern/base/path/library_one.css: {} - /pattern/base/path/library_two.css: {} - js: - /pattern/base/path/library_one.js: {} - pattern_name.library_two: - css: - component: - /pattern/base/path/library_one.css: {} - js: - /pattern/base/path/library_two.js: {} -- actual: - id: pattern_name - base path: /pattern/base/path - libraries: - - library_one: - css: - component: - library_one.css: {} - http://example.com/external.min.css: { type: external, minified: true } - library_two.css: {} - theme: - http://example.com/external.min.css: { type: external, minified: true } - js: - library_one.js: {} - http://example.com/external.min.js: { type: external, minified: true } - library_two.js: {} - expected: - pattern_name.library_one: - css: - component: - /pattern/base/path/library_one.css: {} - http://example.com/external.min.css: { type: external, minified: true } - /pattern/base/path/library_two.css: {} - theme: - http://example.com/external.min.css: { type: external, minified: true } - js: - /pattern/base/path/library_one.js: {} - http://example.com/external.min.js: { type: external, minified: true } - /pattern/base/path/library_two.js: {} -- actual: - id: pattern_name - libraries: - - drupal/library_one - - drupal/library_two - expected: [] -- actual: - id: pattern_name - base path: '' - libraries: - - library_one: - css: - component: - library_one.css: {} - js: - library_one.js: {} - - library_two: - css: - component: - library_one.css: {} - js: - library_two.js: {} - expected: - pattern_name.library_one: - css: - component: - library_one.css: {} - js: - library_one.js: {} - pattern_name.library_two: - css: - component: - library_one.css: {} - js: - library_two.js: {} diff --git a/tests/src/fixtures/pattern_element_libraries.yml b/tests/src/fixtures/pattern_element_libraries.yml deleted file mode 100644 index a679757a..00000000 --- a/tests/src/fixtures/pattern_element_libraries.yml +++ /dev/null @@ -1,24 +0,0 @@ -- actual: - id: pattern_name - libraries: - - drupal/library_one - - drupal/library_two - - library_one: - css: - component: - library_one.css: {} - theme: - library_one.css: {} - js: - library_one.js: {} - - library_two: - css: - component: - library_one.css: {} - js: - library_two.js: {} - expected: - - drupal/library_one - - drupal/library_two - - ui_patterns/pattern_name.library_one - - ui_patterns/pattern_name.library_two diff --git a/tests/src/fixtures/preview_markup.yml b/tests/src/fixtures/preview_markup.yml deleted file mode 100644 index 68892d70..00000000 --- a/tests/src/fixtures/preview_markup.yml +++ /dev/null @@ -1,14 +0,0 @@ -- actual: - type: pattern - id: image - fields: - image: - theme: image - uri: http://lorempixel.com/400/200/nature/2 - expected: - '#type': pattern - '#id': image - '#fields': - image: - '#theme': image - '#uri': http://lorempixel.com/400/200/nature/2 diff --git a/tests/src/fixtures/preview_process.yml b/tests/src/fixtures/preview_process.yml deleted file mode 100644 index 675fb040..00000000 --- a/tests/src/fixtures/preview_process.yml +++ /dev/null @@ -1,8 +0,0 @@ -- render: - '#type': pattern_preview - '#id': foo - expected: preview -- render: - '#type': pattern - '#id': foo - expected: empty diff --git a/tests/src/fixtures/validation.yml b/tests/src/fixtures/validation.yml deleted file mode 100644 index 79700e9e..00000000 --- a/tests/src/fixtures/validation.yml +++ /dev/null @@ -1,67 +0,0 @@ -- pattern: - id: 'not valid Name' - messages: - - "id: This value is not valid." - - "label: This value should not be null." - - "base path: This value should not be null." - - "file name: This value should not be null." - - "provider: This value should not be null." - - "theme hook: This value should not be null." -- pattern: - id: 'id' - messages: - - "id: This value is not valid." - - "label: This value should not be null." - - "base path: This value should not be null." - - "file name: This value should not be null." - - "provider: This value should not be null." - - "theme hook: This value should not be null." -- pattern: - id: pattern_id - label: label - description: description - fields: - fass: - name: 'not valid name' - messages: - - "base path: This value should not be null." - - "file name: This value should not be null." - - "provider: This value should not be null." - - "fields.0.name: This value is not valid." - - "fields.0.label: This value should not be null." - - "theme hook: This value should not be null." -- pattern: - id: pattern_id - label: label - description: description - fields: - type: - type: text - label: type - description: description - preview: preview - messages: - - "base path: This value should not be null." - - "file name: This value should not be null." - - "provider: This value should not be null." - - "fields.0.name: This value should not be null." - - "theme hook: This value should not be null." -- pattern: - id: pattern_id - label: label - description: description - base path: /path/to/pattern - file name: pattern_id.ui_patterns.yml - theme hook: pattern_pattern_id - provider: module - fields: - - - name: camelName - label: Label - type: text - description: description - preview: preview - - - name: name - label: Label - messages: [] diff --git a/ui_patterns.api.php b/ui_patterns.api.php deleted file mode 100644 index 96b837fa..00000000 --- a/ui_patterns.api.php +++ /dev/null @@ -1,85 +0,0 @@ -setLabel('My new label'); -} - -/** - * Alter UI Patterns Source definitions. - * - * @see \Drupal\ui_patterns\UiPatternsSourceManager - */ -function hook_ui_patterns_ui_patterns_source_info_alter(&$definitions) { - $definitions['my_field_source']['tags'][] = 'new_tag'; -} - -/** - * Provide hook theme suggestions for patterns. - * - * @see ui_patterns_theme_suggestions_alter() - */ -function hook_ui_patterns_suggestions_alter(array &$suggestions, array $variables, PatternContext $context) { - if ($context->isOfType('views_row')) { - $hook = $variables['theme_hook_original']; - $view_name = $context->getProperty('view_name'); - $display = $context->getProperty('display'); - - $suggestions[] = $hook . '__views_row__' . $view_name; - $suggestions[] = $hook . '__views_row__' . $view_name . '__' . $display; - } -} - -/** - * Provide hook theme suggestions for patterns destination wrapper. - * - * A pattern render element having '#multiple_sources' set to TRUE can render - * multiple sources on the same destination field. Sources will be rendered - * using the 'patterns_destination' theme function which will use the - * 'patterns-destination.html.twig' template file. - * - * Developers can take over rendering of the template above by providing proper - * suggestions, this is useful in case you wish to provide separators or other - * wrapping elements. - * - * @see ui_patterns_theme_suggestions_alter() - * @see \Drupal\ui_patterns\Element\Pattern::processMultipleSources() - */ -function hook_ui_patterns_destination_suggestions_alter(array &$suggestions, array $variables, PatternContext $context) { - if ($context->isOfType('views_row')) { - $hook = $variables['theme_hook_original']; - $view_name = $context->getProperty('view_name'); - $display = $context->getProperty('display'); - $pattern = $context->getProperty('pattern'); - $field = $context->getProperty('field'); - - $suggestions[] = $hook . '__views_row__' . $view_name . '__' . $pattern . '__' . $field; - $suggestions[] = $hook . '__views_row__' . $view_name . '__' . $display . '__' . $pattern . '__' . $field; - } -} - -/** - * Alter pattern settings form under "Manage display". - * - * @param array $form - * Pattern settings fieldset. - * @param array $configuration - * Pattern configuration. - */ -function hook_ui_patterns_display_settings_form_alter(array &$form, array $configuration) { - $form['element'] = ['#type' => 'input']; -} diff --git a/ui_patterns.info.yml b/ui_patterns.info.yml index fdd0b2e2..00543fbf 100644 --- a/ui_patterns.info.yml +++ b/ui_patterns.info.yml @@ -1,6 +1,7 @@ -name: UI Patterns +name: "UI Patterns" type: module -description: UI patterns -core: 8.x -core_version_requirement: ^8 || ^9 -package: User interface +description: "Define and expose self-contained UI Components as Drupal plugins and use them seamlessly in Drupal development and site-building." +core_version_requirement: ^10 +package: "User interface" +dependencies: + - drupal:sdc diff --git a/ui_patterns.module b/ui_patterns.module deleted file mode 100644 index d4d61cd7..00000000 --- a/ui_patterns.module +++ /dev/null @@ -1,76 +0,0 @@ - [ - 'variables' => ['sources' => NULL, 'context' => NULL], - ], - 'patterns_use_wrapper' => [ - 'variables' => ['use' => NULL], - ], - ]; - - foreach (UiPatterns::getManager()->getPatterns() as $pattern) { - $items += $pattern->getThemeImplementation(); - } - return $items; -} - -/** - * Implements hook_library_info_build(). - */ -function ui_patterns_library_info_build() { - /** @var \Drupal\ui_patterns\Plugin\PatternBase $pattern */ - - $definitions = []; - foreach (UiPatterns::getManager()->getPatterns() as $pattern) { - $definitions += $pattern->getLibraryDefinitions(); - } - return $definitions; -} - -/** - * Implements hook_theme_suggestions_HOOK_alter(). - */ -function ui_patterns_theme_suggestions_alter(array &$suggestions, array $variables, $hook) { - if (UiPatterns::getManager()->isPatternHook($hook)) { - \Drupal::moduleHandler()->alter('ui_patterns_suggestions', $suggestions, $variables, $variables['context']); - \Drupal::theme()->alter('ui_patterns_suggestions', $suggestions, $variables, $variables['context']); - } - - if ($hook == 'patterns_destination') { - \Drupal::moduleHandler()->alter('ui_patterns_destination_suggestions', $suggestions, $variables, $variables['context']); - \Drupal::theme()->alter('ui_patterns_destination_suggestions', $suggestions, $variables, $variables['context']); - } -} - -/** - * Implements hook_ui_patterns_suggestions_alter(). - */ -function ui_patterns_ui_patterns_suggestions_alter(array &$suggestions, array $variables, PatternContext $context) { - // Add preview theme suggestion. - if ($context->isOfType('preview')) { - $suggestions[] = $variables['theme_hook_original'] . '__preview'; - } - - if (!empty($variables['variant'])) { - $suggestions[] = $variables['theme_hook_original'] . '__variant_' . $variables['variant']; - - // Add variant preview theme suggestion. - if ($context->isOfType('preview')) { - $suggestions[] = $variables['theme_hook_original'] . '__variant_' . $variables['variant'] . '__preview'; - } - } -} diff --git a/ui_patterns.services.yml b/ui_patterns.services.yml index cb420c61..c3b3d1fc 100644 --- a/ui_patterns.services.yml +++ b/ui_patterns.services.yml @@ -1,11 +1,36 @@ services: - plugin.manager.ui_patterns: - class: Drupal\ui_patterns\UiPatternsManager - arguments: ['@container.namespaces', '@module_handler', '@theme_handler', '@cache.discovery'] + plugin.manager.ui_patterns_prop_type: + class: Drupal\ui_patterns\PropTypePluginManager + arguments: [ '@container.namespaces', '@cache.discovery', '@module_handler', '@Drupal\sdc\Component\SchemaCompatibilityChecker' ] plugin.manager.ui_patterns_source: - class: Drupal\ui_patterns\UiPatternsSourceManager + class: Drupal\ui_patterns\SourcePluginManager parent: default_plugin_manager ui_patterns.twig.extension: class: Drupal\ui_patterns\Template\TwigExtension tags: - { name: twig.extension } + ui_patterns.plugin.manager.sdc: + class: Drupal\ui_patterns\Sdc\UiPatternsSdcPluginManager + decorates: plugin.manager.sdc + decoration_priority: 9 + public: false + arguments: + - '@ui_patterns.plugin.manager.sdc.inner' + - '@plugin.manager.ui_patterns_prop_type' + - '@plugin.manager.ui_patterns_source' + - '@module_handler' + - '@theme_handler' + - '@cache.discovery' + - '@config.factory' + - '@theme.manager' + - '@Drupal\sdc\ComponentNegotiator' + - '@file_system' + - '@Drupal\sdc\Component\SchemaCompatibilityChecker' + - '@Drupal\sdc\Component\ComponentValidator' + - '%app.root%' + stream_wrapper.ui_patterns_prop_type: + class: Drupal\ui_patterns\StreamWrapper\UiPatternsPropTypeStreamWrapper + arguments: + - '@plugin.manager.ui_patterns_prop_type' + tags: + - { name: stream_wrapper, scheme: ui-patterns }