Kirbytags:after and extra paragraph tags

I really like custom KirbyTags and tags like {{ gallery }} and being able to place them anywhere in a Textarea field. I made a few and (with the help of @texnixe) was able to include Kirbytext also.

Having said that, In several cases I’ve ran into problems with empty paragraph tags being added and as a result breaking the layout. The/a ‘solution’, as I’m learning to understand, is sometimes the placement of the end of a foreach loop.

An example, this would work:

<div class="js-accordion__content"><?= kt($item->accordion_content()) ?></div></div><?php endforeach ?>

But this would break, because empty paragraph tags are created after each loop, so after the last closing </div>:

    <div class="js-accordion__content"><?= kt($item->accordion_content()) ?></div>
  </div>
<?php endforeach ?>

I like to understand why this happens and if there are other (proper) ways to prevent this?
(btw: I’m a designer not a developer)
Thanks!

The plugin code used:

<?php

Kirby::plugin('kirby/accordion', [
    'hooks' => [
        'kirbytags:after' => function ($text, $data) {

            return preg_replace_callback('!{{ accordion }}!siU', function($match) use ($data) {
                
                if ($items = $data['parent']->accordion()->toStructure()) {
                    $block = snippet('accordion', ['items' => $items], true);
                } else {
                    $block = '';
                }
                return $block;

            }, $text);
           
        }
    ]
]);

Have you tried using kti() instead of kt()? This outputs inline elements instead and omits things like <p> (it’s hard to tell if this would help without actually seeing the content of the accordeon_content-field).

Well that improves the situation, sort of, but can’t do without those <p>. This works:

    <div class="js-accordion__content"><?= kti($item->accordion_content()) ?></div></div>
<?php endforeach ?>

But when I place the last </div> on a new line it breaks. When I do this (4 spaces):

    <div class="js-accordion__content"><?= kti($item->accordion_content()) ?></div>
    </div><?php endforeach ?>

I get <pre> tags added so that’s to be expected, I guess.
What would you like to see, the content field can be anything, happy to post any code you would like to investigate, please let me know.

Let’s start by posting the content in the content file, and the output you’re getting.

Hm, I can’t really reproduce this but the for me the kirbytags:after tag doesn’t output the accordion at all and in my setup it works well with the kirbytags:before tag.

What other plugins do you have installed?

Could you also please post your config.php file?

Good questions @texnixe

Did some tests with div tags in accordion snippet placed so it breaks the layout:

  • Disabled all plugins (got a few ;-): no change
  • Text field only with accordion tag: no change
  • Cleared config completely: bingo fixed

Cleared config bit by bit and the culprit seems to be:

'markdown' => [
        'extra' => true,
    ],

When I set Markdown Extra to false it works properly.

Side note: tables still render properly when Markdown Extra is disabled, which is supposed to be a Markdown Extra feature according to the Kirby ref. Just checked https://parsedown.org/demo and the tables are indeed part of ‘regular’ Parsedown (right?).

Can we say that this is a Parse Down Extra problem, can you reproduce?

My bits and pieces:
blueprint:

accordion:
  label: Accordion
  type: structure
  help: Write text and place the accordion tag; accordion between double curly braces, anywhere in the Text field.
  fields:
    accordion_open:
      label: Open
      type: toggle
    accordion_heading:
      label: Header
      type: text
    accordion_content:
      label: Panel
      type: textarea
      buttons:
        - italic
        - bold
        - link
        - email
        - code
        - ul
        - ol
accordion_multiple:
  label: Single panel open
  type: toggle
  width: 1/2
accordion_heading:
  default: 'h3'
  label: Accordion header
  type: select
  width: 1/2
  placeholder: Select a heading type
  options:
    - value: 'h2' 
      text: Heading 2
    - value: 'h3' 
      text: Heading 3
    - value: 'h4' 
      text: Heading 4
    - value: 'h5' 
      text: Heading 5
    - value: 'h6' 
      text: Heading 6

plugin:

<?php

Kirby::plugin('zoon/accordion', [
    'hooks' => [
        'kirbytags:after' => function ($text, $data) {

            return preg_replace_callback('!{{ accordion }}!siU', function($match) use ($data) {
                
                if ($items = $data['parent']->accordion()->toStructure()) {
                    $block = snippet('accordion', ['items' => $items], true);
                } else {
                    $block = '';
                }
                return $block;

            }, $text);
           
        }
    ]
]);

