Problem/Motivation

In #2826690: [META] Add icons to paragraph types we want to add paragraphs type icons, thus, we need first to add a thumbnail/icon field to store the image.

Proposed resolution

Add the icon field, not sure if we should allow the user to select their own icons, in that case the demo module should also offer default ones.

Remaining tasks

User interface changes

API changes

Data model changes

CommentFileSizeAuthor
#85 interdiff-2830016-82-85.txt1.36 KBtoncic
#85 add_a_thumbnail_icon-2830016-85.patch11.69 KBtoncic
#82 interdiff-2830016-80-82.txt1.13 KBtoncic
#82 add_a_thumbnail_icon-2830016-82.patch11.72 KBtoncic
#80 interdiff-2830016-77-80.txt892 bytestoncic
#80 add_a_thumbnail_icon-2830016-80.patch11.69 KBtoncic
#78 interdiff-2830016-75-77.txt1.23 KBtoncic
#77 interdiff-2830016-75-77.txt1.16 KBtoncic
#77 add_a_thumbnail_icon-2830016-77.patch11.69 KBtoncic
#75 interdiff-2830016-73-75.txt2.06 KBtoncic
#75 add_a_thumbnail_icon-2830016-75.patch11.59 KBtoncic
#73 interdiff-2830016-71-73.txt3.41 KBtoncic
#73 add_a_thumbnail_icon-2830016-73.patch11.7 KBtoncic
#71 add_a_thumbnail_icon-2830016-71.patch12 KBtoncic
#69 interdiff-2830016-67-69.txt599 bytestoncic
#69 add_a_thumbnail_icon-2830016-69.patch12.86 KBtoncic
#67 interdiff-2830016-65-67.txt674 bytestoncic
#67 add_a_thumbnail_icon-2830016-67.patch11.94 KBtoncic
#65 interdiff-2830016-61-65.txt2.17 KBtoncic
#65 add_a_thumbnail_icon-2830016-65.patch10.94 KBtoncic
#61 interdiff-2830016-58-61.txt1.71 KBtoncic
#61 add_a_thumbnail_icon-2830016-61.patch10.92 KBtoncic
#58 interdiff-2830016-54-58.txt1.83 KBtoncic
#58 add_a_thumbnail_icon-2830016-58.patch10.93 KBtoncic
#54 interdiff-2830016-42-54.txt1.95 KBtoncic
#54 add_a_thumbnail_icon-2830016-54.patch10.94 KBtoncic
#42 add_a_thumbnail_icon-2830016-42.patch10.36 KBtoncic
#41 add_a_thumbnail_icon-2830016-41.patch10.43 KBtoncic
#35 add_a_thumbnail_icon-2830016-35.patch10.27 KBgpap
#33 interdiff-2830016-32-33.txt1.8 KBtoncic
#33 add_a_thumbnail_icon-2830016-33.patch10.25 KBtoncic
#32 interdiff-2830016-31-32.txt342 bytestoncic
#32 add_a_thumbnail_icon-2830016-32.patch10.24 KBtoncic
#31 centering_icon.png81.85 KBtoncic
#31 interdiff-2830016-29-31.txt932 bytestoncic
#31 add_a_thumbnail_icon-2830016-31.patch10.24 KBtoncic
#29 interdiff-2830016-26-29.txt868 bytestoncic
#29 add_a_thumbnail_icon-2830016-29.patch9.05 KBtoncic
#26 interdiff-2830016-24-26.txt2.48 KBtoncic
#26 add_a_thumbnail_icon-2830016-26.patch9.04 KBtoncic
#26 paragraphs_type_icon_list_builder.png70.74 KBtoncic
#24 upload_icon_paragraph_type.png83.46 KBtoncic
#24 interdiff-2830016-20-24.txt1.28 KBtoncic
#24 add_a_thumbnail_icon-2830016-24.patch7.95 KBtoncic
#21 2-times-warning.png16.23 KBGinovski
#20 add_a_thumbnail_icon-2830016-20.patch8.02 KBtoncic
#20 interdiff-2830016-18-20.txt710 bytestoncic
#18 interdiff-2830016-16-18.txt1.47 KBtoncic
#18 add_a_thumbnail_icon-2830016-18.patch7.78 KBtoncic
#16 interdiff-2830016-14-16.txt5.25 KBtoncic
#16 add_a_thumbnail_icon-2830016-16.patch7.77 KBtoncic
#14 interdiff-2830016-13-14.txt1.05 KBtoncic
#14 add_a_thumbnail_icon-2830016-14.patch6.83 KBtoncic
#13 interdiff-2830016-12-13.txt1.74 KBtoncic
#13 add_a_thumbnail_icon-2830016-13.patch6.93 KBtoncic
#12 interdiff-2830016-6-12.txt3.26 KBtoncic
#12 add_a_thumbnail_icon-2830016-12.patch6.52 KBtoncic
#6 interdiff-2830016-3-6.txt3.81 KBtoncic
#6 add_a_thumbnail_icon-2830016-6.patch5.18 KBtoncic
#6 add_icon_edit.png33.18 KBtoncic
#6 add_icon_list_builder.png28.79 KBtoncic
#3 add_a_thumbnail_icon-2830016-3.patch4.03 KBtoncic
Support from Acquia helps fund testing for Drupal Acquia logo

