-
-
Notifications
You must be signed in to change notification settings - Fork 3.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
#7454: Down-casting whole table on headingRows attribute change #7572
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I caught one thing only - I think that one model.change()
is not needed.
Another thing - could you check if a test for external attribute operation is able to check if the table was refreshed? There might be some tests for similar case in table content refresh post-fixer.
// Must be completely wrapped in enqueueChange to get the current table state (after applying other enqueued changes). | ||
model.enqueueChange( batch, writer => { | ||
function updateHeadingRows( table, first, last, model ) { | ||
model.change( writer => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think that this is not needed any more - it is already called in a model.change()
block.
// Table heading rows change requires reconversion of the whole table. | ||
this.listenTo( model, 'applyOperation', headingRowsAttributeChangeHandler( model ) ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@scofalik & @Reinmar - I need to double check this as this changes how table is converted.
After numerous bugs with changing headings + removing rows we concluded that enough is enough. The table models sucks. Downcasting headingRows
change requires moving <tr>
in the view from <thead>
to <tbody>
or vice-versa. It can fail in multiple scenarios (#6502, #6437, #6544, #6502, #6391, #6406 + #7454) and would potentially never be OK.
Here (#7454) there was a problem with table layout post-fixer which was called after each enqueueChange()
- and thus we hit limit of what API is allowing us to do. The solution is to wait with conversion and post-fixing after all table model changes are done and that table
attribute converter would know that also table
children are changing.
Possible considered solutions were:
- A "hack": set heading rows to 0, remove/add rows, set heading rows to proper value - but it lacks an API for not converting stuff at once.
- Some API to mark that some changes need to be either converted at once or should not trigger model post-fixers.
- Mark
table
to be re-rendered at once.
We choose option 3
using differ.refreshItem()
- which is called on every headingRows
attribute operation as It requires to also take external operations into account when dealing with this bug. Also the API for many TableUtils
get simplified but not passing a batch
instance (:tada:).
In other words the solution is to re-render whole table in the view if headingRows
attribute was changed by any means.
What is needed - some kind of 👍 / 👎 - it might impact collaboration features in some way. Do we need better API for requiring re-converting whole chunk of content?
ps.: @Reinmar this is also some sort of ADR proposal (without sections - but have intro, considered steps and proposed solution).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why this is done on applyOperation
not in a post-fixer?
As a rule of thumb, we try to avoid doing stuff on applyOperation
- this is the last resort solution. Getting between multiple operations is a risky business. OTOH, I see that you only mark the table to refresh, so no changes in the model tree are done. Might be okay.
The one difference that quickly comes to mind is that you might have an attribute operation for heading rows and then an operation that reverses it, so in the end, there's no change. Post-fixer would not refresh the table, AFAIU.
So, once again, if it can be done through post-fixer and there are no counterarguments, I think I'd go with post-fixer. But I don't see real big disadvantages in doing this with applyOperation
either, other than we simply avoid it because it is a more vulnerable "place", if you know what I mean.
@ckeditor/qa-team This change requires some testing help. The scenarios which this change touch are for all structural changes inside (or "touching") heading section, like:
|
1 similar comment
@ckeditor/qa-team This change requires some testing help. The scenarios which this change touch are for all structural changes inside (or "touching") heading section, like:
|
Seems to be ok, I didn't find any new bugs 👍 |
@FilipTokarski could you test it with real-time editing? |
EDIT: I can also reproduce it on live docs now so it's not introduced in this PR, I'll create a separate ticket for this
Result:
|
@FilipTokarski could you check that failing case once again? Maybe last change would fix it. |
I'm still getting 2 header rows in the model, and 3 in view. But apart from this issue, as it comes to real-time editing, things seem to work fine 👍 |
I guess that the table is incorrect and not refreshed because |
It looks like weird bug - the table headings must be updated if you insert row in a heading section. before: insert row in heading section after Dunno why the model doesn't reflect that but the view does o_0. |
Suggested merge commit message (convention)
Fix (table): Table structure should not be changed while removing heading row. Closes #7454.
Fix (table): Merging multiple cells should not crash editor while removing multiple empty rows and columns that appeared after merge.
Additional information