diff --git a/package-lock.json b/package-lock.json index d5d2bed807..782340f1e9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -28,7 +28,7 @@ "@emotion/styled": "^11.3.0", "@fortawesome/fontawesome-free": "^6.4.2", "@joeattardi/emoji-button": "^4.6.4", - "@open-inwoner/design-tokens": "^0.0.5-alpha.5", + "@open-inwoner/design-tokens": "^0.0.3-alpha.0", "@tarekraafat/autocomplete.js": "^10.2.6", "bem.js": "^1.0.10", "emojibase-data": "^7.0.1", diff --git a/requirements/base.txt b/requirements/base.txt index 1e9efa54f8..a22f933406 100644 --- a/requirements/base.txt +++ b/requirements/base.txt @@ -225,7 +225,7 @@ django-localflavor==3.1 # via -r requirements/base.in django-log-outgoing-requests==0.6.1 # via -r requirements/base.in -django-open-forms-client==0.3.0 +django-open-forms-client==0.4.0 # via -r requirements/base.in django-ordered-model==3.7.4 # via diff --git a/requirements/ci.txt b/requirements/ci.txt index 23dc3d7761..8fc1de45f8 100644 --- a/requirements/ci.txt +++ b/requirements/ci.txt @@ -379,7 +379,7 @@ django-log-outgoing-requests==0.6.1 # via # -c requirements/base.txt # -r requirements/base.txt -django-open-forms-client==0.3.0 +django-open-forms-client==0.4.0 # via # -c requirements/base.txt # -r requirements/base.txt diff --git a/requirements/dev.txt b/requirements/dev.txt index e2427937b9..d462052bcc 100644 --- a/requirements/dev.txt +++ b/requirements/dev.txt @@ -423,7 +423,7 @@ django-log-outgoing-requests==0.6.1 # via # -c requirements/ci.txt # -r requirements/ci.txt -django-open-forms-client==0.3.0 +django-open-forms-client==0.4.0 # via # -c requirements/ci.txt # -r requirements/ci.txt diff --git a/src/open_inwoner/components/templates/components/File/File.html b/src/open_inwoner/components/templates/components/File/File.html index 7a03c59646..8e8e4c780c 100644 --- a/src/open_inwoner/components/templates/components/File/File.html +++ b/src/open_inwoner/components/templates/components/File/File.html @@ -18,11 +18,11 @@ {{ name }} {% if extension and size %} - ({{extension}}, {{size|readable_size}}) + ({{ extension }}, {{ size|readable_size }}) {% elif extension %} - ({{extension}}) + ({{ extension }}) {% elif size %} - ({{size|readable_size}}) + ({{ size|readable_size }}) {% endif %} {{ created|date:'j F Y' }} @@ -50,7 +50,7 @@ {% enddropdown %} {% elif show_download %} {% trans "Download" as download %} - {% link href=url text=download primary=True download=True hide_text=True extra_classes="file__download" icon="download" icon_outlined=True icon_position="before" %} + {% link href=url text=download|add:" "|add:name primary=True download=True hide_text=True extra_classes="file__download" icon="download" icon_outlined=True icon_position="before" %} {% endif %} {% if description %}

{{ description }}