Comments

yongt9412 created an issue. See original summary.

toncic’s picture

Title: Add a thumbnail/icon field to Paragraphs » Add a thumbnail/icon field to Paragraphs type
Assigned: Unassigned » toncic
Issue summary: View changes
toncic’s picture

Added icon field for paragraph type. I can not set default icon because getIconFile() return null from unknown reason for me at the moment.
I need to fix that and add test coverage.

drobnjak’s picture

miro_dietiker’s picture

This is the implementation issue of the META about icons. Introducing icons is more than just add them to the types and then use them in the "add" widget. With other features upcoming, the META topic will grow and there are things that need discussion about the icon concept in general.

toncic’s picture

Fixed problem with functionality, only need to write test coverage.
Providing screenshots.

pixelmord’s picture

I already mentioned in another issue that I really like the idea to have icons or similar to be able to visually identify the paragraph type.

However I do not like the proposal to mix content and configuration, because we would have a dependency between a content entity (the file entity for the icon/image) and the configuration (entity) which is the bundle. That rather bad for development and integration.

Could we just use that pattern that we have for the site logo for example, where you can provide a path? That way the might have to make sure that the path is valid for all environments, but it is still configuration and not content..

Berdir’s picture

This is done exactly like embed.module does it, and it works pretty well there. We're not the first to have content dependencies in config, views, blocks and other things have this too.

Providing default icons can be done with the default_content module for example.

Berdir’s picture

