Skip to content

content: Handle <table> elements #1031

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

Merged
merged 2 commits into from
Nov 25, 2024
Merged

Conversation

rajveermalviya
Copy link
Member

@rajveermalviya rajveermalviya commented Oct 28, 2024

Fixes: #360

Flutter Web
Screenshot 2024-10-29 at 01 50 02 Screenshot 2024-10-29 at 01 37 36

@rajveermalviya rajveermalviya force-pushed the pr-tables branch 2 times, most recently from 8db80df to ce3b7fe Compare October 28, 2024 20:24
Copy link
Collaborator

@chrisbobbe chrisbobbe left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great to see this! I might be jumping on this draft PR too early, but I had a few thoughts from a quick skim 🙂. One comment below, and:

In the 'strong (bold)' tests, let's test that a **bold** span in a header cell gets a font weight of 900, even when that span is nested inside another span. (900 is 700 from the header-cell styling plus 200 from the bold-span code.) So, another pair of tests like these:

    testFontWeight('in spoiler header',
      expectedWght: 900,
      // ```spoiler regular **bold**
      // content
      // ```
      content: plainContent(
        '<div class="spoiler-block"><div class="spoiler-header">\n'
          '<p>regular <strong>bold</strong></p>\n'
          '</div><div class="spoiler-content" aria-hidden="true">\n'
          '<p>content</p>\n'
          '</div></div>'
      ),
      styleFinder: findWordBold,
    );

    testFontWeight('in different kind of span in spoiler header',
      expectedWght: 900,
      // ```spoiler *italic **bold***
      // content
      // ```
      content: plainContent(
        '<div class="spoiler-block"><div class="spoiler-header">\n'
          '<p><em>italic <strong>bold</strong></em></p>\n'
          '</div><div class="spoiler-content" aria-hidden="true">\n'
          '<p>content</p>\n'
          '</div></div>'
      ),
      styleFinder: findWordBold,
    );

.merge(weightVariableTextStyle(context, wght: 700))
: DefaultTextStyle.of(context).style,
textAlign: node.textAlign))
: const SizedBox.shrink());
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we include the Padding in this case too? This looks like it could make a cell that doesn't take up any space at all, and I don't think that's consistent with the web app. Here's what the web app does with a table that's nothing but empty cells:

https://chat.zulip.org/#narrow/channel/7-test-here/topic/Chris/near/1971419
image

and each one does take up some space, and from the inspector it looks like that's coming from some padding (though 4px in all directions instead of 5px).

@rajveermalviya
Copy link
Member Author

Thanks for the initial comments @chrisbobbe! This is now ready for review, PTAL.

@rajveermalviya rajveermalviya marked this pull request as ready for review November 4, 2024 19:32
@PIG208 PIG208 added the maintainer review PR ready for review by Zulip maintainers label Nov 12, 2024
Copy link
Collaborator

@chrisbobbe chrisbobbe left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! Comments below.

I wonder if tools/content/check-features shows any surprises after implementing table support; do you have time to run that?

@@ -1,3 +1,5 @@
import 'dart:ui';
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We might prefer keeping dart:ui out of our model code, I'm not sure. @gnprice, what do you think? This revision uses this to put TextAlign enum values in content-node objects. Instead, we could use our own small custom enum and give our widget code the job of invoking dart:ui code.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, that adjustment SGTM.

We already import package:flutter/foundation.dart in a number of places in our model code, I think mostly for ChangeNotifier, and that transitively pulls in dart:ui. So if something would be messy to do without dart:ui, it'd be fine to import it.