{% endif %} diff --git a/src/open_inwoner/components/templates/components/Header/Header.html b/src/open_inwoner/components/templates/components/Header/Header.html index 45f8d477db..f622bee1e3 100644 --- a/src/open_inwoner/components/templates/components/Header/Header.html +++ b/src/open_inwoner/components/templates/components/Header/Header.html @@ -32,7 +32,7 @@ {# end of mobile header-menu with logo #} -
+
{% if cms_apps.products and request.user.is_authenticated or not config.hide_search_from_anonymous_users %} {% endif %} -
diff --git a/src/open_inwoner/templates/pages/cases/statuses.html b/src/open_inwoner/templates/pages/cases/statuses.html index e087e8ad95..98d3a1b4d6 100644 --- a/src/open_inwoner/templates/pages/cases/statuses.html +++ b/src/open_inwoner/templates/pages/cases/statuses.html @@ -13,6 +13,7 @@

{% trans 'Status' %}

{% firstof case.id|slugify as id %} {% firstof 'content-'|add:id|add:'-id' as content_id %} {% icon icon="circle" extra_classes="status-step" %} + {% trans "Niet voltooid" %}

{# Interactive accordion when case is ongoing, and active status is shown as expanded #} @@ -56,6 +57,7 @@

{% firstof case.id|slugify as id %} {% firstof 'completed-'|add:id|add:'-id' as completed_id %} {% icon icon="task_alt" %} + {% trans "Voltooid" %}

@@ -76,6 +78,7 @@

{% if case.second_status_preview %}
  • {% icon icon="circle_outlined" outlined=True %} + {% trans "Toekomstige status" %}
    {{ case.second_status_preview.omschrijving }}
    @@ -86,6 +89,7 @@

    {% if case.end_statustype_data %}
  • {% icon icon="circle_outlined" outlined=True %} + {% trans "Toekomstige voltooide status" %}
    {{ case.end_statustype_data.label }}
    diff --git a/src/open_inwoner/utils/ckeditor.py b/src/open_inwoner/utils/ckeditor.py index e802db6aec..752376724b 100644 --- a/src/open_inwoner/utils/ckeditor.py +++ b/src/open_inwoner/utils/ckeditor.py @@ -16,11 +16,29 @@ ("p", "utrecht-paragraph"), ("a", "link link--secondary"), ("table", "table table--content"), + ("thead", "table__heading"), + ("tbody", "table__body"), + ("tr", "table__row"), ("th", "table__header"), ("td", "table__item"), ] +def convert_first_row_to_th(html_tables): + """ + Converts the first row of all tables from td to th. + """ + for table in html_tables.find_all("table"): + first_row = table.find("tr") + if first_row: + for cell in first_row.find_all("td"): + th = html_tables.new_tag("th") + th.string = cell.string + th.attrs = cell.attrs + th["class"] = "table__header" + cell.replace_with(th) + + def get_rendered_content(content: str) -> str: """ Takes object's content as an input and returns the rendered one. @@ -50,6 +68,8 @@ def get_product_rendered_content(product): html = md.convert(content) soup = BeautifulSoup(html, "html.parser") + convert_first_row_to_th(soup) + for tag, class_name in CLASS_ADDERS: for element in soup.find_all(tag): if element.attrs.get("class") and "cta-button" in element.attrs["class"]: diff --git a/src/open_inwoner/utils/tests/test_migrations.py b/src/open_inwoner/utils/tests/test_migrations.py index b4b61c8788..3cf38bbcb7 100644 --- a/src/open_inwoner/utils/tests/test_migrations.py +++ b/src/open_inwoner/utils/tests/test_migrations.py @@ -1,21 +1,34 @@ +import contextlib + from django.db import connection from django.db.migrations.executor import MigrationExecutor from django.test import TestCase -class TestMigrations(TestCase): +class TestMigrationsBase(TestCase): + app = None + migrate_from = None + migrate_to = None + extra_migrate_from: list[tuple[str, str]] = None """ Test the effect of applying a migration Adapted from https://github.com/open-formulieren/open-forms/blob/e64c9368264d3f662542866e2d7d5ba15e0f265c/src/openforms/utils/tests/test_migrations.py """ - app = None - migrate_from = None - migrate_to = None + @contextlib.contextmanager + def _immediate_constraints(self): + try: + # Force immediate constraint checks to stop error 'cannot ALTER TABLE "<..>" + # because it has pending trigger events' in the tests + with connection.cursor() as cursor: + cursor.execute("SET CONSTRAINTS ALL IMMEDIATE") - extra_migrate_from: list[tuple[str, str]] = None + yield + finally: + with connection.cursor() as cursor: + cursor.execute("SET CONSTRAINTS ALL DEFERRED") - def setUp(self): + def _revert_to_migrate_from(self): assert self.migrate_from and self.migrate_to and self.app, ( "TestCase '%s' must define migrate_from, migrate_to and app properties" % type(self).__name__ @@ -28,27 +41,67 @@ def setUp(self): self.migrate_to = [(self.app, self.migrate_to)] executor = MigrationExecutor(connection) - old_apps = executor.loader.project_state(self.migrate_from).apps - - # Force immediate constraint checks to stop error 'cannot ALTER TABLE "<..>" because it has pending trigger events' in the tests - with connection.cursor() as cursor: - cursor.execute("SET CONSTRAINTS ALL IMMEDIATE") - - # Reverse to the original migration - executor.migrate(self.migrate_from) + self.old_apps = executor.loader.project_state(self.migrate_from).apps - self.setUpBeforeMigration(old_apps) + with self._immediate_constraints(): + # Reverse to the original migration + executor.migrate(self.migrate_from) + def _apply_migration_to(self): # Run the migration to test executor = MigrationExecutor(connection) executor.loader.build_graph() # reload. - executor.migrate(self.migrate_to) - - # Restore constraint checks - with connection.cursor() as cursor: - cursor.execute("SET CONSTRAINTS ALL DEFERRED") - + with self._immediate_constraints(): + executor.migrate(self.migrate_to) self.apps = executor.loader.project_state(self.migrate_to).apps def setUpBeforeMigration(self, apps): pass + + +class TestSuccessfulMigrations(TestMigrationsBase): + """Test a successful migration. + + Set the class attributes `app`, `migration_from`, and `migrate_to`. You + can specify your pre-migration state in `setUpBeforeMigration()`, and + consequently assert against the resulting state (in your test, do + not import models directly but use: + + ``` + MyModel = self.apps.get_model(self.app, "MyModel") + ``` + """ + + def setUp(self): + self._revert_to_migrate_from() + self.setUpBeforeMigration(self.old_apps) + self._apply_migration_to() + + +class TestFailingMigrations(TestMigrationsBase): + """Test a migration which is expected to fail. + + Set the class attributes `app`, `migration_from`, and `migrate_to`. You + can specify your pre-migration state in `setUpBeforeMigration()`, though + you should not import models directly, but rather use: + + ``` + MyModel = self.apps.get_model(self.app, "MyModel") + ``` + + In your test, you can attempt to migration using `self.attempt_migration()` + and assert against the expected exception: + ``` + def test_migration_should_fail(self): + # Setup failure conditions + self.assertRaises(SomeError): + self.attempt_migration() + ``` + """ + + def setUp(self): + self._revert_to_migrate_from() + self.setUpBeforeMigration(self.old_apps) + + def attempt_migration(self): + self._apply_migration_to()