Status: Needs review » Needs work
  1. +++ b/config/schema/paragraphs_type.schema.yml
    @@ -8,6 +8,9 @@ paragraphs.paragraphs_type.*:
    +    icon_uuid:
    +      type: string
    +      label: 'Icon uui'
     
    

    incomplete label

  2. +++ b/src/Controller/ParagraphsTypeListBuilder.php
    @@ -25,6 +29,14 @@ class ParagraphsTypeListBuilder extends ConfigEntityListBuilder {
    +        '#alt' => $this->t('Icon for the @label button.', ['@label' => $entity->label()]),
    

    this is not a button.

  3. +++ b/src/Entity/ParagraphsType.php
    @@ -54,4 +55,29 @@ class ParagraphsType extends ConfigEntityBundleBase implements ParagraphsTypeInt
    +   * UUID of the button's icon file.
    

    nope

  4. +++ b/src/Entity/ParagraphsType.php
    @@ -54,4 +55,29 @@ class ParagraphsType extends ConfigEntityBundleBase implements ParagraphsTypeInt
    +  public $icon_uuid;
    

    lets make this protected

  5. +++ b/src/Entity/ParagraphsType.php
    @@ -54,4 +55,29 @@ class ParagraphsType extends ConfigEntityBundleBase implements ParagraphsTypeInt
    +  public function getIconUrl() {
    +    if ($image = $this->getIconFile()) {
    +      return file_create_url($image->getFileUri());
    +    }
    +  }
    

    we probably want to have a generic fallback token.

We also need a calculateDependencies() method.

Another note on #8/7. We will need to figure out how to support default icons, and while I can imagine we could add a path setting too, it won't really help without having support for module:// stream wrappers as we can't hardcode the path to files in the module, so we still need to copy them to the public folder and then we can just as well make them managed.

We should also register usages of those icons using file_usage service.

miro_dietiker’s picture

Usage: Yeah what i have seen is that usage was still temporary, so it would be deleted on cleanup cron.

Are we going to handle the default icon complexity in a follow-up?

Berdir’s picture

Fine with doing that in a follow-up. We can already do something like have a file in the module, copy it to public:// and save as a file entity, then reference it. For the demo module, for example (which we can also do later, since we don't actually use the icons yet anyway)

toncic’s picture

Added test coverage and implement comm #9. Created follow -up from #11

toncic’s picture

Added calculateDependencies(). Now is ready for review.

toncic’s picture

johnchque’s picture

Status: Needs review » Needs work
  1. +++ b/src/Entity/ParagraphsType.php
    @@ -54,4 +55,42 @@ class ParagraphsType extends ConfigEntityBundleBase implements ParagraphsTypeInt
    +      return $this->entityManager()->loadEntityByUuid('file', $this->icon_uuid);
    

    Can we use the EntityTypeManager here?

  2. +++ b/src/Tests/ParagraphsPreviewTest.php
    @@ -143,4 +143,30 @@ class ParagraphsPreviewTest extends WebTestBase {
    +  public function testParagraphTypeIcon() {
    

    This test is not related with preview. Let's move it to ParagraphsTypesTest

toncic’s picture

Move test in ParagraphsTypesTest and added dependency for file module.

Berdir’s picture

Status: Needs review » Needs work
  1. +++ b/paragraphs.install
    @@ -180,4 +180,11 @@ function paragraphs_update_8008() {
    +/**
    + * Install file module.
    + */
    +function paragraphs_update_8010() {
    +  \Drupal::service('module_installer')->install('file');
    +}
    

    Doesn't look you tested this because that's now how install() works.

  2. +++ b/src/Entity/ParagraphsType.php
    @@ -67,7 +67,7 @@ class ParagraphsType extends ConfigEntityBundleBase implements ParagraphsTypeInt
       public function getIconFile() {
         if ($this->icon_uuid) {
    -      return $this->entityManager()->loadEntityByUuid('file', $this->icon_uuid);
    +      return \Drupal::service('entity.manager')->loadEntityByUuid('file', $this->icon_uuid);
         }
    

    you still use entity.manager.

toncic’s picture

Implemented comm #17.

Berdir’s picture

Status: Needs review » Needs work
+++ b/src/Tests/ParagraphsTypesTest.php
@@ -38,4 +38,31 @@ class ParagraphsTypesTest extends ParagraphsTestBase {
+
+    // Check if icon is saved.
+    $this->drupalGet('admin/structure/paragraphs_type');
+    $this->clickLink('Edit');
+    $this->assertText('image-test-transparent-indexed.gif');
+  }

I'm not seeing any test coverage that the icon is displayed on the overview.

toncic’s picture

Added test case to check if image is presented in list builder. I am asserting if alt attribute is presented, so img element is also presented. I hope that this is enough.

Ginovski’s picture

FileSize
16.23 KB


I'm getting 2 info messages, is this correctly set?

+++ b/src/Tests/ParagraphsTypesTest.php
@@ -38,4 +38,34 @@ class ParagraphsTypesTest extends ParagraphsTestBase {
+    // If alt attribute is presented image tag si also presented.

is*

+++ b/src/Tests/ParagraphsTypesTest.php
@@ -38,4 +38,34 @@ class ParagraphsTypesTest extends ParagraphsTestBase {
+    $this->assertTrue((bool) $this->xpath("//img[@alt='Icon for the Test paragraph type paragraph type.']"), 'Alt text found');

For image assertion, try to get the path of the image and then assert it.
Maybe something like this example I found:

    $elements = $this->xpath('//img[@src="http://local/image.png"]');
    $this->assertEqual(count($elements), 1, 'Image plugin tip found.');
miro_dietiker’s picture

Status: Needs review » Needs work
+++ b/src/Form/ParagraphsTypeForm.php
@@ -41,6 +41,21 @@ class ParagraphsTypeForm extends EntityForm {
+        'file_validate_extension' => ['gif png jpg jpeg'],
+        'file_validate_image_resolution' => ['32x32', '16x16'],

That's far from the expected use of the icon.

The recommended format should be SVG.
And the add widget will display those icons in a way bigger element than 32 pixels.
We might display it downscaled to a tiny size in buttons, but that's IMHO not relevant and should not go through an image style as SVG need to stay vectors IMHO. Not sure how to properly limit / define the requirement / validation in the UI.

Hint: Also Thunder wants to display the icons bigger in the preview / collapsed paragraph, see #2828110: Sorting of paragraphs 03 - create a NEW paragraph "in place" between existing ones

Berdir’s picture

I'd say don't limit the resolution to start, we can always add code to automatically downsize like image fields support when we know how we use the icons and how big they're displayed?

toncic’s picture

I removed resolution validation and now is looking very bad, and also changed the file extension validation to be on 'svg' but still we can upload other types like png. jpg and so on.
Providing screenshot.

miro_dietiker’s picture

Yeah. That looks bad, but the image you uploaded as an example is also highly confusing. ;-)

I'm not sure about column sequence.

We will usually display Icon + Label. It might be best to start the row with an icon column prior to the label.
What also confuses me is the column "PARAGRAPHS TYPES". That should simply be label.

toncic’s picture

Hardcoded width and height for image to scale automatically, and also added css to decrease the width for column where is a icon.
Now is looking better. Also changed the order of column.
Providing screenshot.

johnchque’s picture

As discussed change the class of the icon.

Berdir’s picture

Status: Needs review » Needs work
toncic’s picture

Changed css class name for icon field.

miro_dietiker’s picture

Status: Needs review » Needs work

From the image above i can see that the icon adds some padding below and that's why the padding / white room is not balanced.
Please check the vertical paddings / margins and adjust them.

toncic’s picture

Centering image.

toncic’s picture

toncic’s picture

After discussion with Sascha and Ivica we deal to do on this way.

johnchque’s picture

Status: Needs review » Reviewed & tested by the community

Looks good.

gpap’s picture

Fixed the last patch, as it failed for me in src/Form/ParagraphsTypeForm.php, at line 73. Patch #33 has this:

if ($behavior_plugins = $this->paragraphsBehaviorManager->getDefinitions()) {

As opposed to this (8.x-1.x-dev):

if ($behavior_plugins = $this->paragraphsBehaviorManager->getApplicableDefinitions($paragraphs_type)) {

It was changed here: http://cgit.drupalcode.org/paragraphs/commit/?id=7aaa294ec7284daebe4eb3d...

I could not create an interdiff as #33 breaks, but I fixed this line, a typo and removed an extra space.

The last submitted patch, 33: add_a_thumbnail_icon-2830016-33.patch, failed testing.

The last submitted patch, 33: add_a_thumbnail_icon-2830016-33.patch, failed testing.

miro_dietiker’s picture

Status: Reviewed & tested by the community » Needs work

Can't stay at RTBC without interdiff or another review by someone.

gpap’s picture

@miro_dietiker
The only difference between patch #33 and #35 is this line:

if ($behavior_plugins = $this->paragraphsBehaviorManager->getDefinitions()) {

to this:
if ($behavior_plugins = $this->paragraphsBehaviorManager->getApplicableDefinitions($paragraphs_type)) {

Ran with $ diff add_a_thumbnail_icon-2830016-33.patch add_a_thumbnail_icon-2830016-35.patch command.

tduong’s picture

Now this needs a reroll as there is a conflict in ParagraphsTypeForm. Just tested if the patch works otherwise: not sure if I'm missing a step, but after enabling paragraphs_demo (on a clean env), applying the patch and then running drush updb, it gives me the following exception:

exception 'PDOException' with message 'SQLSTATE[42S02]: Base table or view not found: 1146 Table 'drupal8.batch' doesn't exist'
...

Next exception 'Drupal\Core\Database\DatabaseExceptionWrapper' with message 'SQLSTATE[42S02]: Base table or view not found: 1146 Table 'drupal8.batch' doesn't exist: INSERT INTO {batch} (bid, timestamp, token, batch) VALUES
(:db_insert_placeholder_0, :db_insert_placeholder_1, :db_insert_placeholder_2, :db_insert_placeholder_3);
...

toncic’s picture

Rebasing patch,
@gpap:
That is fixed in some previous patch.
@tduong:
For me works fine, I can't reproduce that error.

toncic’s picture

Rebasing again, experimental widget are committed.

johnchque’s picture

Component: Code » Experimental Widget

Let's organize this.

miro_dietiker’s picture

Component: Experimental Widget » Code

No, the icon field will be there always and affect every Paragraphs user.
We will only use the field in the experimental widget, but that's a different issue.

jeroen.b’s picture

@miro_dietiker, what do we need to do to get this in?

seanB’s picture

  1. +++ b/src/Controller/ParagraphsTypeListBuilder.php
    @@ -14,8 +14,12 @@ class ParagraphsTypeListBuilder extends ConfigEntityListBuilder {
    -    $header['label'] = $this->t('Paragraphs types');
    

    Unrelated change, but I agree this should be done.

  2. +++ b/src/Entity/ParagraphsType.php
    @@ -94,6 +121,20 @@ class ParagraphsType extends ConfigEntityBundleBase implements ParagraphsTypeInt
    +  public function getDependencies() {
    +    parent::calculateDependencies();
    +
    +    // Add the file icon entity as dependency if an UUID was specified.
    +    if ($this->icon_uuid && $file_icon = \Drupal::service('entity.repository')->loadEntityByUuid('file', $this->icon_uuid)) {
    +      $this->addDependency($file_icon->getConfigDependencyKey(), $file_icon->getConfigDependencyName());
    +    }
    +
    +    return $this->dependencies;
    +  }
    

    This is weird, the getDependencies method is now responsible for adding dependencies. Should this not be calculateDependencies?

  3. +++ b/src/ParagraphsTypeInterface.php
    @@ -36,4 +36,28 @@ interface ParagraphsTypeInterface extends ConfigEntityInterface {
    +  /**
    +   * Returns the button's icon file.
    +   *
    +   * @return \Drupal\file\FileInterface
    +   *   The file entity of the button icon.
    +   */
    +  public function getIconFile();
    +
    +  /**
    +   * Returns the URL of the button's icon.
    +   *
    +   * If no icon file is associated with this Embed Button entity, the embed type
    +   * plugin's default icon is used.
    +   *
    +   * @return string
    +   *   The URL of the button icon.
    +   */
    +  public function getIconUrl();
    +
    +  /**
    +   * {@inheritdoc}
    +   */
    +  public function calculateDependencies();
    +
    

    Do we really need to add these to ParagraphsTypeInterface and ParagraphsType?

    calculateDependencies is already in ConfigEntityInterface. getIconFile and getIconUrl contain very little code, having extra methods to return these oneliners seems kind of pointless?

miro_dietiker’s picture

Status: Needs review » Needs work
+++ b/src/ParagraphsTypeInterface.php
@@ -36,4 +36,28 @@ interface ParagraphsTypeInterface extends ConfigEntityInterface {
+  public function calculateDependencies();

This is already part of ConfigEntityInterface.

miro_dietiker’s picture

Priority: Normal » Major

Now that the release is out, this should be the next step to unlock use of icons.

seanB’s picture

  1. +++ b/src/Form/ParagraphsTypeForm.php
    @@ -77,6 +77,20 @@ class ParagraphsTypeForm extends EntityForm {
    +        'file_validate_extension' => ['svg'],
    

    This should be file_validate_extensions

  2. +++ b/src/Tests/Classic/ParagraphsTypesTest.php
    @@ -38,4 +38,34 @@ class ParagraphsTypesTest extends ParagraphsTestBase {
    +  /**
    +   * Test if paragraph type icon.
    +   */
    +  public function testParagraphTypeIcon() {
    

    This doesn't apply? Not sure why but we should check that.

Besides that there is an issue with the configuration. The uuid for the file is exported, but the file it is actually linked to isn't. This way you get a broken icon after a release.

This makes me think about the whole approach of using file uploads for this. I guess this is done so you don't have to write code to get a icon? But now you need to know where to find icons, and somehow match them to the admin theme you are using. Even if we find a way to deploy icons, uploading icons for each paragraph type is not a very friendly way to do this.

We've had a short discussion here at the paragraphs sprint and came to the following conclusions:

  • Uploading icons for each paragraph type is not very user friendly, choosing from a set of icons is a big improvement.
  • Creating a library of icons is the next issue. We can try to provide as much icons as we can through the module (starting with a couple and expand the icon set in followups).
  • You should be able to add/alter the base list of icons. A separate module can maybe provide a way to upload new icons, but paragraphs only allows add/alter through code (so uploading icons is out of scope for now).
  • Themes should be able to override the icons.

I guess we should now discuss what the best way is to move forward.

seanB’s picture

After discussion at the paragraphs sprint with jurcello, sutharsan and marcvangend we came to the following proposal.

What we need at a minimum:

  • name: For storage purposes

How can we do it:

  • We create a icon service to provide available icons.
  • We discover icons in modules / themes in YAML files.
  • Each module / theme can provide a [module/theme name].paragraphs.icons.yml which will be picked up by the icon service.
  • Each icon contains settings like path and a weight, so we can override icons in other modules / themes.
  • We provide an event to alter the outcome of the discovery.
  • We add a configuration settings for a icon base dir (can be set from the interface like media entity) and copy all theme icons to the base dir on discovery + the icon with the heighest weight (can be module or theme).
  • We change icon names with the module/theme name in the icon base dir like [icon name].[theme].[extension]. The heighest weight is just called [name].[extension].
  • We store the result of the discovery, including the updated filename in cache.
  • Each icon can contain an array of settings, by default most can define a path and/or weight, but also things like attributes or other settings can be provided.
  • We provide a theme function that can use the settings of icons to render a icon. In theory a valid icon image is not needed when themes provide icons through a font or something like that.
  • Eacht paragraph type can select their own paragraph icon (we store the name only), and you can reuse generic ones if needed.
  • The active theme icon is always prefered. We fall back to the heighest weight if the theme doesn't provide a icon.

Example of [module/theme name].paragraphs.icons.yml:

text:
  path: '/relative/module/path.svg'
  weight: 0
image:
  path: '/assets/icons/image.svg'
  weight: 1
  attributes:
    id: 'example_id'
    class:
      - 'whatever'
      - 'class'
      - 'you'
      - 'want'
  some_property: some_value
seanB’s picture

Copy/paste from Slack:
-----------
sutharsan
@seanb I do not understand “… + the icon with the heighest weight (can be module or theme).” in "We add a configuration settings for a icon base dir (can be set from the interface like media entity) and copy all theme icons to the base dir on discovery + the icon with the heighest weight (can be module or theme).”

@seanb About "We change icon names with the module/theme name in the icon base dir like [icon name].[theme].[extension]. The heighest weight is just called [name].[extension].”
I think that you should explain that we want themes to be able to override base theme(s) and modules. But also allow fallback if the (final) theme does not provide an icon.

seanb
@sutharsan I can see how this might be confusing. Since multiple modules/themes can implement icons with a specific name, the proposal is to only care about the current theme, or the icon with the highest weight as a fallback. Hopefully copy/pasting this to the issue is a good explanation.
------------

As far as we could see, module/theme icons with lower weight/importance will not be used. Copying/returning/caching those seem pointless. We could off course be missing some edge cases here, so please let me know if there are examples where this could lead to undesired results.

miro_dietiker’s picture

Thank you for the proposal and discussing this at the NL sprint.

The proposal has a key problem: It adds huge complexity to handle a managed overridable set of icons. That dimension of complexity is a bad sign for an approach. If this doesn't exist yet, it should be introduced by a dedicated module (there is also project icon!) and otherwise we should focus on a minimum approach.

Using a file field is IMHO a much better start to iterate. It is simply available and users could start to upload icons. Then we could deal in follow-ups with...
- Providing a default action by code as a fallback for module shipped paragraph type
- Alter the widget to a predefined selection list (consider icon providers?)
- Support exporting references so that the icon is transported in export / import

I believe all these problems can be solved on top of the "icon as content" approach we proposed.
I'll pass the ball to Berdir to provide feedback too.

Berdir’s picture

Yes, this solution isn't perfect, but it's based on the embed.module and I agree that it is a useful starting point.

We can still add a separate field for a filename or selecting from different provided default icons later on. That's exactly why those getIconUrl() and so on methods are important and must be kept, because they can then deal with those additional options without making the public API more complicated.

Deploying content is a problem, but this one case of many and it needs a better general solution. Fabian Bircher actually mentioned that he is working on a module in his session at the mountain camp (https://drupalmountaincamp.ch/sessions/configuration-management-without-...) and that he will release something soon.

toncic’s picture

Switch to use calculateDependencies() instead of getDependencies(), that was my fault. Added test for calculateDependencies(). I agree with @Berdir that this is enough for starting point. Other stuff we can do in follow-ups.

johnchque’s picture

Let's create the follow-ups and get this in. :)

seanB’s picture

Status: Needs review » Needs work

I think we are almost there. It would be nice to link the follow-ups to remove the file field and replace it with some better way of selecting icons. We can at least continue the discussion about it in the other issue.

At least the SVG validation should be fixed.

  1. +++ b/src/Form/ParagraphsTypeForm.php
    @@ -79,6 +79,20 @@ class ParagraphsTypeForm extends EntityForm {
    +        'file_validate_extension' => ['svg'],
    

    I think this should be file_validate_extensions.

  2. +++ b/src/ParagraphsTypeInterface.php
    @@ -37,13 +37,32 @@ interface ParagraphsTypeInterface extends ConfigEntityInterface {
    +   * Returns the button's icon file.
    ...
    +   * Returns the URL of the button's icon.
    

    Nit: Is this just for buttons? Maybe we just remove the word button here.

  3. +++ b/src/ParagraphsTypeInterface.php
    @@ -37,13 +37,32 @@ interface ParagraphsTypeInterface extends ConfigEntityInterface {
    +   *   True or False dependant on plugin state.
    

    Nit: TRUE if the plugin is enabled, FALSE otherwise.

miro_dietiker’s picture

BTW i had the idea of a paragraph type "separator" in the collection that has a related problem:
#2854358: [META] Fundamental Type: Separator
Similar to icons, we could offer a selection of vector separators that a user could choose. Thus, we would need a similar provider / selectioin.
One difference though: Icons should primarily be chosen due to their meaning / semantics. The separator is likely being selected only because of its appearance.

toncic’s picture

toncic’s picture

Status: Needs work » Needs review

Forgot to set on needs review.

Status: Needs review » Needs work

The last submitted patch, 58: add_a_thumbnail_icon-2830016-58.patch, failed testing.

toncic’s picture

Fixing test failing.
After discussing with @Berdir added jpg and png as file extensions.

seanB’s picture

Status: Needs review » Reviewed & tested by the community
+++ b/src/ParagraphsTypeInterface.php
@@ -37,13 +37,32 @@ interface ParagraphsTypeInterface extends ConfigEntityInterface {
+   * Returns the URL of the's icon.

This is a small typo, but I think this can be fixed on commit. Nice work, RTBC!

I will create a follow up to discuss any improvements as suggested in #50.

toncic’s picture

I already created one issue for discussing. Here it is : #2854585: Improve icon selection in paragraphs type form.

miro_dietiker’s picture

Status: Reviewed & tested by the community » Needs work

Small things, plz fix.

  1. +++ b/src/Controller/ParagraphsTypeListBuilder.php
    @@ -14,8 +14,12 @@ class ParagraphsTypeListBuilder extends ConfigEntityListBuilder {
    +    $header['icon_uuid'] = [
    
    @@ -23,6 +27,17 @@ class ParagraphsTypeListBuilder extends ConfigEntityListBuilder {
    +    $row['icon_file'] = '';
    

    The keys don't match, but they should.

  2. +++ b/src/Controller/ParagraphsTypeListBuilder.php
    @@ -23,6 +27,17 @@ class ParagraphsTypeListBuilder extends ConfigEntityListBuilder {
    +        '#width' => 32,
    +        '#height' => 32,
    

    Does this distort the image if it is not square?

  3. +++ b/src/Controller/ParagraphsTypeListBuilder.php
    @@ -23,6 +27,17 @@ class ParagraphsTypeListBuilder extends ConfigEntityListBuilder {
    +        '#alt' => $this->t('Icon for the @label paragraph type.', ['@label' => $entity->label()]),
    

    Too repetitive on table. The label is output separately. So for best accessibility this can even be an empty "".
    https://www.w3.org/WAI/tutorials/images/decorative/

toncic’s picture

2. Yes.

Fixed stuff from previous comment and convert css into sass.

Status: Needs review » Needs work

The last submitted patch, 65: add_a_thumbnail_icon-2830016-65.patch, failed testing.

toncic’s picture

Fixing test failing.

johnchque’s picture

+++ b/css/paragraphs.widget.css
@@ -111,3 +111,11 @@
+.paragraphs-edit-button-container .paragraphs-type-icon .img {

+++ b/css/paragraphs.widget.scss
@@ -120,4 +120,12 @@
+  .img {

does the image have a class img? I think you don't need the "."

toncic’s picture

Status: Needs review » Needs work

The last submitted patch, 69: add_a_thumbnail_icon-2830016-69.patch, failed testing.

toncic’s picture

Ops, that was wrong patch.

tduong’s picture

Status: Needs review » Needs work

Here some feedback :P sorry for being too pedantic...

  1. +++ b/src/Controller/ParagraphsTypeListBuilder.php
    @@ -14,8 +14,12 @@ class ParagraphsTypeListBuilder extends ConfigEntityListBuilder {
    +    $header['icon_file'] = [
    +      'data' => $this->t('Icon'),
    +    ];
    

    This doesn't break the 80 chars length rule, so you can do this in one line.

  2. +++ b/src/Entity/ParagraphsType.php
    @@ -92,6 +119,20 @@ class ParagraphsType extends ConfigEntityBundleBase implements ParagraphsTypeInt
    +    // Add the file icon entity as dependency if an UUID was specified.
    

    "... if a ..." not "an".

    and "... UUID is specified."

  3. +++ b/src/Form/ParagraphsTypeForm.php
    @@ -79,6 +79,20 @@ class ParagraphsTypeForm extends EntityForm {
    +      '#description' => $this->t('Icon for he paragraph type.'),
    

    Typo, but I would write "Paragraph type's icon." or even remove it since it doesn't say anything more useful than its title.

  4. +++ b/src/Form/ParagraphsTypeForm.php
    @@ -130,6 +144,18 @@ class ParagraphsTypeForm extends EntityForm {
    +    // If a file was uploaded to be used as the icon, get its UUID to be stored
    +    // in the config entity.
    

    Better something like "Set the file UUID to the paragraph configuration."

  5. +++ b/src/Form/ParagraphsTypeForm.php
    @@ -130,6 +144,18 @@ class ParagraphsTypeForm extends EntityForm {
    +    $status = $paragraphs_type->save();
    

    Just save it, no need to assign it to any variable.

  6. +++ b/src/ParagraphsTypeInterface.php
    @@ -37,13 +37,32 @@ interface ParagraphsTypeInterface extends ConfigEntityInterface {
    +   * Returns the icon file.
    

    ".. entity."

  7. +++ b/src/ParagraphsTypeInterface.php
    @@ -37,13 +37,32 @@ interface ParagraphsTypeInterface extends ConfigEntityInterface {
    +   *   The file entity of the button icon.
    

    "The icon's file entity."

  8. +++ b/src/ParagraphsTypeInterface.php
    @@ -37,13 +37,32 @@ interface ParagraphsTypeInterface extends ConfigEntityInterface {
    +   * Returns the URL of the's icon.
    

    "Returns the icon's URL."

  9. +++ b/src/ParagraphsTypeInterface.php
    @@ -37,13 +37,32 @@ interface ParagraphsTypeInterface extends ConfigEntityInterface {
    +   * If no icon file is associated with this Embed Button entity, the embed type
    +   * plugin's default icon is used.
    

    Copy/paste from EmbedButtonInterface.. Remove these lines since you don't provide any else-case.

  10. +++ b/src/ParagraphsTypeInterface.php
    @@ -37,13 +37,32 @@ interface ParagraphsTypeInterface extends ConfigEntityInterface {
    +   *   The URL of the button icon.
    

    "The icon's URL."

  11. +++ b/src/Tests/Classic/ParagraphsTypesTest.php
    @@ -39,4 +41,39 @@ class ParagraphsTypesTest extends ParagraphsTestBase {
    +   * Test if paragraph type icon.
    

    "Tests the paragraph type icon settings."

  12. +++ b/src/Tests/Classic/ParagraphsTypesTest.php
    @@ -39,4 +41,39 @@ class ParagraphsTypesTest extends ParagraphsTestBase {
    +    $admin_user = $this->drupalCreateUser(['administer paragraphs types']);
    +    $this->drupalLogin($admin_user);
    

    I wouldn't create a variable just to use it once.

  13. +++ b/src/Tests/Classic/ParagraphsTypesTest.php
    @@ -39,4 +41,39 @@ class ParagraphsTypesTest extends ParagraphsTestBase {
    +    $this->drupalGet('admin/structure/paragraphs_type');
    +    $this->clickLink(t('Add paragraphs type'));
    

    If you are not going to check anything between the paragraph types overview and the add form, then just do drupalGet('admin/structure/paragraphs_type/add').

  14. +++ b/src/Tests/Classic/ParagraphsTypesTest.php
    @@ -39,4 +41,39 @@ class ParagraphsTypesTest extends ParagraphsTestBase {
    +    // Check if icon is saved.
    

    "Check if the icon has been saved."

  15. +++ b/src/Tests/Classic/ParagraphsTypesTest.php
    @@ -39,4 +41,39 @@ class ParagraphsTypesTest extends ParagraphsTestBase {
    +    // If alt attribute is presented image tag is also presented.
    

    "The alt attribute and image tag should be present."

    But you should make it clearer in your assert that you are checking both alt and image tag values.

  16. +++ b/src/Tests/Classic/ParagraphsTypesTest.php
    @@ -39,4 +41,39 @@ class ParagraphsTypesTest extends ParagraphsTestBase {
    +    $this->drupalGet('admin/structure/paragraphs_type');
    

    Not necessary. You are already on that page.

toncic’s picture

@tduong:

1. This is optional, sorry but I prefer like this.
2. This is debatable.
3. Typo?

tduong’s picture

Status: Needs review » Needs work

2. it is "a UUID" and not "an UUID", see here the explanation/example ;)
3.

+++ b/src/Form/ParagraphsTypeForm.php
@@ -82,7 +82,7 @@ class ParagraphsTypeForm extends EntityForm {
-      '#description' => $this->t('Icon for he paragraph type.'),
+      '#description' => $this->t('Icon for the Paragraphs type.'),

The typo that you've fixed, but again, this description doesn't say anything more than the title. There is even an issue to #2849207: Drop redundant descriptions.

12. and 13. are not addressed yet?

toncic’s picture

12. IMHO it is more readable like this and if I put in one line, there will be more than 80 characters.

Primsi’s picture

Status: Needs review » Needs work
  1. +++ b/src/Entity/ParagraphsType.php
    @@ -75,6 +83,16 @@ class ParagraphsType extends ConfigEntityBundleBase implements ParagraphsTypeInt
    +  public function getIconFile() {
    +    if ($this->icon_uuid) {
    +      return \Drupal::service('entity.repository')
    +        ->loadEntityByUuid('file', $this->icon_uuid);
    

    We should have $this->entityTypeManager() here, should we use ...->getStorage('file')->loadByProperties() instead?

  2. +++ b/src/Entity/ParagraphsType.php
    @@ -92,6 +119,20 @@ class ParagraphsType extends ConfigEntityBundleBase implements ParagraphsTypeInt
    +    if ($this->icon_uuid && $file_icon = \Drupal::service('entity.repository')->loadEntityByUuid('file', $this->icon_uuid)) {
    

    Same as above.

Also, do we want to add some of those icons to demo paragraph types? Maybe can be done in follow-up

toncic’s picture

I hope this is what you mentioned.
Yes, would be nice if we add icons to paragraphs type in demo content, there is an issue #2860212: Add icons to paragraphs type in demo content.

toncic’s picture

FileSize
1.23 KB

Correct interdiff.

Primsi’s picture

Status: Needs review » Needs work
+++ b/src/Entity/ParagraphsType.php
@@ -85,8 +85,16 @@ class ParagraphsType extends ConfigEntityBundleBase implements ParagraphsTypeInt
+      $icon_file = [];
+      if (count($files)) {

No need to count, empty arrays validate false. Also IMHO we should return FALSE here if we don't find a file. And the inteface docblock should be updated accordingly.

toncic’s picture

Yes, you are right.

tduong’s picture

Status: Needs review » Needs work

A few things:

  1. +++ b/src/Entity/ParagraphsType.php
    @@ -75,6 +83,23 @@ class ParagraphsType extends ConfigEntityBundleBase implements ParagraphsTypeInt
    +        return $icon_file = array_shift($files);
    

    Don't assign to any variable if you just want to return it.

  2. +++ b/src/Entity/ParagraphsType.php
    @@ -85,6 +110,15 @@ class ParagraphsType extends ConfigEntityBundleBase implements ParagraphsTypeInt
    +    if ($image = $this->getIconFile()) {
    +      return file_create_url($image->getFileUri());
    +    }
    

    If getIconFile() else-case returns FALSE, shouldn't this method return FALSE as well?

  3. +++ b/src/ParagraphsTypeInterface.php
    @@ -37,13 +37,29 @@ interface ParagraphsTypeInterface extends ConfigEntityInterface {
    +   *   The URL of the button icon.
    

    "The icon's URL, FALSE if no icon file is found."

EDIT: and needs a reroll

toncic’s picture

Made changes from #81.

tduong’s picture

Just a small thing:

+++ b/src/Entity/ParagraphsType.php
@@ -75,6 +83,23 @@ class ParagraphsType extends ConfigEntityBundleBase implements ParagraphsTypeInt
+      if ($files) {
+        return array_shift($files);
+      }
+    }
+
+    return FALSE;

Rethinking about this, I would just create $files = []; and do a single return line like return $files ? array_shift($files) : FALSE;

Primsi’s picture

Status: Needs review » Needs work
  1. +++ b/src/Form/ParagraphsTypeForm.php
    @@ -130,6 +143,17 @@ class ParagraphsTypeForm extends EntityForm {
    +    $paragraphs_type->save();
    

    Why are we setting/saving stuff in validate()? Is there a reason we don't do this in save()?

  2. +++ b/src/ParagraphsTypeInterface.php
    @@ -37,13 +37,29 @@ interface ParagraphsTypeInterface extends ConfigEntityInterface {
    +   * @return \Drupal\file\FileInterface
    

    If we also return FALSE, this should be reflected here.

  3. +++ b/src/ParagraphsTypeInterface.php
    @@ -37,13 +37,29 @@ interface ParagraphsTypeInterface extends ConfigEntityInterface {
    +   * @return string
    +   *   The icon's URL or FALSE if icon does not exits.
    

    Same here

Also we could open a follow up to discuss how we should output the icon as mentioned in one of the comments. We just output the full size now.

toncic’s picture

Status: Needs work » Needs review

  • Primsi committed ae21d1a on 8.x-1.x authored by toncic
    Issue #2830016 by toncic, gpap, Ginovski, miro_dietiker, yongt9412,...
Primsi’s picture

Status: Needs review » Fixed

Committed, thanks to everyone!
@toncic Could you open a follow up issue in paragraphs collection for that too?

toncic’s picture

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.