In this weekly series, Martin describes a question that was raised by a Fonto developer, how it was resolved and why Fonto behaved like that in the first place. This week, a partner uses an uncommon way to defined sublists: with different element names!
A support question came in that went a little like this:
Our client does not use a list in a list or a list inside a list item to define sublists. Instead they use a different element name. A list item can contain a sublist element, which is a separate element, which has list items as well.
Our visualization works as expected, however, when inserting a sublist in a list, we get a wrong result:
Visually, the lists would display:
1. List item 1
a. Sublist item 1
b. Sublist item 2
2. List item 2When we insert a sublist at the end of list item 2 below we get the following result:
1. List item 1
a. Sublist item 1
b. Sublist item 2
2. List item 2
3. a. Sublist item 1But we expected:
1. List item 1
a. Sublist item 1
b. Sublist item 2
2. List item 2
a. Sublist item 1Do you have any suggestions on how to get this to work? Do we have to write a custom mutation? We were wondering if the code to the insert-list-item would give us insight as that is creating the list in a list.
Let’s take a look at what kind of XML we are trying to achieve. The initial list looks something like this in XML:
<list> <item> <p>List item 1</p> <sublist> <item> <p>Sublist item 1</p> </item> <item> <p>Sublist item 2</p> </item> </sublist> </item> <item> <p>List item 2</p> </item> </list>
This of course differs from how lists often look, where the element name of a sublist is he same as the element name of the root list. This causes a bug when executing the convert-list
operation, which is behind the list
button. When attempting to insert a sublist after the ‘list item 2’, this happens:
<list> <item> <p>List item 1</p> <sublist> <item> <p>Sublist item 1</p> </item> <item> <p>Sublist item 2</p> </item> </sublist> </item> <item> <sublist> <item> <p>List item 2</p> </item> </sublist> </item> </list>
Specifically, the paragraph containing ‘List item 2’ is converted to a sublist, instead of a new sublist being inserted! Turns out this was happening because the convert-list
operation chose to convert the paragraph instead of inserting a new list. This is quite commonly what you want to do. To fix this, we changed the paragraphSelector
argument to the operation to not match these paragraphs in normal lists: self::p and not(parent::item/parent::list)
. This fixed this bug and the customer was content.
But why?
But why did the operation chose to do this? And why did changing the selector fix it? For that, we should see how the convert-list
operation works:
- First: if we have a selection in a list of a different type, attempt to convert it;
- If that fails, see if we have a selection of multiple items in the same list as the one we are working with, and see if we can convert them to normal paragraphs:
pull them out of the list; - If that fails, convert the paragraph that contains the selection to a list;
- If that fails, just insert a list with a single item on the place of the cursor;
- If that also fails, give up: the operation is disabled.
In HTML-like lists, where nesting is defined by the nesting of list elements, lists are converted when you press the ‘convert-list’ button for the same list as the one containing your selection. Or a list will be unwrapped if the type does match.
In our case, the first step fails because a <sublist />
element is only allowed inside <item />
elements. In the situation at hand, the <list />
element is contained in a division element, which cannot contain a <sublist />
, which causes the step to fail.
The second step fails because the outer list is not a <sublist />
: it is a <list />
. The list cannot be unwrapped like that.
The third step succeeds: we can wrap the <p />
to a new <sublist />
, so we think we are done. The convert-list
operation accepts a paragraphSelector
argument that we can use to tell the operation what to consider as paragraphs. If we do not consider paragraphs in lists as convertible paragraphs, we can direct the operation!
I hope this explained how Fonto works and why it works like that. During the years we built quite the product and we are aware some parts work in unexpected ways for those who have not been with it from the start. If you have any points of Fonto you would like some focus on, we are always ready and willing to share! Reach out on Twitter to Martin Middel or file a support issue!
Developer advocate / Evangelist. Has been with Fonto since it all began in 2013. He’s currently designing the next steps in Fonto Developer APIs with the input of our valuable partners.
In his spare time, Martin is an avid home brewer.
Receive updates on new Fonto Why & How blog posts by email