But referring to something UI-specific like TextAlign is definitely a good prompt to think about if the logic would best live in our widgets code. In this example, I think this resulting change in the later revision, where this bit of logic moved from the parser to the widget:

       switch (cellStyle) {
         case null:
-          // Default text alignment specified in for `tr > th` element
-          // in `web/styles/rendered_markdown.css`:
-          //  https://github.com/zulip/zulip/blob/d556c0e0a5e9e8ba0ff548354bf2ba6ef2c97d4b/web/styles/rendered_markdown.css#L140
-          textAlign = isHeader ? TextAlign.left : null;
+          textAlignment = TableCellTextAlignment.defaults;
+      // The web client sets `text-align: left;` for the header cells,
+      // overriding the default browser alignment (which is `center` for header
+      // and `start` for body). By default, the [Table] widget uses `start` for
+      // text alignment, a saner choice that supports RTL text. So, defer to that.
+      // See discussion:
+      //  https://github.com/zulip/zulip-flutter/pull/1031#discussion_r1831950371
+      TableCellTextAlignment.defaults => null,

was a improvement in clarity.

Comment on lines 1293 to 1324
switch (cellStyle) {
case null:
// Default text alignment specified in for `tr > th` element
// in `web/styles/rendered_markdown.css`:
// https://github.com/zulip/zulip/blob/d556c0e0a5e9e8ba0ff548354bf2ba6ef2c97d4b/web/styles/rendered_markdown.css#L140
textAlign = isHeader ? TextAlign.left : null;
case 'text-align: left;':
textAlign = TextAlign.left;
case 'text-align: center;':
textAlign = TextAlign.center;
case 'text-align: right;':
textAlign = TextAlign.right;
default:
return null;
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some thoughts on what we're modeling, and how to split responsibilities between model code and UI code.

A message author has four options to set the text alignment of a column's cells (as in GFM):

  1. |: - | all cells' text left-aligned
  2. |: - :| all cells' text center-aligned
  3. | - :| all cells' text right-aligned
  4. | - | accept default for each cell.

In the HTML we're parsing, the setting is represented as:

  1. all the column's cells have style="text-align: left;"
  2. all the column's cells have style="text-align: center;"
  3. all the column's cells have style="text-align: right;"
  4. all the column's cells have no style attribute.

Putting something on each cell is understandable, I guess, since there isn't a per-column element to attach it to; a table's cells are organized as a list of rows, not columns.

I have doubts about RTL support with the "left" and "right" options, but I don't fully understand the bugs involved, so for now I think it's best to just interpret and apply those options literally. So, as in this revision, left-aligning each cell that has text-align: left; and right-aligning each cell that has text-align: right;, whether the text is LTR or RTL. Let's include a TODO(i18n) pointing to that RTL issue I filed in zulip/zulip.

The "center" option should be fine because center means the same in RTL and LTR.

For the fourth option, "defaults", how about we let UI code handle the specifics of what the defaults are? So, remove (from this file) the code that sets left-alignment for header cells. Web's idea of "default" alignment comes from the user-agent stylesheet (typically start-alignment for non-header cells) and from some custom CSS that you linked to (left-alignment for header cells)1. Actually, web's choice to left-align instead of start-align header cells sounds like a bug that we don't want to carry over; let's start-align all cells. From a quick test, that's the default behavior from Flutter's table widgets anyway, so that'll be easy.

How about representing those four options with an enum, like:

// The text-alignment setting that applies to a cell's column, from the delimiter row.
//
// See GitHub-flavored Markdown:
//   https://github.github.com/gfm/#tables-extension-
enum TableColumnTextAlignment {
  /// All cells' text left-aligned, represented in Markdown as `|: --- |`.
  left, // TODO(i18n) RTL issues? https://github.com/zulip/zulip/issues/32265
  /// All cells' text center-aligned, represented in Markdown as `|: --- :|`.
  center,
  /// All cells' text right-aligned, represented in Markdown as `| --- :|`.
  right, // TODO(i18n) RTL issues? https://github.com/zulip/zulip/issues/32265
  /// Cells' text aligned the default way, represented in Markdown as `| --- |`.
  defaults,
}

? And the widget code does the appropriate thing with those values. It'll be fine to store the value on each cell, given that our representation (like the HTML) lacks a single per-column place for it.

I understand we could get correct behavior by just having a dart:ui TextAlign? on the TableCellNode. But I think it's helpful to connect the HTML to the per-column setting, for readers, and the enum's dartdoc is a convenient place to do that. Also, as mentioned, I like delegating the details of the defaults option to UI code, even if the details are very simple, as they (currently) are.

Footnotes

  1. Apparently the user-agent stylesheet typically center-aligns text in header cells; I guess the custom CSS is a reaction to that.

return TableCell(
verticalAlignment: TableCellVerticalAlignment.middle,
child: Padding(
padding: const EdgeInsets.all(4),
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
padding: const EdgeInsets.all(4),
// Web has 4px padding and 1px border on all sides.
// In web, the 1px border grows each cell by 0.5px in all directions.
// Our border doesn't affect the layout, it's just painted on,
// so we add 0.5px on all sides to match web.
padding: const EdgeInsets.all(4 + 0.5),

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Interesting, found an upstream issue for this: flutter/flutter#78691.
Included the link for reference.

@rajveermalviya rajveermalviya force-pushed the pr-tables branch 2 times, most recently from 9450a99 to 979b7ce Compare November 15, 2024 16:51
@rajveermalviya
Copy link
Member Author

Thanks for the review @chrisbobbe! Pushed a new revision.
I also ran the tools/content/check-features script and found that with this PR there are zero failures for the <table> content 🎉. This is hopefully accurate, as the current parser impl is overly strict about missing or extra classes, attributes and nodes.

Copy link
Collaborator

@chrisbobbe chrisbobbe left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! Small comments below.

// row based and needs alignment information to be per cell.
enum TableCellTextAlignment {
/// Text inside the table cell is left-aligned.
// Represented in markdown as `|: --- |` for the specific column in the delimeter row.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: "delimiter" (here and below)

Comment on lines 546 to 559
/// The text-alignment that applies to text inside the table cell.
///
/// See GitHub-flavored Markdown:
/// https://github.github.com/gfm/#tables-extension-
// In Markdown, alignment is defined per column using the delimiter row.
// However, the generated HTML specifies alignment for each cell in a row
// individually, that matches the UI widget implementation which is also
// row based and needs alignment information to be per cell.
enum TableCellTextAlignment {
/// Text inside the table cell is left-aligned.
// Represented in markdown as `|: --- |` for the specific column in the delimeter row.
// TODO(i18n) RTL issues? https://github.com/zulip/zulip/issues/32265
left,
/// Text inside the table cell is center-aligned.
// Represented in markdown as `|: --- :|` for the specific column in the delimeter row.
center,
/// Text inside the table cell is right-aligned.
// Represented in markdown as `| --- :|` for the specific column in the delimeter row.
// TODO(i18n) RTL issues? https://github.com/zulip/zulip/issues/32265
right,
/// Text inside the table cell is aligned the default way.
// Represented in markdown as `| --- |` for the specific column in the delimeter row.
defaults
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In how we name and document this enum, I'd like to be more explicit that what's being represented is a per-column setting, in the string from the server and in our model. That's how users experience the feature: we tell them they can set the alignment per-column, but not per individual cell (or for a row, or for the whole table, etc.).

It's true that the enum value is repeated on all of a column's cells, and our parsing logic has to understand that fact in order to recognize what the setting is for a given column. I also think it's fine for our model to repeat something on all of a column's cells too, because that's convenient for our parsing code and our UI code. But when doing those things, I'd still like the per-column abstraction to be clear somewhere, and I think the best place for that is in this enum's name and documentation. As in my proposal in #1031 (comment) :

// The text-alignment setting that applies to a cell's column, from the delimiter row.
//
// See GitHub-flavored Markdown:
//   https://github.github.com/gfm/#tables-extension-
enum TableColumnTextAlignment {
  /// All cells' text left-aligned, represented in Markdown as `|: --- |`.
  left, // TODO(i18n) RTL issues? https://github.com/zulip/zulip/issues/32265
  /// All cells' text center-aligned, represented in Markdown as `|: --- :|`.
  center,
  /// All cells' text right-aligned, represented in Markdown as `| --- :|`.
  right, // TODO(i18n) RTL issues? https://github.com/zulip/zulip/issues/32265
  /// Cells' text aligned the default way, represented in Markdown as `| --- |`.
  defaults,
}

In theory, our parsing code could grow a validation step to make sure the setting is expressed coherently, i.e., scan through a column's cells to check that their style attribute is consistent. I don't think we have to do that (could be expensive), but I do think it would be consistent with what's supposed to be represented.

I think the explicit per-column abstraction will be helpful when we come back to address the RTL issues. As part of that work, we'll want to get clear ideas of what we expect to happen vs. what's actually happening, at all of the steps from the compose-box input to how the message appears in the message list.

Copy link
Member Author

@rajveermalviya rajveermalviya Nov 21, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Makes sense, updated the enum's name and the documentation with this proposal.

@rajveermalviya
Copy link
Member Author

Thanks for the review @chrisbobbe! Pushed a new revision, PTAL.

@chrisbobbe
Copy link
Collaborator

Thanks, LGTM! Marking for Greg's review.

@chrisbobbe chrisbobbe assigned gnprice and unassigned chrisbobbe Nov 22, 2024
@chrisbobbe chrisbobbe added integration review Added by maintainers when PR may be ready for integration and removed maintainer review PR ready for review by Zulip maintainers labels Nov 22, 2024
Copy link
Member

@gnprice gnprice left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @rajveermalviya for building this, and @chrisbobbe for the previous reviews!

Generally this looks great. Various comments below, mainly small.

@override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties);
properties.add(DiagnosticsProperty<bool>('isHeader', isHeader));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: instead of DiagnosticsProperty<bool>, use FlagProperty

(see the latter's docs; and for examples, see the handful of existing places we use it in this file)

Comment on lines 950 to 951
static const tableWithDifferentTextAligmentInColumns = ContentExample(
'table with different text aligment in columns',
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: spelling

Suggested change
static const tableWithDifferentTextAligmentInColumns = ContentExample(
'table with different text aligment in columns',
static const tableWithDifferentTextAlignmentInColumns = ContentExample(
'table with different text alignment in columns',

@override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties);
properties.add(DiagnosticsProperty<TableColumnTextAlignment>('textAlign', textAlignment));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: use EnumProperty

And set defaultValue to TableColumnTextAlignment.defaults — that way this won't get printed when that's the value, since that's the typical value and it's boring for a cell/column to have that value

Comment on lines 1546 to 1526
case 'blockquote':
case 'div':
case 'table':
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: keep in same order as parsing logic above, for ease of comparison

Suggested change
case 'blockquote':
case 'div':
case 'table':
case 'blockquote':
case 'table':
case 'div':

TableCellNode? parseTableCell(dom.Element node, bool isHeader) {
assert(node.localName == (isHeader ? 'th' : 'td'));
assert(node.className.isEmpty);
assert(node.attributes.length <= 1);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Our existing parsing code tolerates attributes it didn't expect, while being strict about classes it didn't expect. So let's follow that here.

(It's possible somewhere else would be a better place to draw the line in general. But until we revisit that, it's easier to think about if the line is the same for different types of content.)

Comment on lines 1295 to 1296
? _buildBlockInlineContainer(
node: node,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: indentation

Suggested change
? _buildBlockInlineContainer(
node: node,
? _buildBlockInlineContainer(
node: node,

Comment on lines 1294 to 1295
child: node.nodes.isNotEmpty
? _buildBlockInlineContainer(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: put short exceptional case first, then long normal case (just like we regularly do with early returns)

Suggested change
child: node.nodes.isNotEmpty
? _buildBlockInlineContainer(
child: node.nodes.isEmpty
? const SizedBox.shrink()
: _buildBlockInlineContainer(

]),
]);

static const tableWithLinksInHeaderCells = ContentExample(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This and the one with links in body cells can be just one example, with one column and a total of two cells, and a link in each of them.

That'd cover everything these do, right?

]),
]);

static const tableWithImagesInCells = ContentExample(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about a version of this with just one column, and one image?

if (node.localName != 'tr') return false;
if (node.className.isNotEmpty) return false;
if (node.attributes.isNotEmpty) return false;
if (node.nodes.isEmpty) return false;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like this case can happen:
https://chat.zulip.org/#narrow/channel/7-test-here/topic/table/near/1987383

| table |
| - |

… Huh but I'm puzzled because the app with this PR handles that message just fine. Not sure how.

Oh I see, it's that the HTML has no tbody element at all.

Anyway, let's have a test for that edge case.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(I found this edge case by following the handy link to here: https://github.github.com/gfm/#tables-extension- and then reading through it looking for any odd cases that would make it through to the HTML. That's the only one I found.)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, looks like for this case the generated HTML adds the empty <td> node:

<table>
<thead>
<tr>
<th>table</th>
</tr>
</thead>
<tbody>
<tr>
<td></td>
</tr>
</tbody>
</table>

Something similar happens when markdown misses a column in the body row, the generated HTML here again contains an empty <td> node for the missing cell in markdown:

| a | b |
| - | - |
| a |
<table>
<thead>
<tr>
<th>a</th>
<th>b</th>
</tr>
</thead>
<tbody>
<tr>
<td>a</td>
<td></td>
</tr>
</tbody>
</table>

I've added tests for these two cases, and kept this assert in to catch if that changes in future.


Additionally, I've also restructured the parsing code for <thead> and <tbody> elements to ensure that both nodes are always present.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, looks like for this case the generated HTML adds the empty <td> node:

I see — and a <tr> around that. I guess that also suffices for causing this code (with the rejection on node.nodes.isEmpty) to work correctly.

I was looking at the GFM spec:
https://github.github.com/gfm/#example-205
but hadn't checked what Zulip actually produces. Good to have checked!

Something similar happens when markdown misses a column in the body row,

Yeah. That part matches the spec:
https://github.github.com/gfm/#example-204

@rajveermalviya
Copy link
Member Author

Thanks for the review @gnprice! Pushed a new revision, PTAL.

Comment on lines 579 to 580
properties.add(EnumProperty<TableColumnTextAlignment>(
'textAlignment', textAlignment,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: type can be inferred

Suggested change
properties.add(EnumProperty<TableColumnTextAlignment>(
'textAlignment', textAlignment,
properties.add(EnumProperty('textAlignment', textAlignment,

if (node.localName != 'tr') return false;
if (node.className.isNotEmpty) return false;
if (node.attributes.isNotEmpty) return false;
if (node.nodes.isEmpty) return false;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, looks like for this case the generated HTML adds the empty <td> node:

I see — and a <tr> around that. I guess that also suffices for causing this code (with the rejection on node.nodes.isEmpty) to work correctly.

I was looking at the GFM spec:
https://github.github.com/gfm/#example-205
but hadn't checked what Zulip actually produces. Good to have checked!

Something similar happens when markdown misses a column in the body row,

Yeah. That part matches the spec:
https://github.github.com/gfm/#example-204

Copy link
Member

@gnprice gnprice left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the revision! Comments below — one more test case and otherwise all nits.

if (tbodyElement.className.isNotEmpty) return null;
if (tbodyElement.nodes.isEmpty) return null;

late final int headerColumnCount;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: can avoid late:

Suggested change
late final int headerColumnCount;
final int headerColumnCount;

That way the compiler is asked to prove to itself that the variable gets correctly initialized, so we'd get a static error if some future change broke that.

Comment on lines 1046 to 1047
static const tableWithDifferentTextAlignmentInColumns = ContentExample(
'table with different text aligment in columns',
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: bump #1031 (comment) 🙂

Suggested change
static const tableWithDifferentTextAlignmentInColumns = ContentExample(
'table with different text aligment in columns',
static const tableWithDifferentTextAlignmentInColumns = ContentExample(
'table with different text alignment in columns',

super.debugHtmlNode,
required super.nodes,
required super.links,
required this.textAlignment,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit:

content: Handle column `text-alignment` in `<table>`

The backticks on "text-alignment" mark it as code, not prose. When a name is formatted as code, it should match exactly the name of something it's referring to in the code. But I don't think there's anything in the code called text-alignment.

Could make it not code, just normal English prose:

content: Handle column text-alignment in <table>

Or could refer to a name found in the code; for example:

content: Handle text-align in <table>

(for the CSS property; which then isn't so much about a column as a cell).

Comment on lines 579 to 580
properties.add(EnumProperty(
'textAlignment', textAlignment,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: tighten (bump #1031 (comment))

Suggested change
properties.add(EnumProperty(
'textAlignment', textAlignment,
properties.add(EnumProperty('textAlignment', textAlignment,

Comment on lines 1009 to 1010
])
]);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: use trailing comma whenever not closing list on same line

Suggested change
])
]);
]),
]);

(here and a couple of other spots)

Comment on lines 988 to 989
static const tableWithImagesInCells = ContentExample(
'table with images in cells',
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: just one image (and "in cells" is redundant):

Suggested change
static const tableWithImagesInCells = ContentExample(
'table with images in cells',
static const tableWithImage = ContentExample(
'table with image',

])
]);

static const tableWithoutAnyBodyCellsInMarkdown = ContentExample(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here's a comment to clarify a bit why we bother to have this test case:

Suggested change
static const tableWithoutAnyBodyCellsInMarkdown = ContentExample(
// As is, this HTML doesn't look particularly different to our parser.
// But if Zulip's table support followed GFM, this would have no <tbody>:
// https://github.github.com/gfm/#example-205
// https://github.com/zulip/zulip-flutter/pull/1031#discussion_r1855931989
static const tableWithoutAnyBodyCellsInMarkdown = ContentExample(

Comment on lines -869 to +904
style: widget.style, nodes: widget.nodes);
style: widget.style, nodes: widget.nodes, textAlign: widget.textAlign);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cool, I think this fixes #1031 (comment) .

Let's have a test case that would have caught that bug. Doesn't need to exercise all the possible alignments (that's covered by another test case you already have), just one of them.

@rajveermalviya
Copy link
Member Author

Thanks for the review @gnprice! Pushed a new revision, PTAL.

@gnprice
Copy link
Member

gnprice commented Nov 25, 2024

Thanks! Looks good; merging.

@gnprice gnprice merged commit 15705ad into zulip:main Nov 25, 2024
@rajveermalviya rajveermalviya deleted the pr-tables branch November 26, 2024 10:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
integration review Added by maintainers when PR may be ready for integration
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Handle <table> elements
4 participants