Skip to content

Conversation

@jevinjojo
Copy link

@jevinjojo jevinjojo commented Nov 24, 2025

Issue

auto-expand.mp4

Description

  • Add I18nAutoExpandingTextarea widget with height adjustment
  • Self contained inline javascript for component
  • Apply widget to frontpage_text field in event settings

Summary by Sourcery

Introduce an auto-expanding internationalized textarea widget and apply it to the event frontpage text setting.

New Features:

  • Add an I18nAutoExpandingTextarea widget that automatically adjusts its height based on content for i18n text fields.

Enhancements:

  • Use the auto-expanding textarea widget for the event frontpage text setting to improve editing long descriptions.
  • Ensure templates load the static files tag needed for the new widget behavior.

@sourcery-ai
Copy link
Contributor

sourcery-ai bot commented Nov 24, 2025

Reviewer's Guide

Introduces an auto-expanding internationalized textarea widget and applies it to the event frontpage text setting, including inline JavaScript to dynamically adjust the textarea height for static and dynamically added i18n fields.

Sequence diagram for auto-expanding frontpage text textarea

sequenceDiagram
  actor "Organizer" as Organizer
  participant "Browser" as Browser
  participant "DjangoView" as DjangoView
  participant "I18nAutoExpandingTextarea" as Widget
  participant "JS_AutoExpand" as JS

  "Organizer" ->> "Browser": "Open event settings page"
  "Browser" ->> "DjangoView": "GET /event/settings"
  "DjangoView" ->> "Widget": "Render field 'frontpage_text' with widget 'I18nAutoExpandingTextarea'"
  "Widget" -->> "DjangoView": "HTML <textarea data-auto-expand='true'> + inline <script>"
  "DjangoView" -->> "Browser": "Settings page HTML with auto-expanding textarea"

  "Browser" ->> "JS": "Trigger 'DOMContentLoaded' event"
  "JS" ->> "Browser": "Select 'textarea[data-auto-expand="true"]' and initialize autoExpandTextarea"
  "JS" ->> "Browser": "Attach 'input' and 'paste' listeners to textarea"

  "Organizer" ->> "Browser": "Type or paste content into frontpage text textarea"
  "Browser" ->> "JS": "Fire 'input' or 'paste' event"
  "JS" ->> "Browser": "adjustHeight(): set height between 80px and 300px, toggle vertical overflow"

  "Browser" ->> "JS": "MutationObserver detects dynamically added i18n textareas (language tabs)"
  "JS" ->> "Browser": "Initialize autoExpandTextarea on new i18n textareas with data-auto-expand='true'"
Loading

Class diagram for the new I18nAutoExpandingTextarea widget

classDiagram
  class I18nTextarea {
  }

  class I18nAutoExpandingTextarea {
    + __init__(attrs=None, **kwargs)
    + render(name, value, attrs=None, renderer=None)
    - auto-expanding behavior configured via HTML attributes and inline JavaScript
  }

  I18nTextarea <|-- I18nAutoExpandingTextarea
Loading

File-Level Changes

Change Details Files
Add an auto-expanding i18n textarea widget that adjusts height based on content.
  • Create I18nAutoExpandingTextarea subclass extending the existing I18nTextarea widget
  • Initialize widget with default CSS classes, data-auto-expand attribute, and inline min/max height and transition styles
  • Enhance attrs merging so caller-provided classes are appended to default classes
app/eventyay/base/forms/__init__.py
Implement inline JavaScript behavior to auto-resize textareas and support dynamically added i18n tabs.
  • Override render to append a self-contained <script> block next to the rendered textarea HTML
  • Within the script, define autoExpandTextarea that sets height between 80px and 300px and toggles overflow based on scroll height
  • Attach input and paste listeners to trigger resizing, and initialize all matching textareas on DOMContentLoaded
  • Use a MutationObserver on document.body to auto-initialize textareas added later (e.g., when switching or adding i18n tabs)
