Repository: mathesar-foundation/mathesar

Issue:
Allow the user to hide grouping separators for Number and Money
## Current behavior

- Look at this table of numbers. The right-most column is storing years as in integer, which I think will be common. But they are displayed with grouping separators. Ugh. That looks terrible!

    ![image](https://user-images.githubusercontent.com/42411/165846061-9648a4bd-2aac-4074-851b-882c77aa8bea.png)

- There is currently no way for the user to disable the display of grouping separators.

## Desired behavior

- The user can use display options to enable/disable grouping separators.

## Proposal

### Schema

- Use the `use_grouping` property as [spec'ed in this comment](https://github.com/centerofci/mathesar/discussions/1243#discussioncomment-2519195).

### UX

- Add a new form field labeled "Use Grouping Separators", with a dropdown input having options "Yes", "No", and "Auto". Display this new field before the already-existing "Format" field.

    For integers, the default is "No".

    For non-integers, and Money, the default is "Yes".

- Modify the "Format" form field to behave as follows:

    - If we have an **integer with grouping separators**, then present the following options:

        | option    | value |
        | --------- | ------- |
        | Use browser locale | `null` |
        | 1,234,567 | `'english'` |
        | 1.234.567 | `'german'`  |
        | 1 234 567 | `'french'`  |
        | 12,34,567 | `'hindi'`   |
        | 1'234'567 | `'swiss'`   |

    - If we have an **integer without grouping separators**, then don't show the "format" option, because it's irrelevant. Keep value as `null`.

    - If we have a **non-integer with grouping separators**, then present the following options:

        | option    | value |
        | --------- | ------- |
        | Use browser locale | `null` |
        | 1,234,567.89 | `'english'` |
        | 1.234.567,89 | `'german'`  |
        | 1 234 567,89 | `'french'`  |
        | 12,34,567.89 | `'hindi'`   |
        | 1'234'567.89 | `'swiss'`   |

    - If we have a **non-integer without grouping separators**, then present the following options:

        | option    | value |
        | --------- | ------- |
        | Use browser locale | `null` |
        | 1234.56 | `'english'` |
        | 1234,56 | `'german'`  |

- When changing the "Use Grouping Separators" field from "No" to "Yes", we set a more specific value for the "Format" field, using the browser locale to resolve ambiguity. For example, if the user has configured `format` to `'german'` (because they just wanted a comma decimal separator), but their locale is `fr-FR`, then we change `'german'` to `'french'`, which also has a comma decimal separator but is more likely to match their desired grouping separator. We can use the browser locale to determine the format option by formatting a number and inspecting it. As a further edge case, if the user has configured `format` to `'german'`, but their locale is `en-US` (which is _not_ a more specific form of `'german'`), then we'd leave the value untouched.




Discussion:
This sounds fine to me, let's do it.

Resolving patch:
diff --git a/mathesar/api/display_options.py b/mathesar/api/display_options.py
--- a/mathesar/api/display_options.py
+++ b/mathesar/api/display_options.py
@@ -51,6 +51,11 @@ def _money_display_options_schema():
                     "type": "string",
                     "enum": ['dropdown', 'checkbox']
                 },
+                {
+                    "name": "use_grouping",
+                    "type": "string",
+                    "enum": ['true', 'false', 'auto']
+                },
                 {
                     "name": "locale",
                     "type": "string"
diff --git a/mathesar/api/serializers/shared_serializers.py b/mathesar/api/serializers/shared_serializers.py
--- a/mathesar/api/serializers/shared_serializers.py
+++ b/mathesar/api/serializers/shared_serializers.py
@@ -143,6 +143,18 @@ class AbstractNumberDisplayOptionSerializer(BaseDisplayOptionsSerializer):
         choices=['english', 'german', 'french', 'hindi', 'swiss']
     )
 
+    use_grouping = serializers.ChoiceField(required=False, choices=['true', 'false', 'auto'], default='auto')
+    """
+    The choices here correspond to the options available for the `useGrouping`
+    property within the [Intl API][1]. True and False are encoded as strings
+    instead of booleans to maintain consistency with the Intl API and to keep
+    the type consistent. We did considering using an optional boolean but
+    decided a string would be better, especially if we want to support other
+    options eventually, like "min2".
+
+    [1]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/NumberFormat/NumberFormat
+    """
+
 
 class NumberDisplayOptionSerializer(AbstractNumberDisplayOptionSerializer):
     show_as_percentage = serializers.BooleanField(default=False)


Test patch:
diff --git a/mathesar/tests/api/test_column_api_display_options.py b/mathesar/tests/api/test_column_api_display_options.py
--- a/mathesar/tests/api/test_column_api_display_options.py
+++ b/mathesar/tests/api/test_column_api_display_options.py
@@ -28,7 +28,7 @@ def column_test_table_with_service_layer_options(patent_schema):
     column_data_list = [
         {},
         {'display_options': {'input': "dropdown", "custom_labels": {"TRUE": "yes", "FALSE": "no"}}, 'show_fk_preview': True},
-        {'display_options': {'show_as_percentage': True, 'number_format': "english", 'show_fk_preview': True}},
+        {'display_options': {'show_as_percentage': True, 'number_format': "english", 'show_fk_preview': True, "use_grouping": 'auto'}},
         {'display_options': None},
         {},
         {
@@ -91,18 +91,23 @@ def column_test_table_with_service_layer_options(patent_schema):
     ),
     (
         PostgresType.MONEY,
-        {'number_format': "english", 'currency_symbol': '$', 'currency_symbol_location': 'after-minus'},
-        {'currency_symbol': '$', 'currency_symbol_location': 'after-minus', 'number_format': "english", 'show_fk_preview': True}
+        {'number_format': "english", 'currency_symbol': '$', 'currency_symbol_location': 'after-minus', 'use_grouping': 'true'},
+        {'currency_symbol': '$', 'currency_symbol_location': 'after-minus', 'number_format': "english", 'use_grouping': 'true', 'show_fk_preview': True}
     ),
     (
         PostgresType.NUMERIC,
-        {"show_as_percentage": True, 'number_format': None},
-        {"show_as_percentage": True, 'number_format': None, 'show_fk_preview': True}
+        {},
+        {"show_as_percentage": False, 'number_format': None, 'use_grouping': 'auto', 'show_fk_preview': True}
+    ),
+    (
+        PostgresType.NUMERIC,
+        {"show_as_percentage": True, 'number_format': None, 'use_grouping': 'false'},
+        {"show_as_percentage": True, 'number_format': None, 'use_grouping': 'false', 'show_fk_preview': True}
     ),
     (
         PostgresType.NUMERIC,
-        {"show_as_percentage": True, 'number_format': "english"},
-        {"show_as_percentage": True, 'number_format': "english", 'show_fk_preview': True}
+        {"show_as_percentage": True, 'number_format': "english", 'use_grouping': 'auto'},
+        {"show_as_percentage": True, 'number_format': "english", 'use_grouping': 'auto', 'show_fk_preview': True}
     ),
     (
         PostgresType.TIMESTAMP_WITH_TIME_ZONE,