Repository: python-cmd2/cmd2
Pull request #1163 (2021-11-10 03:34:02)

Added ability to set border background color on BorderTables and AlternatingTables

codecov[bot]: # [Codecov](https://codecov.io/gh/python-cmd2/cmd2/pull/1163?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=python-cmd2) Report
> Merging [#1163](https://codecov.io/gh/python-cmd2/cmd2/pull/1163?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=python-cmd2) (ddd00e2) into [master](https://codecov.io/gh/python-cmd2/cmd2/commit/af473b87c19497e35e862d5f31236f86d11f89cb?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=python-cmd2) (af473b8) will **decrease** coverage by `1.15%`.
> The diff coverage is `100.00%`.

[![Impacted file tree graph](https://codecov.io/gh/python-cmd2/cmd2/pull/1163/graphs/tree.svg?width=650&height=150&src=pr&token=eks2PUwa6A&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=python-cmd2)](https://codecov.io/gh/python-cmd2/cmd2/pull/1163?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=python-cmd2)

```diff
@@            Coverage Diff             @@
##           master    #1163      +/-   ##
==========================================
- Coverage   98.55%   97.39%   -1.16%     
==========================================
  Files          22       22              
  Lines        5657     5642      -15     
==========================================
- Hits         5575     5495      -80     
- Misses         82      147      +65     
```


| [Impacted Files](https://codecov.io/gh/python-cmd2/cmd2/pull/1163?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=python-cmd2) | Coverage Δ | |
|---|---|---|
| [cmd2/table\_creator.py](https://codecov.io/gh/python-cmd2/cmd2/pull/1163/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=python-cmd2#diff-Y21kMi90YWJsZV9jcmVhdG9yLnB5) | `100.00% <100.00%> (ø)` | |
| [cmd2/clipboard.py](https://codecov.io/gh/python-cmd2/cmd2/pull/1163/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=python-cmd2#diff-Y21kMi9jbGlwYm9hcmQucHk=) | `71.42% <0.00%> (-28.58%)` | :arrow_down: |
| [cmd2/rl\_utils.py](https://codecov.io/gh/python-cmd2/cmd2/pull/1163/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=python-cmd2#diff-Y21kMi9ybF91dGlscy5weQ==) | `71.87% <0.00%> (-28.13%)` | :arrow_down: |
| [cmd2/\_\_init\_\_.py](https://codecov.io/gh/python-cmd2/cmd2/pull/1163/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=python-cmd2#diff-Y21kMi9fX2luaXRfXy5weQ==) | `96.15% <0.00%> (-3.85%)` | :arrow_down: |
| [cmd2/cmd2.py](https://codecov.io/gh/python-cmd2/cmd2/pull/1163/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=python-cmd2#diff-Y21kMi9jbWQyLnB5) | `96.76% <0.00%> (-1.41%)` | :arrow_down: |
| [cmd2/transcript.py](https://codecov.io/gh/python-cmd2/cmd2/pull/1163/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=python-cmd2#diff-Y21kMi90cmFuc2NyaXB0LnB5) | `91.96% <0.00%> (-1.02%)` | :arrow_down: |
| [cmd2/utils.py](https://codecov.io/gh/python-cmd2/cmd2/pull/1163/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=python-cmd2#diff-Y21kMi91dGlscy5weQ==) | `96.97% <0.00%> (-0.94%)` | :arrow_down: |
| [cmd2/argparse\_custom.py](https://codecov.io/gh/python-cmd2/cmd2/pull/1163/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=python-cmd2#diff-Y21kMi9hcmdwYXJzZV9jdXN0b20ucHk=) | `94.41% <0.00%> (-0.77%)` | :arrow_down: |
| [cmd2/parsing.py](https://codecov.io/gh/python-cmd2/cmd2/pull/1163/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=python-cmd2#diff-Y21kMi9wYXJzaW5nLnB5) | `99.31% <0.00%> (-0.69%)` | :arrow_down: |
| [cmd2/ansi.py](https://codecov.io/gh/python-cmd2/cmd2/pull/1163/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=python-cmd2#diff-Y21kMi9hbnNpLnB5) | `100.00% <0.00%> (ø)` | |

------

[Continue to review full report at Codecov](https://codecov.io/gh/python-cmd2/cmd2/pull/1163?src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=python-cmd2).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=python-cmd2)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/python-cmd2/cmd2/pull/1163?src=pr&el=footer&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=python-cmd2). Last update [af473b8...ddd00e2](https://codecov.io/gh/python-cmd2/cmd2/pull/1163?src=pr&el=lastupdated&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=python-cmd2). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=python-cmd2).


Patch:
diff --git a/cmd2/table_creator.py b/cmd2/table_creator.py
index 68d46a01..19eead49 100644
--- a/cmd2/table_creator.py
+++ b/cmd2/table_creator.py
@@ -748,6 +748,7 @@ class BorderedTable(TableCreator):
         column_borders: bool = True,
         padding: int = 1,
         border_fg: Optional[ansi.FgColor] = None,
+        border_bg: Optional[ansi.BgColor] = None,
         header_bg: Optional[ansi.BgColor] = None,
         data_bg: Optional[ansi.BgColor] = None,
     ) -> None:
@@ -762,6 +763,7 @@ class BorderedTable(TableCreator):
                                a row's cells. (Defaults to True)
         :param padding: number of spaces between text and left/right borders of cell
         :param border_fg: optional foreground color for borders (defaults to None)
+        :param border_bg: optional background color for borders (defaults to None)
         :param header_bg: optional background color for header cells (defaults to None)
         :param data_bg: optional background color for data cells (defaults to None)
         :raises: ValueError if tab_width is less than 1
@@ -776,18 +778,19 @@ class BorderedTable(TableCreator):
         self.padding = padding
 
         self.border_fg = border_fg
+        self.border_bg = border_bg
         self.header_bg = header_bg
         self.data_bg = data_bg
 
-    def apply_border_fg(self, value: Any) -> str:
+    def apply_border_color(self, value: Any) -> str:
         """
-        If defined, apply the border foreground color to border text
+        If defined, apply the border foreground and background colors
         :param value: object whose text is to be colored
         :return: formatted text
         """
-        if self.border_fg is None:
+        if self.border_fg is None and self.border_bg is None:
             return str(value)
-        return ansi.style(value, fg=self.border_fg)
+        return ansi.style(value, fg=self.border_fg, bg=self.border_bg)
 
     def apply_header_bg(self, value: Any) -> str:
         """
@@ -853,10 +856,10 @@ class BorderedTable(TableCreator):
 
         return self.generate_row(
             row_data=self.empty_data,
-            fill_char=self.apply_border_fg(fill_char),
-            pre_line=self.apply_border_fg(pre_line),
-            inter_cell=self.apply_border_fg(inter_cell),
-            post_line=self.apply_border_fg(post_line),
+            fill_char=self.apply_border_color(fill_char),
+            pre_line=self.apply_border_color(pre_line),
+            inter_cell=self.apply_border_color(inter_cell),
+            post_line=self.apply_border_color(post_line),
         )
 
     def generate_header_bottom_border(self) -> str:
@@ -874,32 +877,32 @@ class BorderedTable(TableCreator):
 
         return self.generate_row(
             row_data=self.empty_data,
-            fill_char=self.apply_border_fg(fill_char),
-            pre_line=self.apply_border_fg(pre_line),
-            inter_cell=self.apply_border_fg(inter_cell),
-            post_line=self.apply_border_fg(post_line),
+            fill_char=self.apply_border_color(fill_char),
+            pre_line=self.apply_border_color(pre_line),
+            inter_cell=self.apply_border_color(inter_cell),
+            post_line=self.apply_border_color(post_line),
         )
 
     def generate_row_bottom_border(self) -> str:
         """Generate a border which appears at the bottom of rows"""
-        fill_char = self.apply_data_bg('─')
+        fill_char = '─'
 
-        pre_line = '╟' + self.apply_data_bg(self.padding * '─')
+        pre_line = '╟' + self.padding * '─'
 
         inter_cell = self.padding * '─'
         if self.column_borders:
             inter_cell += '┼'
         inter_cell += self.padding * '─'
-        inter_cell = self.apply_data_bg(inter_cell)
+        inter_cell = inter_cell
 
-        post_line = self.apply_data_bg(self.padding * '─') + '╢'
+        post_line = self.padding * '─' + '╢'
 
         return self.generate_row(
             row_data=self.empty_data,
-            fill_char=self.apply_border_fg(fill_char),
-            pre_line=self.apply_border_fg(pre_line),
-            inter_cell=self.apply_border_fg(inter_cell),
-            post_line=self.apply_border_fg(post_line),
+            fill_char=self.apply_border_color(fill_char),
+            pre_line=self.apply_border_color(pre_line),
+            inter_cell=self.apply_border_color(inter_cell),
+            post_line=self.apply_border_color(post_line),
         )
 
     def generate_table_bottom_border(self) -> str:
@@ -917,25 +920,24 @@ class BorderedTable(TableCreator):
 
         return self.generate_row(
             row_data=self.empty_data,
-            fill_char=self.apply_border_fg(fill_char),
-            pre_line=self.apply_border_fg(pre_line),
-            inter_cell=self.apply_border_fg(inter_cell),
-            post_line=self.apply_border_fg(post_line),
+            fill_char=self.apply_border_color(fill_char),
+            pre_line=self.apply_border_color(pre_line),
+            inter_cell=self.apply_border_color(inter_cell),
+            post_line=self.apply_border_color(post_line),
         )
 
     def generate_header(self) -> str:
         """Generate table header"""
         fill_char = self.apply_header_bg(SPACE)
 
-        pre_line = self.apply_border_fg('║') + self.apply_header_bg(self.padding * SPACE)
+        pre_line = self.apply_border_color('║') + self.apply_header_bg(self.padding * SPACE)
 
-        inter_cell = self.padding * SPACE
+        inter_cell = self.apply_header_bg(self.padding * SPACE)
         if self.column_borders:
-            inter_cell += self.apply_border_fg('│')
-        inter_cell += self.padding * SPACE
-        inter_cell = self.apply_header_bg(inter_cell)
+            inter_cell += self.apply_border_color('│')
+        inter_cell += self.apply_header_bg(self.padding * SPACE)
 
-        post_line = self.apply_header_bg(self.padding * SPACE) + self.apply_border_fg('║')
+        post_line = self.apply_header_bg(self.padding * SPACE) + self.apply_border_color('║')
 
         # Apply background color to header text in Columns which allow it
         to_display: List[Any] = []
@@ -968,15 +970,14 @@ class BorderedTable(TableCreator):
         """
         fill_char = self.apply_data_bg(SPACE)
 
-        pre_line = self.apply_border_fg('║') + self.apply_data_bg(self.padding * SPACE)
+        pre_line = self.apply_border_color('║') + self.apply_data_bg(self.padding * SPACE)
 
-        inter_cell = self.padding * SPACE
+        inter_cell = self.apply_data_bg(self.padding * SPACE)
         if self.column_borders:
-            inter_cell += self.apply_border_fg('│')
-        inter_cell += self.padding * SPACE
-        inter_cell = self.apply_data_bg(inter_cell)
+            inter_cell += self.apply_border_color('│')
+        inter_cell += self.apply_data_bg(self.padding * SPACE)
 
-        post_line = self.apply_data_bg(self.padding * SPACE) + self.apply_border_fg('║')
+        post_line = self.apply_data_bg(self.padding * SPACE) + self.apply_border_color('║')
 
         # Apply background color to data text in Columns which allow it
         to_display: List[Any] = []
@@ -1041,6 +1042,7 @@ class AlternatingTable(BorderedTable):
         column_borders: bool = True,
         padding: int = 1,
         border_fg: Optional[ansi.FgColor] = None,
+        border_bg: Optional[ansi.BgColor] = None,
         header_bg: Optional[ansi.BgColor] = None,
         odd_bg: Optional[ansi.BgColor] = None,
         even_bg: Optional[ansi.BgColor] = ansi.Bg.DARK_GRAY,
@@ -1058,6 +1060,7 @@ class AlternatingTable(BorderedTable):
                                a row's cells. (Defaults to True)
         :param padding: number of spaces between text and left/right borders of cell
         :param border_fg: optional foreground color for borders (defaults to None)
+        :param border_bg: optional background color for borders (defaults to None)
         :param header_bg: optional background color for header cells (defaults to None)
         :param odd_bg: optional background color for odd numbered data rows (defaults to None)
         :param even_bg: optional background color for even numbered data rows (defaults to StdBg.DARK_GRAY)
@@ -1065,7 +1068,13 @@ class AlternatingTable(BorderedTable):
         :raises: ValueError if padding is less than 0
         """
         super().__init__(
-            cols, tab_width=tab_width, column_borders=column_borders, padding=padding, border_fg=border_fg, header_bg=header_bg
+            cols,
+            tab_width=tab_width,
+            column_borders=column_borders,
+            padding=padding,
+            border_fg=border_fg,
+            border_bg=border_bg,
+            header_bg=header_bg,
         )
         self.row_num = 1
         self.odd_bg = odd_bg
diff --git a/examples/table_creation.py b/examples/table_creation.py
index 4fc7f2e6..78b3a0f0 100755
--- a/examples/table_creation.py
+++ b/examples/table_creation.py
@@ -39,6 +39,14 @@ class DollarFormatter:
         return "${:,.2f}".format(self.val)
 
 
+class Relative:
+    """Class used for example data"""
+
+    def __init__(self, name: str, relationship: str) -> None:
+        self.name = name
+        self.relationship = relationship
+
+
 class Book:
     """Class used for example data"""
 
@@ -55,6 +63,7 @@ class Author:
         self.birthday = birthday
         self.place_of_birth = place_of_birth
         self.books: List[Book] = []
+        self.relatives: List[Relative] = []
 
 
 def ansi_print(text):
@@ -108,9 +117,7 @@ def basic_tables():
 
 def nested_tables():
     """
-    Demonstrates how to nest tables using the style_data_text keyword to handle tables with conflicting styles.
-    In these cases, the inner tables reset the background color applied by the outer AlternatingTable.
-
+    Demonstrates how to nest tables with styles which conflict with the parent table by setting style_data_text to False.
     It also demonstrates coloring various aspects of tables.
     """
 
@@ -123,6 +130,12 @@ def nested_tables():
     author_1.books.append(Book("God Emperor of Dune", "1981"))
     author_1.books.append(Book("Heretics of Dune", "1984"))
     author_1.books.append(Book("Chapterhouse: Dune", "1985"))
+    author_1.relatives.append(Relative("Flora Lillian Parkinson", "First Wife"))
+    author_1.relatives.append(Relative("Beverly Ann Stuart", "Second Wife"))
+    author_1.relatives.append(Relative("Theresa Diane Shackelford", "Third Wife"))
+    author_1.relatives.append(Relative("Penelope Herbert", "Daughter"))
+    author_1.relatives.append(Relative("Brian Patrick Herbert", "Son"))
+    author_1.relatives.append(Relative("Bruce Calvin Herbert", "Son"))
 
     author_2 = Author("Jane Austen", "12/16/1775", "Steventon, Hampshire, England")
     author_2.books.append(Book("Sense and Sensibility", "1811"))
@@ -132,12 +145,19 @@ def nested_tables():
     author_2.books.append(Book("Northanger Abbey", "1818"))
     author_2.books.append(Book("Persuasion", "1818"))
     author_2.books.append(Book("Lady Susan", "1871"))
+    author_2.relatives.append(Relative("James Austen", "Brother"))
+    author_2.relatives.append(Relative("George Austen", "Brother"))
+    author_2.relatives.append(Relative("Edward Austen", "Brother"))
+    author_2.relatives.append(Relative("Henry Thomas Austen", "Brother"))
+    author_2.relatives.append(Relative("Cassandra Elizabeth Austen", "Sister"))
+    author_2.relatives.append(Relative("Francis William Austen", "Brother"))
+    author_2.relatives.append(Relative("Charles John Austen", "Brother"))
 
     author_data.append(author_1)
     author_data.append(author_2)
 
     # Define table which presents Author data fields vertically with no header.
-    # This will be nested in the parent table.
+    # This will be nested in the parent table's first column.
     author_columns: List[Column] = list()
     author_columns.append(Column("", width=14))
     author_columns.append(Column("", width=20))
@@ -146,18 +166,18 @@ def nested_tables():
     # When styled text is aligned, a TextStyle.RESET_ALL sequence is inserted between the aligned text
     # and the fill characters. Therefore, the Author table will contain TextStyle.RESET_ALL sequences,
     # which would interfere with the background color applied by the parent table. To account for this,
-    # we will color the Author tables to match the background colors of the parent AlternatingTable's rows
-    # and set style_data_text to False in the Author column. See below for that.
+    # we will manually color the Author tables to match the background colors of the parent AlternatingTable's
+    # rows and set style_data_text to False in the Author column.
     odd_author_tbl = SimpleTable(author_columns, data_bg=EightBitBg.GRAY_0)
     even_author_tbl = SimpleTable(author_columns, data_bg=EightBitBg.GRAY_15)
 
-    # Define AlternatingTable table for books checked out by people in the first table.
-    # This will also be nested in the parent table.
+    # Define AlternatingTable for books checked out by people in the first table.
+    # This will be nested in the parent table's second column.
     books_columns: List[Column] = list()
-    books_columns.append(Column("Title", width=25))
+    books_columns.append(Column(ansi.style("Title", bold=True), width=25))
     books_columns.append(
         Column(
-            "Published",
+            ansi.style("Published", bold=True),
             width=9,
             header_horiz_align=HorizontalAlignment.RIGHT,
             data_horiz_align=HorizontalAlignment.RIGHT,
@@ -173,17 +193,48 @@ def nested_tables():
         even_bg=EightBitBg.GRAY_15,
     )
 
+    # Define BorderedTable for relatives of the author
+    # This will be nested in the parent table's third column.
+    relative_columns: List[Column] = list()
+    relative_columns.append(Column(ansi.style("Name", bold=True), width=25))
+    relative_columns.append(Column(ansi.style("Relationship", bold=True), width=12))
+
+    # Since the header labels are bold, we have the same issue as the Author table. Therefore, we will manually
+    # color Relatives tables to match the background colors of the parent AlternatingTable's rows and set style_data_text
+    # to False in the Relatives column.
+    odd_relatives_tbl = BorderedTable(
+        relative_columns,
+        border_fg=EightBitFg.GRAY_15,
+        border_bg=EightBitBg.GRAY_0,
+        header_bg=EightBitBg.GRAY_0,
+        data_bg=EightBitBg.GRAY_0,
+    )
+
+    even_relatives_tbl = BorderedTable(
+        relative_columns,
+        border_fg=EightBitFg.GRAY_0,
+        border_bg=EightBitBg.GRAY_15,
+        header_bg=EightBitBg.GRAY_15,
+        data_bg=EightBitBg.GRAY_15,
+    )
+
     # Define parent AlternatingTable which contains Author and Book tables
     parent_tbl_columns: List[Column] = list()
 
-    # Both the Author and Books tables already have background colors. Set style_data_text
+    # All of the nested tables already have background colors. Set style_data_text
     # to False so the parent AlternatingTable does not apply background color to them.
-    parent_tbl_columns.append(Column("Author", width=odd_author_tbl.total_width(), style_data_text=False))
-    parent_tbl_columns.append(Column("Books", width=books_tbl.total_width(), style_data_text=False))
+    parent_tbl_columns.append(
+        Column(ansi.style("Author", bold=True), width=odd_author_tbl.total_width(), style_data_text=False)
+    )
+    parent_tbl_columns.append(Column(ansi.style("Books", bold=True), width=books_tbl.total_width(), style_data_text=False))
+    parent_tbl_columns.append(
+        Column(ansi.style("Relatives", bold=True), width=odd_relatives_tbl.total_width(), style_data_text=False)
+    )
 
     parent_tbl = AlternatingTable(
         parent_tbl_columns,
         column_borders=False,
+        border_fg=EightBitFg.GRAY_93,
         header_bg=EightBitBg.GRAY_0,
         odd_bg=EightBitBg.GRAY_0,
         even_bg=EightBitBg.GRAY_15,
@@ -209,8 +260,13 @@ def nested_tables():
         table_data = [[book.title, book.year_published] for book in author.books]
         book_tbl_str = books_tbl.generate_table(table_data)
 
+        # Lastly build the relatives table and color it based on row number
+        relatives_tbl = even_relatives_tbl if row % 2 == 0 else odd_relatives_tbl
+        table_data = [[relative.name, relative.relationship] for relative in author.relatives]
+        relatives_tbl_str = relatives_tbl.generate_table(table_data)
+
         # Add these tables to the parent table's data
-        parent_table_data.append(['\n' + author_tbl_str, '\n' + book_tbl_str + '\n\n'])
+        parent_table_data.append(['\n' + author_tbl_str, '\n' + book_tbl_str + '\n\n', '\n' + relatives_tbl_str + '\n\n'])
 
     # Build the parent table
     top_table_str = parent_tbl.generate_table(parent_table_data)


Test patch:
diff --git a/tests/test_table_creator.py b/tests/test_table_creator.py
index 275a1103..bc6909f8 100644
--- a/tests/test_table_creator.py
+++ b/tests/test_table_creator.py
@@ -564,16 +564,17 @@ def test_bordered_table_creation():
     assert "Padding cannot be less than 0" in str(excinfo.value)
 
     # Test border, header, and data colors
-    bt = BorderedTable([column_1, column_2], border_fg=Fg.LIGHT_YELLOW, header_bg=Bg.GREEN, data_bg=Bg.LIGHT_BLUE)
+    bt = BorderedTable([column_1, column_2], border_fg=Fg.LIGHT_YELLOW, border_bg=Bg.WHITE,
+                       header_bg=Bg.GREEN, data_bg=Bg.LIGHT_BLUE)
     table = bt.generate_table(row_data)
     assert table == (
-        '\x1b[93m╔═\x1b[39m\x1b[0m\x1b[0m\x1b[93m═══════════════\x1b[39m\x1b[0m\x1b[93m═╤═\x1b[39m\x1b[0m\x1b[0m\x1b[93m═══════════════\x1b[39m\x1b[0m\x1b[93m═╗\x1b[39m\n'
-        '\x1b[93m║\x1b[39m\x1b[42m \x1b[49m\x1b[0m\x1b[42mCol 1\x1b[49m\x1b[0m\x1b[42m          \x1b[49m\x1b[0m\x1b[42m \x1b[93m│\x1b[39m \x1b[49m\x1b[0m\x1b[42mCol 2\x1b[49m\x1b[0m\x1b[42m          \x1b[49m\x1b[0m\x1b[42m \x1b[49m\x1b[93m║\x1b[39m\n'
-        '\x1b[93m╠═\x1b[39m\x1b[0m\x1b[0m\x1b[93m═══════════════\x1b[39m\x1b[0m\x1b[93m═╪═\x1b[39m\x1b[0m\x1b[0m\x1b[93m═══════════════\x1b[39m\x1b[0m\x1b[93m═╣\x1b[39m\n'
-        '\x1b[93m║\x1b[39m\x1b[104m \x1b[49m\x1b[0m\x1b[104mCol 1 Row 1\x1b[49m\x1b[0m\x1b[104m    \x1b[49m\x1b[0m\x1b[104m \x1b[93m│\x1b[39m \x1b[49m\x1b[0m\x1b[104mCol 2 Row 1\x1b[49m\x1b[0m\x1b[104m    \x1b[49m\x1b[0m\x1b[104m \x1b[49m\x1b[93m║\x1b[39m\n'
-        '\x1b[93m╟\x1b[104m─\x1b[49m\x1b[39m\x1b[0m\x1b[0m\x1b[93m\x1b[104m───────────────\x1b[49m\x1b[39m\x1b[0m\x1b[93m\x1b[104m─┼─\x1b[49m\x1b[39m\x1b[0m\x1b[0m\x1b[93m\x1b[104m───────────────\x1b[49m\x1b[39m\x1b[0m\x1b[93m\x1b[104m─\x1b[49m╢\x1b[39m\n'
-        '\x1b[93m║\x1b[39m\x1b[104m \x1b[49m\x1b[0m\x1b[104mCol 1 Row 2\x1b[49m\x1b[0m\x1b[104m    \x1b[49m\x1b[0m\x1b[104m \x1b[93m│\x1b[39m \x1b[49m\x1b[0m\x1b[104mCol 2 Row 2\x1b[49m\x1b[0m\x1b[104m    \x1b[49m\x1b[0m\x1b[104m \x1b[49m\x1b[93m║\x1b[39m\n'
-        '\x1b[93m╚═\x1b[39m\x1b[0m\x1b[0m\x1b[93m═══════════════\x1b[39m\x1b[0m\x1b[93m═╧═\x1b[39m\x1b[0m\x1b[0m\x1b[93m═══════════════\x1b[39m\x1b[0m\x1b[93m═╝\x1b[39m'
+        '\x1b[93m\x1b[107m╔═\x1b[39m\x1b[49m\x1b[0m\x1b[0m\x1b[93m\x1b[107m═══════════════\x1b[39m\x1b[49m\x1b[0m\x1b[93m\x1b[107m═╤═\x1b[39m\x1b[49m\x1b[0m\x1b[0m\x1b[93m\x1b[107m═══════════════\x1b[39m\x1b[49m\x1b[0m\x1b[93m\x1b[107m═╗\x1b[39m\x1b[49m\n'
+        '\x1b[93m\x1b[107m║\x1b[39m\x1b[49m\x1b[42m \x1b[49m\x1b[0m\x1b[42mCol 1\x1b[49m\x1b[0m\x1b[42m          \x1b[49m\x1b[0m\x1b[42m \x1b[49m\x1b[93m\x1b[107m│\x1b[39m\x1b[49m\x1b[42m \x1b[49m\x1b[0m\x1b[42mCol 2\x1b[49m\x1b[0m\x1b[42m          \x1b[49m\x1b[0m\x1b[42m \x1b[49m\x1b[93m\x1b[107m║\x1b[39m\x1b[49m\n'
+        '\x1b[93m\x1b[107m╠═\x1b[39m\x1b[49m\x1b[0m\x1b[0m\x1b[93m\x1b[107m═══════════════\x1b[39m\x1b[49m\x1b[0m\x1b[93m\x1b[107m═╪═\x1b[39m\x1b[49m\x1b[0m\x1b[0m\x1b[93m\x1b[107m═══════════════\x1b[39m\x1b[49m\x1b[0m\x1b[93m\x1b[107m═╣\x1b[39m\x1b[49m\n'
+        '\x1b[93m\x1b[107m║\x1b[39m\x1b[49m\x1b[104m \x1b[49m\x1b[0m\x1b[104mCol 1 Row 1\x1b[49m\x1b[0m\x1b[104m    \x1b[49m\x1b[0m\x1b[104m \x1b[49m\x1b[93m\x1b[107m│\x1b[39m\x1b[49m\x1b[104m \x1b[49m\x1b[0m\x1b[104mCol 2 Row 1\x1b[49m\x1b[0m\x1b[104m    \x1b[49m\x1b[0m\x1b[104m \x1b[49m\x1b[93m\x1b[107m║\x1b[39m\x1b[49m\n'
+        '\x1b[93m\x1b[107m╟─\x1b[39m\x1b[49m\x1b[0m\x1b[0m\x1b[93m\x1b[107m───────────────\x1b[39m\x1b[49m\x1b[0m\x1b[93m\x1b[107m─┼─\x1b[39m\x1b[49m\x1b[0m\x1b[0m\x1b[93m\x1b[107m───────────────\x1b[39m\x1b[49m\x1b[0m\x1b[93m\x1b[107m─╢\x1b[39m\x1b[49m\n'
+        '\x1b[93m\x1b[107m║\x1b[39m\x1b[49m\x1b[104m \x1b[49m\x1b[0m\x1b[104mCol 1 Row 2\x1b[49m\x1b[0m\x1b[104m    \x1b[49m\x1b[0m\x1b[104m \x1b[49m\x1b[93m\x1b[107m│\x1b[39m\x1b[49m\x1b[104m \x1b[49m\x1b[0m\x1b[104mCol 2 Row 2\x1b[49m\x1b[0m\x1b[104m    \x1b[49m\x1b[0m\x1b[104m \x1b[49m\x1b[93m\x1b[107m║\x1b[39m\x1b[49m\n'
+        '\x1b[93m\x1b[107m╚═\x1b[39m\x1b[49m\x1b[0m\x1b[0m\x1b[93m\x1b[107m═══════════════\x1b[39m\x1b[49m\x1b[0m\x1b[93m\x1b[107m═╧═\x1b[39m\x1b[49m\x1b[0m\x1b[0m\x1b[93m\x1b[107m═══════════════\x1b[39m\x1b[49m\x1b[0m\x1b[93m\x1b[107m═╝\x1b[39m\x1b[49m'
     )
 
     # Make sure BorderedTable respects style_header_text and style_data_text flags.
@@ -583,11 +584,11 @@ def test_bordered_table_creation():
     table = bt.generate_table(row_data)
     assert table == (
         '╔═════════════════╤═════════════════╗\n'
-        '║\x1b[42m \x1b[49m\x1b[0m\x1b[42mCol 1\x1b[49m\x1b[0m\x1b[42m          \x1b[49m\x1b[0m\x1b[42m │ \x1b[49m\x1b[0mCol 2\x1b[0m\x1b[42m          \x1b[49m\x1b[0m\x1b[42m \x1b[49m║\n'
+        '║\x1b[42m \x1b[49m\x1b[0m\x1b[42mCol 1\x1b[49m\x1b[0m\x1b[42m          \x1b[49m\x1b[0m\x1b[42m \x1b[49m│\x1b[42m \x1b[49m\x1b[0mCol 2\x1b[0m\x1b[42m          \x1b[49m\x1b[0m\x1b[42m \x1b[49m║\n'
         '╠═════════════════╪═════════════════╣\n'
-        '║\x1b[104m \x1b[49m\x1b[0m\x1b[104mCol 1 Row 1\x1b[49m\x1b[0m\x1b[104m    \x1b[49m\x1b[0m\x1b[104m │ \x1b[49m\x1b[0mCol 2 Row 1\x1b[0m\x1b[104m    \x1b[49m\x1b[0m\x1b[104m \x1b[49m║\n'
-        '╟\x1b[104m─\x1b[49m\x1b[0m\x1b[0m\x1b[104m───────────────\x1b[49m\x1b[0m\x1b[104m─┼─\x1b[49m\x1b[0m\x1b[0m\x1b[104m───────────────\x1b[49m\x1b[0m\x1b[104m─\x1b[49m╢\n'
-        '║\x1b[104m \x1b[49m\x1b[0m\x1b[104mCol 1 Row 2\x1b[49m\x1b[0m\x1b[104m    \x1b[49m\x1b[0m\x1b[104m │ \x1b[49m\x1b[0mCol 2 Row 2\x1b[0m\x1b[104m    \x1b[49m\x1b[0m\x1b[104m \x1b[49m║\n'
+        '║\x1b[104m \x1b[49m\x1b[0m\x1b[104mCol 1 Row 1\x1b[49m\x1b[0m\x1b[104m    \x1b[49m\x1b[0m\x1b[104m \x1b[49m│\x1b[104m \x1b[49m\x1b[0mCol 2 Row 1\x1b[0m\x1b[104m    \x1b[49m\x1b[0m\x1b[104m \x1b[49m║\n'
+        '╟─────────────────┼─────────────────╢\n'
+        '║\x1b[104m \x1b[49m\x1b[0m\x1b[104mCol 1 Row 2\x1b[49m\x1b[0m\x1b[104m    \x1b[49m\x1b[0m\x1b[104m \x1b[49m│\x1b[104m \x1b[49m\x1b[0mCol 2 Row 2\x1b[0m\x1b[104m    \x1b[49m\x1b[0m\x1b[104m \x1b[49m║\n'
         '╚═════════════════╧═════════════════╝'
     )
 
@@ -646,7 +647,7 @@ def test_alternating_table_creation():
         '║ Col 1           │ Col 2           ║\n'
         '╠═════════════════╪═════════════════╣\n'
         '║ Col 1 Row 1     │ Col 2 Row 1     ║\n'
-        '║\x1b[100m \x1b[49m\x1b[0m\x1b[100mCol 1 Row 2\x1b[49m\x1b[0m\x1b[100m    \x1b[49m\x1b[0m\x1b[100m │ \x1b[49m\x1b[0m\x1b[100mCol 2 Row 2\x1b[49m\x1b[0m\x1b[100m    \x1b[49m\x1b[0m\x1b[100m \x1b[49m║\n'
+        '║\x1b[100m \x1b[49m\x1b[0m\x1b[100mCol 1 Row 2\x1b[49m\x1b[0m\x1b[100m    \x1b[49m\x1b[0m\x1b[100m \x1b[49m│\x1b[100m \x1b[49m\x1b[0m\x1b[100mCol 2 Row 2\x1b[49m\x1b[0m\x1b[100m    \x1b[49m\x1b[0m\x1b[100m \x1b[49m║\n'
         '╚═════════════════╧═════════════════╝'
     )
 
@@ -658,7 +659,7 @@ def test_alternating_table_creation():
         '║ Col 1            Col 2           ║\n'
         '╠══════════════════════════════════╣\n'
         '║ Col 1 Row 1      Col 2 Row 1     ║\n'
-        '║\x1b[100m \x1b[49m\x1b[0m\x1b[100mCol 1 Row 2\x1b[49m\x1b[0m\x1b[100m    \x1b[49m\x1b[0m\x1b[100m  \x1b[49m\x1b[0m\x1b[100mCol 2 Row 2\x1b[49m\x1b[0m\x1b[100m    \x1b[49m\x1b[0m\x1b[100m \x1b[49m║\n'
+        '║\x1b[100m \x1b[49m\x1b[0m\x1b[100mCol 1 Row 2\x1b[49m\x1b[0m\x1b[100m    \x1b[49m\x1b[0m\x1b[100m \x1b[49m\x1b[100m \x1b[49m\x1b[0m\x1b[100mCol 2 Row 2\x1b[49m\x1b[0m\x1b[100m    \x1b[49m\x1b[0m\x1b[100m \x1b[49m║\n'
         '╚══════════════════════════════════╝'
     )
 
@@ -668,7 +669,7 @@ def test_alternating_table_creation():
     assert table == (
         '╔═════════════════╤═════════════════╗\n'
         '║ Col 1 Row 1     │ Col 2 Row 1     ║\n'
-        '║\x1b[100m \x1b[49m\x1b[0m\x1b[100mCol 1 Row 2\x1b[49m\x1b[0m\x1b[100m    \x1b[49m\x1b[0m\x1b[100m │ \x1b[49m\x1b[0m\x1b[100mCol 2 Row 2\x1b[49m\x1b[0m\x1b[100m    \x1b[49m\x1b[0m\x1b[100m \x1b[49m║\n'
+        '║\x1b[100m \x1b[49m\x1b[0m\x1b[100mCol 1 Row 2\x1b[49m\x1b[0m\x1b[100m    \x1b[49m\x1b[0m\x1b[100m \x1b[49m│\x1b[100m \x1b[49m\x1b[0m\x1b[100mCol 2 Row 2\x1b[49m\x1b[0m\x1b[100m    \x1b[49m\x1b[0m\x1b[100m \x1b[49m║\n'
         '╚═════════════════╧═════════════════╝'
     )
 
@@ -680,7 +681,7 @@ def test_alternating_table_creation():
         '║  Col 1            │  Col 2            ║\n'
         '╠═══════════════════╪═══════════════════╣\n'
         '║  Col 1 Row 1      │  Col 2 Row 1      ║\n'
-        '║\x1b[100m  \x1b[49m\x1b[0m\x1b[100mCol 1 Row 2\x1b[49m\x1b[0m\x1b[100m    \x1b[49m\x1b[0m\x1b[100m  │  \x1b[49m\x1b[0m\x1b[100mCol 2 Row 2\x1b[49m\x1b[0m\x1b[100m    \x1b[49m\x1b[0m\x1b[100m  \x1b[49m║\n'
+        '║\x1b[100m  \x1b[49m\x1b[0m\x1b[100mCol 1 Row 2\x1b[49m\x1b[0m\x1b[100m    \x1b[49m\x1b[0m\x1b[100m  \x1b[49m│\x1b[100m  \x1b[49m\x1b[0m\x1b[100mCol 2 Row 2\x1b[49m\x1b[0m\x1b[100m    \x1b[49m\x1b[0m\x1b[100m  \x1b[49m║\n'
         '╚═══════════════════╧═══════════════════╝'
     )
 
@@ -690,16 +691,16 @@ def test_alternating_table_creation():
     assert "Padding cannot be less than 0" in str(excinfo.value)
 
     # Test border, header, and data colors
-    at = AlternatingTable([column_1, column_2], border_fg=Fg.LIGHT_YELLOW, header_bg=Bg.GREEN,
-                          odd_bg=Bg.LIGHT_BLUE, even_bg=Bg.LIGHT_RED)
+    at = AlternatingTable([column_1, column_2], border_fg=Fg.LIGHT_YELLOW, border_bg=Bg.WHITE,
+                          header_bg=Bg.GREEN, odd_bg=Bg.LIGHT_BLUE, even_bg=Bg.LIGHT_RED)
     table = at.generate_table(row_data)
     assert table == (
-        '\x1b[93m╔═\x1b[39m\x1b[0m\x1b[0m\x1b[93m═══════════════\x1b[39m\x1b[0m\x1b[93m═╤═\x1b[39m\x1b[0m\x1b[0m\x1b[93m═══════════════\x1b[39m\x1b[0m\x1b[93m═╗\x1b[39m\n'
-        '\x1b[93m║\x1b[39m\x1b[42m \x1b[49m\x1b[0m\x1b[42mCol 1\x1b[49m\x1b[0m\x1b[42m          \x1b[49m\x1b[0m\x1b[42m \x1b[93m│\x1b[39m \x1b[49m\x1b[0m\x1b[42mCol 2\x1b[49m\x1b[0m\x1b[42m          \x1b[49m\x1b[0m\x1b[42m \x1b[49m\x1b[93m║\x1b[39m\n'
-        '\x1b[93m╠═\x1b[39m\x1b[0m\x1b[0m\x1b[93m═══════════════\x1b[39m\x1b[0m\x1b[93m═╪═\x1b[39m\x1b[0m\x1b[0m\x1b[93m═══════════════\x1b[39m\x1b[0m\x1b[93m═╣\x1b[39m\n'
-        '\x1b[93m║\x1b[39m\x1b[104m \x1b[49m\x1b[0m\x1b[104mCol 1 Row 1\x1b[49m\x1b[0m\x1b[104m    \x1b[49m\x1b[0m\x1b[104m \x1b[93m│\x1b[39m \x1b[49m\x1b[0m\x1b[104mCol 2 Row 1\x1b[49m\x1b[0m\x1b[104m    \x1b[49m\x1b[0m\x1b[104m \x1b[49m\x1b[93m║\x1b[39m\n'
-        '\x1b[93m║\x1b[39m\x1b[101m \x1b[49m\x1b[0m\x1b[101mCol 1 Row 2\x1b[49m\x1b[0m\x1b[101m    \x1b[49m\x1b[0m\x1b[101m \x1b[93m│\x1b[39m \x1b[49m\x1b[0m\x1b[101mCol 2 Row 2\x1b[49m\x1b[0m\x1b[101m    \x1b[49m\x1b[0m\x1b[101m \x1b[49m\x1b[93m║\x1b[39m\n'
-        '\x1b[93m╚═\x1b[39m\x1b[0m\x1b[0m\x1b[93m═══════════════\x1b[39m\x1b[0m\x1b[93m═╧═\x1b[39m\x1b[0m\x1b[0m\x1b[93m═══════════════\x1b[39m\x1b[0m\x1b[93m═╝\x1b[39m'
+        '\x1b[93m\x1b[107m╔═\x1b[39m\x1b[49m\x1b[0m\x1b[0m\x1b[93m\x1b[107m═══════════════\x1b[39m\x1b[49m\x1b[0m\x1b[93m\x1b[107m═╤═\x1b[39m\x1b[49m\x1b[0m\x1b[0m\x1b[93m\x1b[107m═══════════════\x1b[39m\x1b[49m\x1b[0m\x1b[93m\x1b[107m═╗\x1b[39m\x1b[49m\n'
+        '\x1b[93m\x1b[107m║\x1b[39m\x1b[49m\x1b[42m \x1b[49m\x1b[0m\x1b[42mCol 1\x1b[49m\x1b[0m\x1b[42m          \x1b[49m\x1b[0m\x1b[42m \x1b[49m\x1b[93m\x1b[107m│\x1b[39m\x1b[49m\x1b[42m \x1b[49m\x1b[0m\x1b[42mCol 2\x1b[49m\x1b[0m\x1b[42m          \x1b[49m\x1b[0m\x1b[42m \x1b[49m\x1b[93m\x1b[107m║\x1b[39m\x1b[49m\n'
+        '\x1b[93m\x1b[107m╠═\x1b[39m\x1b[49m\x1b[0m\x1b[0m\x1b[93m\x1b[107m═══════════════\x1b[39m\x1b[49m\x1b[0m\x1b[93m\x1b[107m═╪═\x1b[39m\x1b[49m\x1b[0m\x1b[0m\x1b[93m\x1b[107m═══════════════\x1b[39m\x1b[49m\x1b[0m\x1b[93m\x1b[107m═╣\x1b[39m\x1b[49m\n'
+        '\x1b[93m\x1b[107m║\x1b[39m\x1b[49m\x1b[104m \x1b[49m\x1b[0m\x1b[104mCol 1 Row 1\x1b[49m\x1b[0m\x1b[104m    \x1b[49m\x1b[0m\x1b[104m \x1b[49m\x1b[93m\x1b[107m│\x1b[39m\x1b[49m\x1b[104m \x1b[49m\x1b[0m\x1b[104mCol 2 Row 1\x1b[49m\x1b[0m\x1b[104m    \x1b[49m\x1b[0m\x1b[104m \x1b[49m\x1b[93m\x1b[107m║\x1b[39m\x1b[49m\n'
+        '\x1b[93m\x1b[107m║\x1b[39m\x1b[49m\x1b[101m \x1b[49m\x1b[0m\x1b[101mCol 1 Row 2\x1b[49m\x1b[0m\x1b[101m    \x1b[49m\x1b[0m\x1b[101m \x1b[49m\x1b[93m\x1b[107m│\x1b[39m\x1b[49m\x1b[101m \x1b[49m\x1b[0m\x1b[101mCol 2 Row 2\x1b[49m\x1b[0m\x1b[101m    \x1b[49m\x1b[0m\x1b[101m \x1b[49m\x1b[93m\x1b[107m║\x1b[39m\x1b[49m\n'
+        '\x1b[93m\x1b[107m╚═\x1b[39m\x1b[49m\x1b[0m\x1b[0m\x1b[93m\x1b[107m═══════════════\x1b[39m\x1b[49m\x1b[0m\x1b[93m\x1b[107m═╧═\x1b[39m\x1b[49m\x1b[0m\x1b[0m\x1b[93m\x1b[107m═══════════════\x1b[39m\x1b[49m\x1b[0m\x1b[93m\x1b[107m═╝\x1b[39m\x1b[49m'
     )
 
     # Make sure AlternatingTable respects style_header_text and style_data_text flags.
@@ -709,9 +710,9 @@ def test_alternating_table_creation():
     table = at.generate_table(row_data)
     assert table == (
         '╔═════════════════╤═════════════════╗\n'
-        '║\x1b[42m \x1b[49m\x1b[0m\x1b[42mCol 1\x1b[49m\x1b[0m\x1b[42m          \x1b[49m\x1b[0m\x1b[42m │ \x1b[49m\x1b[0mCol 2\x1b[0m\x1b[42m          \x1b[49m\x1b[0m\x1b[42m \x1b[49m║\n'
+        '║\x1b[42m \x1b[49m\x1b[0m\x1b[42mCol 1\x1b[49m\x1b[0m\x1b[42m          \x1b[49m\x1b[0m\x1b[42m \x1b[49m│\x1b[42m \x1b[49m\x1b[0mCol 2\x1b[0m\x1b[42m          \x1b[49m\x1b[0m\x1b[42m \x1b[49m║\n'
         '╠═════════════════╪═════════════════╣\n'
-        '║\x1b[104m \x1b[49m\x1b[0m\x1b[104mCol 1 Row 1\x1b[49m\x1b[0m\x1b[104m    \x1b[49m\x1b[0m\x1b[104m │ \x1b[49m\x1b[0mCol 2 Row 1\x1b[0m\x1b[104m    \x1b[49m\x1b[0m\x1b[104m \x1b[49m║\n'
-        '║\x1b[101m \x1b[49m\x1b[0m\x1b[101mCol 1 Row 2\x1b[49m\x1b[0m\x1b[101m    \x1b[49m\x1b[0m\x1b[101m │ \x1b[49m\x1b[0mCol 2 Row 2\x1b[0m\x1b[101m    \x1b[49m\x1b[0m\x1b[101m \x1b[49m║\n'
+        '║\x1b[104m \x1b[49m\x1b[0m\x1b[104mCol 1 Row 1\x1b[49m\x1b[0m\x1b[104m    \x1b[49m\x1b[0m\x1b[104m \x1b[49m│\x1b[104m \x1b[49m\x1b[0mCol 2 Row 1\x1b[0m\x1b[104m    \x1b[49m\x1b[0m\x1b[104m \x1b[49m║\n'
+        '║\x1b[101m \x1b[49m\x1b[0m\x1b[101mCol 1 Row 2\x1b[49m\x1b[0m\x1b[101m    \x1b[49m\x1b[0m\x1b[101m \x1b[49m│\x1b[101m \x1b[49m\x1b[0mCol 2 Row 2\x1b[0m\x1b[101m    \x1b[49m\x1b[0m\x1b[101m \x1b[49m║\n'
         '╚═════════════════╧═════════════════╝'
     )