app/eventyay/base/forms/__init__.py
Apply the new auto-expanding widget to the event frontpage text setting and adjust template tags.
  • Replace the frontpage_text form configuration to use I18nAutoExpandingTextarea instead of I18nTextarea
  • Ensure the event settings template loads the static template tag library required by other assets/scripts
app/eventyay/base/configurations/default_setting.py
app/eventyay/eventyay_common/templates/eventyay_common/event/settings.html

Assessment against linked issues

Issue Objective Addressed Explanation
#1336 Make the 'Frontpage text' textarea in the Event Settings page automatically expand its height based on the content as the user types, instead of using a fixed-height textarea with an internal scrollbar.

Possibly linked issues


Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Copy link
Contributor

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

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

Hey there - I've reviewed your changes - here's some feedback:

  • The auto-expanding behavior is injected via an inline <script> on every widget render; consider moving this JS into a shared static file or a global initialization hook to avoid repeated scripts and improve maintainability.
  • The widget hardcodes presentation details (min/max height and transitions) directly in the style attribute; consider moving these into CSS classes so they can be more easily themed or adjusted without changing Python code.
  • In event/settings.html, {% load static %} is now present twice; you can remove the duplicated tag to keep the template tidy.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The auto-expanding behavior is injected via an inline <script> on every widget render; consider moving this JS into a shared static file or a global initialization hook to avoid repeated scripts and improve maintainability.
- The widget hardcodes presentation details (min/max height and transitions) directly in the style attribute; consider moving these into CSS classes so they can be more easily themed or adjusted without changing Python code.
- In event/settings.html, {% load static %} is now present twice; you can remove the duplicated tag to keep the template tidy.

## Individual Comments

### Comment 1
<location> `app/eventyay/base/forms/__init__.py:188-197` </location>
<code_context>
+    def render(self, name, value, attrs=None, renderer=None):
</code_context>

<issue_to_address>
**issue (performance):** Embedding a full <script> block in each widget render may cause duplication and performance overhead.

Since `render` returns `html + js_code`, every widget instance adds another identical `<script>` tag to the DOM. With multiple fields this leads to repeated DOMContentLoaded handlers / observers and inflated HTML. Prefer having the widget only set `data-auto-expand` (and classes) and loading the auto-expand logic once via shared JS or a base template. If you must inline here, add a simple guard (e.g., a global flag on `window`) so the logic runs only once even if the script tag is duplicated.
</issue_to_address>

### Comment 2
<location> `app/eventyay/base/forms/__init__.py:185` </location>
<code_context>
    def __init__(self, attrs=None, **kwargs):
        default_attrs = {
            'class': 'form-control auto-expanding-textarea',
            'data-auto-expand': 'true',
            'style': 'min-height: 80px; max-height: 300px; overflow-y: hidden; resize: vertical; transition: height 0.2s ease-in-out;'
        }
        if attrs:
            if 'class' in attrs:
                default_attrs['class'] = default_attrs['class'] + ' ' + attrs['class']
            default_attrs.update(attrs)
        super().__init__(attrs=default_attrs, **kwargs)

</code_context>

