gutenberg icon indicating copy to clipboard operation
gutenberg copied to clipboard

Footnote numbers are incorrect when paragraphs are deleted and undone.

Open torounit opened this issue 1 year ago • 3 comments

Description

Footnote numbers are incorrect when paragraphs are deleted and undone.

Step-by-step reproduction instructions

  1. Set footnotes for each of several paragraphs.
  2. Delete the first paragraph.
  3. Undo.

Screenshots, screen recording, code snippet

https://github.com/WordPress/gutenberg/assets/1908815/360284a9-aa75-4a85-a13f-26faeb7f04a2

Environment info

  • WordPress 6.4.3
  • Enable and disable Gutenberg 17.9.0

Please confirm that you have searched existing issues in the repo.

Yes

Please confirm that you have tested with all plugins deactivated except Gutenberg.

Yes

torounit avatar Mar 20 '24 17:03 torounit

Hi, This issue has gone 30 days without any activity. This means it is time for a check-in to make sure it is still relevant. If you are still experiencing this issue with the latest versions, you can help the project by responding to confirm the problem and by providing any updated reproduction steps. Thanks for helping out.

github-actions[bot] avatar Apr 20 '24 00:04 github-actions[bot]

I was able to reproduce this issue using the described steps on WordPress 6.5.5 with Gutenberg 18.7.1.

I noticed that once you've reproduced the issue by deleting and then undoing the delete of the first paragraph, if you add another footnote then all footnotes are updated to the correct numbers.

stacimc avatar Jul 09 '24 21:07 stacimc

Hi, I tested this issue on the Gutenberg version (20.9.0) and can confirm that the incorrect footnote numbering still persists after deleting and undoing paragraphs.

To verify:

  • I added footnotes to multiple paragraphs.
  • Deleted the first paragraph.
  • Used the undo to restore the deleted paragraph.

After these steps, the footnote numbers did not restore correctly.

Video demonstration

https://github.com/user-attachments/assets/715a9fbf-13d1-45eb-ad2c-1391d7e26036

R1shabh-Gupta avatar Jun 05 '25 09:06 R1shabh-Gupta

I'm unable to reproduce this bug with the latest Gutenberg.

Update: I was able to reproduce it, but the bug isn't consistent. More details - https://github.com/WordPress/gutenberg/pull/70911#issuecomment-3126015081.

Screencast

https://github.com/user-attachments/assets/32635380-1d06-44b4-89b0-b7500aac1ff4

Mamaduka avatar Jul 28 '25 07:07 Mamaduka

Hi so after some researching and debugging, I was able to pinpoint the location of where the issue was arising.

Testing

When we are editing the post, the editedBlock being defined is returned directly. This is where the issue occurs. When removing and undoing the first footnote, the editedBlock is returned as it is hence and the when exploring the value of the RichText I found that the innerHtml of core/footnotes with sup tag has it's value set to 1 and not the updated value 2.

For the Adding footnotes to saved para case the OriginalHTML of block doesn't have footnotes.

Image

For deleting and undo when adding a new block to post having existing footnotes case This still happens but things gets a bit confusing for me. The OriginalHTML is correct in this case ( showing text as 2 ) but the innerHTML in RichTextData is set to 1 aka it didn't update in both case.

Image

Tried Approaches

My first approach on this was to use the existing updateFootnotesFromMeta on editedBlocks but when tried, i found out that if statement for checking order changes will always return true for our case as ids order is correct from both footnotes block and meta.

My second approach was to remove that if statement. This approach does work but I worry about the performance as this prevents early exits due to which footnotes tags are being update every edit.

I think the second approach would work but we need to see a way to still allow early exits so footnotes tags are not updated when changes are not made to footnotes.

Update

So I tried to solve this by filtering all para blocks that have footnotes superscript and then compare the html string generated from richTextData with originalContent.

If there is a diff then we continue with normal update else we early exit.

Sample Code

// regex to match <sup data-fn="..." class="fn"><a href="..." id="...">...</a></sup>"
const footnotesBlocks = blocks
	.filter(
		( block ) =>
			block.originalContent &&
			/<sup\s+data-fn="[^"]+"\s+class="fn">\s*<a\s+href="[^"]+"\s+id="[^"]+">.*?<\/a>\s*<\/sup>/.test(
				block.originalContent
			)
	)
	.map( ( block ) => {
		return {
			block,
			dataFn: create( {
				html: block.originalContent,
			} ).replacements.filter(
				( replacement ) => replacement.type === 'core/footnote'
			)[ 0 ].attributes[ 'data-fn' ],
		};
	} )
	.reduce( ( acc, { block, dataFn } ) => {
		acc[ dataFn ] = block;
		return acc;
	}, {} );

if ( ! Object.keys( footnotesBlocks ).length ) {
	return output;
}

const newOrder = getFootnotesOrder( blocks );
const currentOrder = footnotes.map( ( fn ) => fn.id );

if ( currentOrder.join( ' ' ) === newOrder.join( ' ' ) ) {
	let isDiffFootnotes = false;
	for ( const fn of footnotes ) {
		const block = footnotesBlocks[ fn.id ];
		const oldHtml = toHTMLString( { value: block.attributes.content } );
		const originalHTML = ( block.originalContent || '' )
			.replace( '<p>', '' )
			.replace( '</p>', '' );

		if ( oldHtml !== originalHTML ) {
			isDiffFootnotes = true;
			break;
		}
	}

	if ( ! isDiffFootnotes ) {
		return output;
	}
}

cc: @Mamaduka

USERSATOSHI avatar Jul 29 '25 13:07 USERSATOSHI

cc @mcsf, @ellatrix

Mamaduka avatar Jul 30 '25 04:07 Mamaduka