snippet:

<?php if($items->count()): ?>
<div class="js-accordion" data-accordion-prefix-classes="animated-accordion" <?= e($page->accordion_multiple()->bool(), 'data-accordion-multiselectable="none"', '') ?>>
<?php $n=0; foreach ($items as $item): $n++; ?>
<div class="js-accordion__panel" <?= e($item->accordion_open()->bool(), 'data-accordion-opened="true"', '') ?>>
<?php if($page->accordion_heading()->isNotEmpty()): ?>
  <?= '<' . $page->accordion_heading()->value() . ' class="js-accordion__header">'?>
    <?= smartypants($item->accordion_heading()) ?>
  <?= '</' . $page->accordion_heading()->value() . '>' ?>
<?php else: ?>
  <h3 class="js-accordion__header">
    <?= smartypants($item->accordion_heading()) ?>
  </h3>
<?php endif ?>
<div class="js-accordion__content">
    <?= $item->accordion_content()->kt() ?></div>
</div>
<?php endforeach ?>
</div>
<?php endif ?>

… and this code for selecting a heading type (h2–h6) doesn’t work full stop, but I can try different ways:

<?php if($page->accordion_heading()->isNotEmpty()): ?>
  <?= '<' . $page->accordion_heading()->value() . ' class="js-accordion__header">'?>
    <?= smartypants($item->accordion_heading()) ?>
  <?= '</' . $page->accordion_heading()->value() . '>' ?>
<?php else: ?>
…

(Just installed Kirby Editor, would be better for UX to put an accordion into a block, also multiple accordions. But have to spend time to learn to code custom blocks first.)

That was actually what I was expecting.

We have some issues with Parsedown and there are ideas of replacing that parser some time in the future. I can’t make further tests atm.

As far as I know, tables are a feature of Markdown Extra and I wouldn’t rely on the demo.

Can I Shutdown Parsedown and switch to regular Markdown and Markdown Extra?
This is very much déjà vu: ran into some unpredictable stuff in the past also. Really frustrating and time consuming while Kirby is just very predictable and reliable.

(tables work in Kirby without md-extra)

You can replace the Parsedown with your own markdown component: https://getkirby.com/docs/reference/plugins/components/markdown

Thanks, but I’m not sure I understand.
Is that a matter of putting markdown original code into that index file?

Can you say a bit more about ideas to replace Parsedown? An easy switch to normal md would be much appreciated. Thank you.

In principle, it’s no more than doing this (installing with composer)

plugins/test/index.php

<?php 
@include_once __DIR__ . '/vendor/autoload.php';

use Michelf\Markdown;
Kirby::plugin('texnixe/test', [
    
  
    'components' => [
        'markdown' => function (Kirby $kirby, string $text = null, array $options = [], bool $inline = false) {
            return  Markdown::defaultTransform($text);
        }
    ]
]);

Thanks, I’ll give that a spin …

I’ve abandoned the use of a hook for the implementation of an accordion and other widgets. This is because Parsedown (extra) unpredictable behaviour: adding tags and breaking the layout when I not carefully plan/test the use of spaces. Which is, imho, pretty silly to have to deal with.

I’m rebuilding with Kirby-builder (the good: all sorts of added benefits).

I had the same problem and I solved it with the use of a KirbyTag and running markdown() first and then kirbytags(). Like this:

$page->text()->markdown()->clean()->kirbytags()

The clean() field method is my own custom method that strips away the enclosing <p> tags after Markdown:

App::plugin('project/plugin', [
	'fieldMethods' => [
		'clean' => function ($field) {
			$field->value = preg_replace('/<p>(\(.*?\))<\/p>/', '$1', $field->value);
			return $field;
		}
	]
]);

So if I have the following content:

Lorem ipsum:

(example: slider)

dolor sit amet.

markdown() turns it into:

<p>Lorem ipsum:</p>
<p>(example: slider)</p>
<p>dolor sit amet.</p>

clean() turns it to:

<p>Lorem ipsum:</p>
(example: slider)
<p>dolor sit amet.</p>

kirbytags() turns it to:

<p>Lorem ipsum:</p>
<div>....</div>
<p>dolor sit amet.</p>