<issue_to_address>
**suggestion (code-quality):** Merge dictionary updates via the union operator ([`dict-assign-update-to-union`](https://docs.sourcery.ai/Reference/Default-Rules/suggestions/dict-assign-update-to-union/))

```suggestion
            default_attrs |= attrs
```
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Copilot finished reviewing on behalf of mariobehling November 24, 2025 14:22
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR adds an auto-expanding textarea widget for internationalized text fields in event settings, specifically applied to the frontpage text field to improve the editing experience for long descriptions.

Key changes:

  • Introduces I18nAutoExpandingTextarea widget with self-contained JavaScript for dynamic height adjustment
  • Applies the new widget to the frontpage_text field in event settings configuration
  • Adds template tag loading in the settings template

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 6 comments.

File Description
app/eventyay/base/forms/init.py Adds new I18nAutoExpandingTextarea widget class with inline JavaScript for auto-expanding functionality
app/eventyay/base/configurations/default_setting.py Updates frontpage_text field to use the new I18nAutoExpandingTextarea widget and imports the widget
app/eventyay/eventyay_common/templates/eventyay_common/event/settings.html Adds {% load static %} tag (though it appears to be duplicated)

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 4 out of 4 changed files in this pull request and generated 6 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

</fieldset>
<fieldset>
<legend>{% trans "Localization" %}</legend>
{% bootstrap_field sform.locales layout="control" %}
Copy link

Copilot AI Nov 28, 2025

Choose a reason for hiding this comment

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

The content_locales field has been removed from the Localization section. This appears to be unintentional as it's not mentioned in the PR description and is unrelated to the auto-expanding textarea feature being added.

The removed line was:

{% if sform.content_locales %}{% bootstrap_field sform.content_locales layout="control" %}{% endif %}

This should be restored between sform.locales and sform.locale fields to maintain the original functionality.

Suggested change
{% bootstrap_field sform.locales layout="control" %}
{% bootstrap_field sform.locales layout="control" %}
{% if sform.content_locales %}{% bootstrap_field sform.content_locales layout="control" %}{% endif %}

Copilot uses AI. Check for mistakes.
default_attrs = {
'class': 'form-control auto-expanding-textarea',
'data-auto-expand': 'true',
'style': 'min-height: 80px; max-height: 300px; overflow-y: auto; resize: vertical; transition: height 0.2s ease-in-out;'
Copy link

Copilot AI Nov 28, 2025

Choose a reason for hiding this comment

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

The default style includes overflow-y: auto which conflicts with the dynamic overflow management in the JavaScript. The JavaScript sets overflowY to either 'auto' or 'hidden' based on content height (line 20 of auto-expanding-textarea.js), but the initial overflow-y: auto means scrollbars will be visible before the JavaScript initializes or when content is below the max height.

Consider changing overflow-y: auto to overflow-y: hidden in the default style:

'style': 'min-height: 80px; max-height: 300px; overflow-y: hidden; resize: vertical; transition: height 0.2s ease-in-out;'

This will prevent unnecessary scrollbars from appearing initially and let the JavaScript control the overflow behavior.

Suggested change
'style': 'min-height: 80px; max-height: 300px; overflow-y: auto; resize: vertical; transition: height 0.2s ease-in-out;'
'style': 'min-height: 80px; max-height: 300px; overflow-y: hidden; resize: vertical; transition: height 0.2s ease-in-out;'

Copilot uses AI. Check for mistakes.
Comment on lines +168 to +169


Copy link

Copilot AI Nov 28, 2025

Choose a reason for hiding this comment

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

Trailing whitespace detected on this line. According to PEP 8 style guidelines, lines should not have trailing whitespace. Remove the trailing spaces from this line.

Suggested change

Copilot uses AI. Check for mistakes.
Comment on lines +191 to 192


Copy link

Copilot AI Nov 28, 2025

Choose a reason for hiding this comment

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

Trailing whitespace detected on this line. According to PEP 8 style guidelines, lines should not have trailing whitespace. Remove the trailing spaces from this line.

Suggested change

Copilot uses AI. Check for mistakes.
Comment on lines 193 to +221
class I18nURLFormField(i18nfield.forms.I18nFormField):
"""
Custom form field to handle internationalized URL inputs. It extends the I18nFormField
and ensures that all provided URLs are valid.
Methods:
clean(value: LazyI18nString) -> LazyI18nString:
Validates the URL(s) in the provided internationalized input.
"""

def clean(self, value) -> LazyI18nString:
"""
Cleans and validates the internationalized URL input.
Args:
value (LazyI18nString): The input value to clean and validate.
Returns:
LazyI18nString: The cleaned and validated input value.
Raises:
ValidationError: If any of the URLs are invalid.
"""
value = super().clean(value)
if not value:
return value

url_validator = URLValidator()

if isinstance(value.data, dict):
for val in value.data.values():
if val:
url_validator(val)
else:
url_validator(value.data)

return value
return value
Copy link

Copilot AI Nov 28, 2025

Choose a reason for hiding this comment

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

[nitpick] Blank lines have been removed from the docstring of the I18nURLFormField class. These formatting changes appear to be unintentional as they're unrelated to the auto-expanding textarea feature being added. The removed blank lines served to improve readability by separating sections of the docstring.

Consider restoring the original formatting of this docstring to keep changes focused on the feature being implemented.

Copilot uses AI. Check for mistakes.
Comment on lines +15 to +20
var computedStyle = window.getComputedStyle(textarea);
var minHeight = parseInt(computedStyle.minHeight, 10) || 80;
var maxHeight = parseInt(computedStyle.maxHeight, 10) || 300;
var newHeight = Math.max(minHeight, Math.min(textarea.scrollHeight, maxHeight));
textarea.style.height = newHeight + 'px';
textarea.style.overflowY = (textarea.scrollHeight > maxHeight) ? 'auto' : 'hidden';
Copy link

Copilot AI Nov 28, 2025

Choose a reason for hiding this comment

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

The height adjustment logic has a potential race condition. When setting textarea.style.height = 'auto' on line 14, the scrollHeight used on line 18 might not reflect the correct content height if the browser hasn't finished reflow. This could lead to incorrect height calculations, especially on slower devices or with large amounts of text.

Consider using requestAnimationFrame to ensure the DOM has been updated before reading scrollHeight:

function adjustHeight() {
    textarea.style.height = 'auto';
    requestAnimationFrame(function() {
        var computedStyle = window.getComputedStyle(textarea);
        var minHeight = parseInt(computedStyle.minHeight, 10) || 80;
        var maxHeight = parseInt(computedStyle.maxHeight, 10) || 300;
        var newHeight = Math.max(minHeight, Math.min(textarea.scrollHeight, maxHeight));
        textarea.style.height = newHeight + 'px';
        textarea.style.overflowY = (textarea.scrollHeight > maxHeight) ? 'auto' : 'hidden';
    });
}
Suggested change
var computedStyle = window.getComputedStyle(textarea);
var minHeight = parseInt(computedStyle.minHeight, 10) || 80;
var maxHeight = parseInt(computedStyle.maxHeight, 10) || 300;
var newHeight = Math.max(minHeight, Math.min(textarea.scrollHeight, maxHeight));
textarea.style.height = newHeight + 'px';
textarea.style.overflowY = (textarea.scrollHeight > maxHeight) ? 'auto' : 'hidden';
requestAnimationFrame(function() {
var computedStyle = window.getComputedStyle(textarea);
var minHeight = parseInt(computedStyle.minHeight, 10) || 80;
var maxHeight = parseInt(computedStyle.maxHeight, 10) || 300;
var newHeight = Math.max(minHeight, Math.min(textarea.scrollHeight, maxHeight));
textarea.style.height = newHeight + 'px';
textarea.style.overflowY = (textarea.scrollHeight > maxHeight) ? 'auto' : 'hidden';
});

Copilot uses AI. Check for mistakes.
Copy link
Contributor

@Saksham-Sirohi Saksham-Sirohi left a comment

Choose a reason for hiding this comment

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

Hi! Since the frontpage text page contains only a single input field, it would be great if the textarea started with a larger initial height. More visible content improves usability, and the textarea could also adapt to the available page height.

Additionally, the current auto-resize feels a bit delayed. Making the resize more responsive (closer to instantaneous) would improve the editing experience.

enhancing these two aspects would make it feel much smoother for users.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Backlog

Development

Successfully merging this pull request may close these issues.

Feature (forms): Auto-Expanding Textarea for Frontpage Text in Event Settings

3 participants