Skip to content

Latest commit

 

History

History
760 lines (494 loc) · 27.5 KB

CHANGELOG.md

File metadata and controls

760 lines (494 loc) · 27.5 KB

Release notes

v0.124

Refactor

  • The undocumented Component.component_id was removed. Instead, use Component.id. Changes:

    • While component_id was unique every time you instantiated Component, The new id is unique every time you render the component (e.g. with Component.render())
    • The new id is available only during render, so e.g. from within get_context_data()

v0.123

Fix

  • Fix edge cases around rendering components whose templates used the {% extends %} template tag (#859)

v0.122

Feat

v0.121

Fix

  • Fix the use of Django template filters (|lower:"etc") with component inputs #855.

v0.120

⚠️ Attention ⚠️ - Please update to v0.121 to fix bugs introduced in v0.119.

Fix

  • Fix the use of translation strings _("bla") as inputs to components #849.

v0.119

⚠️ Attention ⚠️ - This release introduced bugs #849, #855. Please update to v0.121.

Fix

  • Fix compatibility with custom subclasses of Django's Template that need to access origin or other initialization arguments. (#828)

Refactor

  • Compatibility with django-debug-toolbar-template-profiler:

    • Monkeypatching of Django's Template now happens at AppConfig.ready() (#825)
  • Internal parsing of template tags tag was updated. No API change. (#827)

v0.118

Feat

  • Add support for context_processors and RenderContext inside component templates

    Component.render() and Component.render_to_response() now accept an extra kwarg request.

    def my_view(request)
        return MyTable.render_to_response(
            request=request
        )
    • When you pass in request, the component will use RenderContext instead of Context. Thus the context processors will be applied to the context.

    • NOTE: When you pass in both request and context to Component.render(), and context is already an instance of Context, the request kwarg will be ignored.

v0.117

Fix

  • The HTML parser no longer erronously inserts <html><head><body> on some occasions, and no longer tries to close unclosed HTML tags.

Refactor

v0.116

⚠️ Attention ⚠️ - Please update to v0.117 to fix known bugs. See #791 and #789 and #818.

Fix

  • Fix the order of execution of JS scripts:

    • Scripts in Component.Media.js are executed in the order they are defined
    • Scripts in Component.js are executed AFTER Media.js scripts
  • Fix compatibility with AlpineJS

    • Scripts in Component.Media.js are now again inserted as <script> tags
    • By default, Component.Media.js are inserted as synchronous <script> tags, so the AlpineJS components registered in the Media.js scripts will now again run BEFORE the core AlpineJS script.

    AlpineJS can be configured like so:

    Option 1 - AlpineJS loaded in <head> with defer attribute:

    <html>
      <head>
        {% component_css_dependencies %}
        <script defer src="https://unpkg.com/alpinejs"></script>
      </head>
      <body>
        {% component 'my_alpine_component' / %}
        {% component_js_dependencies %}
      </body>
    </html>

    Option 2 - AlpineJS loaded in <body> AFTER {% component_js_depenencies %}:

    <html>
        <head>
            {% component_css_dependencies %}
        </head>
        <body>
            {% component 'my_alpine_component' / %}
            {% component_js_dependencies %}
    
            <script src="https://unpkg.com/alpinejs"></script>
        </body>
    </html>

v0.115

⚠️ Attention ⚠️ - Please update to v0.117 to fix known bugs. See #791 and #789 and #818.

Fix

  • Fix integration with ManifestStaticFilesStorage on Windows by resolving component filepaths (like Component.template_name) to POSIX paths.

v0.114

⚠️ Attention ⚠️ - Please update to v0.117 to fix known bugs. See #791 and #789 and #818.

Fix

  • Prevent rendering Slot tags during fill discovery stage to fix a case when a component inside a slot fill tried to access provided data too early.

v0.113

⚠️ Attention ⚠️ - Please update to v0.117 to fix known bugs. See #791 and #789 and #818.

Fix

  • Ensure consistent order of scripts in Component.Media.js

v0.112

⚠️ Attention ⚠️ - Please update to v0.117 to fix known bugs. See #791 and #789 and #818.

Fix

  • Allow components to accept default fill even if no default slot was encountered during rendering

v0.111

⚠️ Attention ⚠️ - Please update to v0.117 to fix known bugs. See #791 and #789 and #818.

Fix

  • Prevent rendering Component tags during fill discovery stage to fix a case when a component inside the default slot tried to access provided data too early.

🚨📢 v0.110

⚠️ Attention ⚠️ - Please update to v0.117 to fix known bugs. See #791 and #789 and #818.

General

🚨📢 BREAKING CHANGES

  • Installation changes:

    • If your components include JS or CSS, you now must use the middleware and add django-components' URLs to your urlpatterns (See "Adding support for JS and CSS")
  • Component typing signature changed from

    Component[Args, Kwargs, Data, Slots]

    to

    Component[Args, Kwargs, Slots, Data, JsData, CssData]
  • If you rendered a component A with Component.render() and then inserted that into another component B, now you must pass render_dependencies=False to component A:

    prerendered_a = CompA.render(
        args=[...],
        kwargs={...},
        render_dependencies=False,
    )
    
    html = CompB.render(
        kwargs={
            content=prerendered_a,
        },
    )

Feat

  • Intellisense and mypy validation for settings:

    Instead of defining the COMPONENTS settings as a plain dict, you can use ComponentsSettings:

    # settings.py
    from django_components import ComponentsSettings
    
    COMPONENTS = ComponentsSettings(
        autodiscover=True,
        ...
    )
  • Use get_component_dirs() and get_component_files() to get the same list of dirs / files that would be imported by autodiscover(), but without actually importing them.

Refactor

  • For advanced use cases, use can omit the middleware and instead manage component JS and CSS dependencies yourself with render_dependencies

  • The ComponentRegistry settings RegistrySettings were lowercased to align with the global settings:

    • RegistrySettings.CONTEXT_BEHAVIOR -> RegistrySettings.context_behavior
    • RegistrySettings.TAG_FORMATTER -> RegistrySettings.tag_formatter

    The old uppercase settings CONTEXT_BEHAVIOR and TAG_FORMATTER are deprecated and will be removed in v1.

  • The setting reload_on_template_change was renamed to reload_on_file_change. And now it properly triggers server reload when any file in the component dirs change. The old name reload_on_template_change is deprecated and will be removed in v1.

  • The setting forbidden_static_files was renamed to static_files_forbidden to align with static_files_allowed The old name forbidden_static_files is deprecated and will be removed in v1.

Tags

🚨📢 BREAKING CHANGES

  • {% component_dependencies %} tag was removed. Instead, use {% component_js_dependencies %} and {% component_css_dependencies %}

    • The combined tag was removed to encourage the best practice of putting JS scripts at the end of <body>, and CSS styles inside <head>.

      On the other hand, co-locating JS script and CSS styles can lead to a flash of unstyled content, as either JS scripts will block the rendering, or CSS will load too late.

  • The undocumented keyword arg preload of {% component_js_dependencies %} and {% component_css_dependencies %} tags was removed. This will be replaced with HTML fragment support.

Fix

  • Allow using forward slash (/) when defining custom TagFormatter, e.g. {% MyComp %}..{% /MyComp %}.

Refactor

  • {% component_dependencies %} tags are now OPTIONAL - If your components use JS and CSS, but you don't use {% component_dependencies %} tags, the JS and CSS will now be, by default, inserted at the end of <body> and at the end of <head> respectively.

Slots

Feat

  • Fills can now be defined within loops ({% for %}) or other tags (like {% with %}), or even other templates using {% include %}.

    Following is now possible

    {% component "table" %}
      {% for slot_name in slots %}
        {% fill name=slot_name %}
        {% endfill %}
      {% endfor %}
    {% endcomponent %}
  • If you need to access the data or the default content of a default fill, you can set the name kwarg to "default".

    Previously, a default fill would be defined simply by omitting the {% fill %} tags:

    {% component "child" %}
      Hello world
    {% endcomponent %}

    But in that case you could not access the slot data or the default content, like it's possible for named fills:

    {% component "child" %}
      {% fill name="header" data="data" %}
        Hello {{ data.user.name }}
      {% endfill %}
    {% endcomponent %}

    Now, you can specify default tag by using name="default":

    {% component "child" %}
      {% fill name="default" data="data" %}
        Hello {{ data.user.name }}
      {% endfill %}
    {% endcomponent %}
  • When inside get_context_data() or other component methods, the default fill can now be accessed as Component.input.slots["default"], e.g.:

    class MyTable(Component):
        def get_context_data(self, *args, **kwargs):
            default_slot = self.input.slots["default"]
            ...
  • You can now dynamically pass all slots to a child component. This is similar to passing all slots in Vue:

    class MyTable(Component):
        def get_context_data(self, *args, **kwargs):
            return {
                "slots": self.input.slots,
            }
    
        template: """
          <div>
            {% component "child" %}
              {% for slot_name in slots %}
                {% fill name=slot_name data="data" %}
                  {% slot name=slot_name ...data / %}
                {% endfill %}
              {% endfor %}
            {% endcomponent %}
          </div>
        """

Fix

  • Slots defined with {% fill %} tags are now properly accessible via self.input.slots in get_context_data()

  • Do not raise error if multiple slots with same name are flagged as default

  • Slots can now be defined within loops ({% for %}) or other tags (like {% with %}), or even other templates using {% include %}.

    Previously, following would cause the kwarg name to be an empty string:

    {% for slot_name in slots %}
      {% slot name=slot_name %}
    {% endfor %}

Refactor

  • When you define multiple slots with the same name inside a template, you now have to set the default and required flags individually.

    <div class="calendar-component">
        <div class="header">
            {% slot "image" default required %}Image here{% endslot %}
        </div>
        <div class="body">
            {% slot "image" default required %}Image here{% endslot %}
        </div>
    </div>

    This means you can also have multiple slots with the same name but different conditions.

    E.g. in this example, we have a component that renders a user avatar

    • a small circular image with a profile picture of name initials.

    If the component is given image_src or name_initials variables, the image slot is optional. But if neither of those are provided, you MUST fill the image slot.

    <div class="avatar">
        {% if image_src %}
            {% slot "image" default %}
                <img src="{{ image_src }}" />
            {% endslot %}
        {% elif name_initials %}
            {% slot "image" default required %}
                <div style="
                    border-radius: 25px;
                    width: 50px;
                    height: 50px;
                    background: blue;
                ">
                    {{ name_initials }}
                </div>
            {% endslot %}
        {% else %}
            {% slot "image" default required / %}
        {% endif %}
    </div>
  • The slot fills that were passed to a component and which can be accessed as Component.input.slots can now be passed through the Django template, e.g. as inputs to other tags.

    Internally, django-components handles slot fills as functions.

    Previously, if you tried to pass a slot fill within a template, Django would try to call it as a function.

    Now, something like this is possible:

    class MyTable(Component):
        def get_context_data(self, *args, **kwargs):
            return {
                "child_slot": self.input.slots["child_slot"],
            }
    
        template: """
          <div>
            {% component "child" content=child_slot / %}
          </div>
        """

    NOTE: Using {% slot %} and {% fill %} tags is still the preferred method, but the approach above may be necessary in some complex or edge cases.

  • The is_filled variable (and the {{ component_vars.is_filled }} context variable) now returns False when you try to access a slot name which has not been defined:

    Before:

    {{ component_vars.is_filled.header }} -> True
    {{ component_vars.is_filled.footer }} -> False
    {{ component_vars.is_filled.nonexist }} -> "" (empty string)

    After:

    {{ component_vars.is_filled.header }} -> True
    {{ component_vars.is_filled.footer }} -> False
    {{ component_vars.is_filled.nonexist }} -> False
  • Components no longer raise an error if there are extra slot fills

  • Components will raise error when a slot is doubly-filled.

    E.g. if we have a component with a default slot:

    {% slot name="content" default / %}

    Now there is two ways how we can target this slot: Either using name="default" or name="content".

    In case you specify BOTH, the component will raise an error:

    {% component "child" %}
      {% fill slot="default" %}
        Hello from default slot
      {% endfill %}
      {% fill slot="content" data="data" %}
        Hello from content slot
      {% endfill %}
    {% endcomponent %}

🚨📢 v0.100

BREAKING CHANGES

Feat

  • Beside the top-level /components directory, you can now define also app-level components dirs, e.g. [app]/components (See COMPONENTS.app_dirs).

Refactor

  • When you call as_view() on a component instance, that instance will be passed to View.as_view()

v0.97

Fix

  • Fixed template caching. You can now also manually create cached templates with cached_template()

Refactor

  • The previously undocumented get_template was made private.

  • In it's place, there's a new get_template, which supersedes get_template_string (will be removed in v1). The new get_template is the same as get_template_string, except it allows to return either a string or a Template instance.

  • You now must use only one of template, get_template, template_name, or get_template_name.

v0.96

Feat

  • Run-time type validation for Python 3.11+ - If the Component class is typed, e.g. Component[Args, Kwargs, ...], the args, kwargs, slots, and data are validated against the given types. (See Runtime input validation with types)

  • Render hooks - Set on_render_before and on_render_after methods on Component to intercept or modify the template or context before rendering, or the rendered result afterwards. (See Component hooks)

  • component_vars.is_filled context variable can be accessed from within on_render_before and on_render_after hooks as self.is_filled.my_slot

0.95

Feat

  • Added support for dynamic components, where the component name is passed as a variable. (See Dynamic components)

Refactor

  • Changed Component.input to raise RuntimeError if accessed outside of render context. Previously it returned None if unset.

v0.94

Feat

v0.93

Feat

  • Spread operator ...dict inside template tags. (See Spread operator)

  • Use template tags inside string literals in component inputs. (See Use template tags inside component inputs)

  • Dynamic slots, fills and provides - The name argument for these can now be a variable, a template expression, or via spread operator

  • Component library authors can now configure CONTEXT_BEHAVIOR and TAG_FORMATTER settings independently from user settings.

🚨📢 v0.92

BREAKING CHANGES

  • Component class is no longer a subclass of View. To configure the View class, set the Component.View nested class. HTTP methods like get or post can still be defined directly on Component class, and Component.as_view() internally calls Component.View.as_view(). (See Modifying the View class)

Feat

  • The inputs (args, kwargs, slots, context, ...) that you pass to Component.render() can be accessed from within get_context_data, get_template and get_template_name via self.input. (See Accessing data passed to the component)

  • Typing: Component class supports generics that specify types for Component.render (See Adding type hints with Generics)

v0.90

Feat

  • All tags (component, slot, fill, ...) now support "self-closing" or "inline" form, where you can omit the closing tag:

    {# Before #}
    {% component "button" %}{% endcomponent %}
    {# After #}
    {% component "button" / %}
  • All tags now support the "dictionary key" or "aggregate" syntax (kwarg:key=val):

    {% component "button" attrs:class="hidden" %}
  • You can change how the components are written in the template with TagFormatter.

    The default is django_components.component_formatter:

    {% component "button" href="..." disabled %}
        Click me!
    {% endcomponent %}

    While django_components.shorthand_component_formatter allows you to write components like so:

    {% button href="..." disabled %}
        Click me!
    {% endbutton %}

🚨📢 v0.85

BREAKING CHANGES

  • Autodiscovery module resolution changed. Following undocumented behavior was removed:

    • Previously, autodiscovery also imported any [app]/components.py files, and used SETTINGS_MODULE to search for component dirs.

      To migrate from:

      • [app]/components.py - Define each module in COMPONENTS.libraries setting, or import each module inside the AppConfig.ready() hook in respective apps.py files.

      • SETTINGS_MODULE - Define component dirs using STATICFILES_DIRS

    • Previously, autodiscovery handled relative files in STATICFILES_DIRS. To align with Django, STATICFILES_DIRS now must be full paths (Django docs).

🚨📢 v0.81

BREAKING CHANGES

  • The order of arguments to render_to_response has changed, to align with the (now public) render method of Component class.

Feat

  • Component.render() is public and documented

  • Slots passed render_to_response and render can now be rendered also as functions.

v0.80

Feat

  • Vue-like provide/inject with the {% provide %} tag and inject() method.

🚨📢 v0.79

BREAKING CHANGES

  • Default value for the COMPONENTS.context_behavior setting was changes from "isolated" to "django". If you did not set this value explicitly before, this may be a breaking change. See the rationale for change here.

🚨📢 v0.77

BREAKING

  • The syntax for accessing default slot content has changed from

    {% fill "my_slot" as "alias" %}
        {{ alias.default }}
    {% endfill %}
    

    to

    {% fill "my_slot" default="alias" %}
        {{ alias }}
    {% endfill %}

v0.74

Feat

  • {% html_attrs %} tag for formatting data as HTML attributes

  • prefix:key=val construct for passing dicts to components

🚨📢 v0.70

BREAKING CHANGES

  • {% if_filled "my_slot" %} tags were replaced with {{ component_vars.is_filled.my_slot }} variables.

  • Simplified settings - slot_context_behavior and context_behavior were merged. See the documentation for more details.

v0.67

Refactor

  • Changed the default way how context variables are resolved in slots. See the documentation for more details.

🚨📢 v0.50

BREAKING CHANGES

  • {% component_block %} is now {% component %}, and {% component %} blocks need an ending {% endcomponent %} tag.

    The new python manage.py upgradecomponent command can be used to upgrade a directory (use --path argument to point to each dir) of templates that use components to the new syntax automatically.

    This change is done to simplify the API in anticipation of a 1.0 release of django_components. After 1.0 we intend to be stricter with big changes like this in point releases.

v0.34

Feat

  • Components as views, which allows you to handle requests and render responses from within a component. See the documentation for more details.

v0.28

Feat

  • 'implicit' slot filling and the default option for slot tags.

v0.27

Feat

  • A second installable app django_components.safer_staticfiles. It provides the same behavior as django.contrib.staticfiles but with extra security guarantees (more info below in Security Notes).

🚨📢 v0.26

BREAKING CHANGES

  • Changed the syntax for {% slot %} tags. From now on, we separate defining a slot ({% slot %}) from filling a slot with content ({% fill %}). This means you will likely need to change a lot of slot tags to fill.

    We understand this is annoying, but it's the only way we can get support for nested slots that fill in other slots, which is a very nice feature to have access to. Hoping that this will feel worth it!

v0.22

Feat

  • All files inside components subdirectores are autoimported to simplify setup.

    An existing project might start to get AlreadyRegistered errors because of this. To solve this, either remove your custom loading of components, or set "autodiscover": False in settings.COMPONENTS.

v0.17

BREAKING CHANGES

  • Renamed Component.context and Component.template to get_context_data and get_template_name. The old methods still work, but emit a deprecation warning.

    This change was done to sync naming with Django's class based views, and make using django-components more familiar to Django users. Component.context and Component.template will be removed when version 1.0 is released.