Skip to content

Conversation

@rileyseaburg
Copy link
Contributor

Summary

This release includes fixes for the Touchpoints widget not displaying correctly, along with several infrastructure improvements.

Key Changes

Testing

  • All CI tests passing
  • Widget modal functionality verified to work correctly

Fixes Zendesk ticket #37620

- Add restore/save cache for Cargo target and registry directories
- Use Cargo.lock checksum for cache key to ensure proper invalidation
- Verify Rust library linkage with ldd before deploy
- Fail fast if library has unresolved dependencies
- Should significantly speed up Rust builds on subsequent runs
- Handle nil organization in User#cx_collections to prevent NoMethodError
- Add eager loading for service_provider.organization and cx_collection_details
- Add missing database indexes on cx_collections, cx_collection_details, cx_responses

Fixes the 'We're sorry, but something went wrong' error at:
/admin/cx_collections/export_csv
* Production release: Fix CX Collections export CSV error (#1911)

* Add Cargo caching and library linkage verification to CircleCI

- Add restore/save cache for Cargo target and registry directories
- Use Cargo.lock checksum for cache key to ensure proper invalidation
- Verify Rust library linkage with ldd before deploy
- Fail fast if library has unresolved dependencies
- Should significantly speed up Rust builds on subsequent runs

* Fix cx_collections export_csv 500 error

- Handle nil organization in User#cx_collections to prevent NoMethodError
- Add eager loading for service_provider.organization and cx_collection_details
- Add missing database indexes on cx_collections, cx_collection_details, cx_responses

Fixes the 'We're sorry, but something went wrong' error at:
/admin/cx_collections/export_csv

* Update schema.rb with new indexes for CircleCI

* Update schema version to include new migration

* Add tests for User#cx_collections method

* fix: use git URL for rust buildpack in production manifest

* fix: correct redis service name in production manifest

* Release: WidgetRenderer load fix (#1914)

* Add Cargo caching and library linkage verification to CircleCI

- Add restore/save cache for Cargo target and registry directories
- Use Cargo.lock checksum for cache key to ensure proper invalidation
- Verify Rust library linkage with ldd before deploy
- Fail fast if library has unresolved dependencies
- Should significantly speed up Rust builds on subsequent runs

* Fix cx_collections export_csv 500 error

- Handle nil organization in User#cx_collections to prevent NoMethodError
- Add eager loading for service_provider.organization and cx_collection_details
- Add missing database indexes on cx_collections, cx_collection_details, cx_responses

Fixes the 'We're sorry, but something went wrong' error at:
/admin/cx_collections/export_csv

* Update schema.rb with new indexes for CircleCI

* Update schema version to include new migration

* Add tests for User#cx_collections method

* Fix widget renderer load when native lib missing (#1913)

* Fix User#cx_collections specs for Service owner (#1915) (#1917)

* Release: set prod disk quota to 2G (#1919)

* Add Cargo caching and library linkage verification to CircleCI

- Add restore/save cache for Cargo target and registry directories
- Use Cargo.lock checksum for cache key to ensure proper invalidation
- Verify Rust library linkage with ldd before deploy
- Fail fast if library has unresolved dependencies
- Should significantly speed up Rust builds on subsequent runs

* Fix cx_collections export_csv 500 error

- Handle nil organization in User#cx_collections to prevent NoMethodError
- Add eager loading for service_provider.organization and cx_collection_details
- Add missing database indexes on cx_collections, cx_collection_details, cx_responses

Fixes the 'We're sorry, but something went wrong' error at:
/admin/cx_collections/export_csv

* Update schema.rb with new indexes for CircleCI

* Update schema version to include new migration

* Add tests for User#cx_collections method

* Fix widget renderer load when native lib missing (#1913)

* Fix User#cx_collections specs for Service owner (#1915)

* Fix production manifest for rust buildpack (#1918)

* fix: remove empty secret keys from manifest to prevent wiping env vars on deploy

Empty values in the manifest were overwriting secrets set via cf set-env.
Secrets should only be managed via cf set-env, not in the manifest.

* fix: Use fba-usa-modal class for USWDS Modal compatibility

* fix: Add CSS support to Rust widget renderer to fix modal positioning

* fix: Use parent_id instead of parent in Organization factory

* Increase Cloud Foundry start timeout to 180s and fix Sidekiq health check type

* Fix Sidekiq crash and optimize Rust build script

- Added rust-buildpack to Sidekiq deployment
- Updated build_widget_renderer.sh to handle workspace paths and avoid rebuilds
- Added rust-buildpack to touchpoints-demo manifest

* build(cf): simplify widget_renderer build script and ignore rules

Refactors the `.profile.d/build_widget_renderer.sh` script to remove complex logic for locating Rust dependencies, building from source, and checking library linkage. The script now focuses solely on setting `LD_LIBRARY_PATH` and ensuring the prebuilt `libwidget_renderer.so` is in the correct location.

Additionally, updates `.cfignore` to exclude the `target/` directory and specific release artifacts, while ensuring the root `libwidget_renderer.so` is preserved. This suggests a shift towards deploying a precompiled binary rather than building the Rust extension during the Cloud Foundry deployment process.

* Increase deployment timeout and add widget_renderer fallback to resolve startup crashes

* fix(widget_renderer): restore LoadError and adjust app timeout

- Re-enable raising `LoadError` in `widget_renderer.rb` when the native library is missing, removing the previous fallback log message. This ensures the application fails fast if the required extension is absent.
- Reduce application timeout from 600 to 180 seconds in `touchpoints.yml` to better align with platform constraints or performance expectations.
- Remove the hardcoded `WEB_CONCURRENCY: 1` environment variable from `touchpoints.yml`, allowing the buildpack or platform defaults to manage concurrency.

* ci: increase deployment wait time and enable static files in dev

- Increase the `max_wait` time in `.circleci/deploy.sh` from 600 to 800 seconds to prevent timeouts during longer deployment processes.
- Update `config/environments/development.rb` to conditionally enable the public file server based on the `RAILS_SERVE_STATIC_FILES` environment variable, aligning development behavior with other environments when needed.

* ci(deploy): increase timeouts and enable static file serving

- Increase Cloud Foundry push timeout from 180s to 600s in deployment scripts and `touchpoints.yml` to prevent timeouts during startup.
- Enable `RAILS_SERVE_STATIC_FILES` in production configuration and manifest to allow the application to serve precompiled assets directly.
- Update `deploy.sh` to accept a manifest path argument and refactor retry logic for better error handling.

* Decouple db:migrate from deploy: migrations must be run separately to avoid CF 180s timeout

* Add automated pre-deploy migrations via cf run-task to avoid 180s timeout during app start

* Set health check type to process for sidekiq worker before rolling deploy

* Fix sidekiq worker timeout: explicitly set to 180s before rolling deploy

* Fix flaky logo upload test: add wait time to prevent Selenium stale element race condition

* Fix cf set-health-check: use --invocation-timeout instead of --timeout

* Fix Rack::Attack test: create actual form fixture to avoid 404 responses

* Scale sidekiq worker to 1 instance during rolling deploy to avoid org memory quota exceeded

* Stop sidekiq worker before push to free memory for staging (avoids org quota exceeded)

* Fix cf run-task syntax: add --command flag for migrations

* Skip WidgetRenderer load during migrations - library not built in task droplet

* Fix Rack::Attack test: add valid submission params to avoid 400 errors

* Temporarily disable pre-deploy migrations to unblock deployment

* Fix widget_renderer initializer - use simpler skip detection logic

* Fix deployment: restore db:migrate in manifest and enable migrations in deploy script

* Revert migrations to start command - Rust library not available in cf tasks

* Build Rust library at runtime in .profile.d script

* Revert to working widget_renderer script that copies prebuilt library

* Keep prebuilt Rust library during deployment to ensure correct linking with CF Ruby installation

* Fix deploy-sidekiq.sh: remove explicit buildpack flags to avoid re-installing Rust

- Remove -b flags from cf push in deploy-sidekiq.sh
- Let CF auto-detect buildpacks from app metadata like deploy.sh does
- Prevents unnecessary reinstallation of Rust during staging
- Matches web deployment behavior for consistency

* Fix touchpoints.yml: comment out buildpacks to prevent Rust reinstallation

- Comment out explicit buildpacks in manifest
- Let CF auto-detect buildpacks from app metadata
- Prevents re-running supply phase (Rust installation) during deployment
- Rust library already built in CircleCI via bundle install (gem extensions)
- Matches deploy-sidekiq.sh approach for consistency

* Fix flaky timing test in submission_digest mailer spec

Use a single time_threshold let variable to ensure consistent timestamp
comparison instead of calling days_ago.days.ago multiple times which can
result in 1-second differences in CI environments.

* Fix custom-button-modal USWDS initialization

- Add try/catch error handling
- Add conditional checks for fbaUswds methods
- Initialize custom button element for custom-button-modal
- Prevents modal from appearing visible at page bottom

* Bump Cargo version to force Rust rebuild

* Bump widget_renderer version to force Cargo rebuild

* Force cargo clean before build to ensure recompilation

* Bump widget_renderer gem version to 0.1.2 to force rebuild

* Force Rust rebuild with BUILD_ID and version bump

* Bump widget_renderer to 0.1.2 to force CF to rebuild native extension

* Update Gemfile.lock for widget_renderer 0.1.2

* Add Rust library verification before CF push

* Prioritize workspace-level Rust library and bump to 0.1.3

* Invalidate CircleCI cargo cache to force fresh Rust build

* Fix Rust widget renderer modal button initialization

The Rust widget renderer was missing the USWDS Modal initialization
for the #fba-button element used in 'modal' delivery method forms.
This caused the toast/feedback button to not open the modal when clicked,
instead rendering the form inline at the bottom of the page.

Added initialization for #fba-button to match the ERB template behavior
in _fba.js.erb (lines 858-875).

Fixes Zendesk ticket #37620

* Fix breaking changes from PR review

1. Fix modal_class prefix logic: Now respects load_css setting
   - When load_css=true: uses 'fba-usa-modal' prefix
   - When load_css=false: uses 'usa-modal' (no prefix)
   - Matches ERB template behavior in _fba.js.erb line 110

2. Fix CSS backtick escaping: Added escape for backticks in CSS
   - Prevents JavaScript syntax errors when CSS contains backticks
   - CSS is inserted into JS template literals using backticks

3. Remove expired certificate file: tmp_expired_login_gov_cert.pem
   - Certificate expired Aug 2023
   - Added *.pem to .gitignore to prevent future accidental commits
Copilot AI review requested due to automatic review settings December 22, 2025 15:17
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 fixes critical issues with the Touchpoints widget modal button initialization and includes several infrastructure improvements. The main fix ensures that the toast/feedback button properly opens the modal when clicked by adding correct USWDS Modal initialization. Additional improvements include deployment configuration updates, database index optimizations, test fixes, and enhanced error handling for the Rust widget renderer.

Key Changes:

  • Fixed Rust widget renderer modal button initialization by properly initializing USWDS Modal for all button types
  • Added CSS backtick escaping to prevent JavaScript syntax errors in widget rendering
  • Improved deployment scripts with pre-deployment migrations, better error handling, and Login.gov environment variable synchronization
  • Added database indexes on cx_collections tables to improve query performance
  • Enhanced widget renderer loading logic to skip initialization during rake tasks and migrations

Reviewed changes

Copilot reviewed 30 out of 33 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
ext/widget_renderer/src/template_renderer.rs Adds USWDS Modal initialization for fba-button and custom buttons, fixes modal class prefix logic to respect load_css setting, adds CSS escaping for JavaScript strings
ext/widget_renderer/src/form_data.rs Adds css field to FormData struct with default value
ext/widget_renderer/lib/widget_renderer.rb Updates library search paths to prioritize workspace target directory, improves error handling with LoadError exceptions
ext/widget_renderer/extconf.rb Adds cargo clean step before build and redirects stderr to stdout
ext/widget_renderer/widget_renderer.gemspec Updates version to 0.1.3 and adds BUILD_ID to packaged files
ext/widget_renderer/BUILD_ID Adds new build identifier file
ext/widget_renderer/Cargo.toml Bumps version to 0.1.1
app/models/form.rb Adds render_widget_css method to provide CSS content to Rust renderer
app/models/user.rb Fixes cx_collections method to return empty collection when user has no organization
app/models/cx_collection.rb Optimizes to_csv query with proper includes and removes syntax error in csv generation
config/initializers/widget_renderer.rb Adds logic to skip loading during rake tasks and migrations, improves error handling
config/routes.rb Adds default value '/admin' for INDEX_URL environment variable
config/environments/production.rb Enables static file serving when RAILS_SERVE_STATIC_FILES is set
config/environments/development.rb Enables static file serving when RAILS_SERVE_STATIC_FILES is set
db/migrate/20251210192727_add_indexes_to_cx_collections.rb Adds missing foreign key indexes to cx_collections, cx_collection_details, and cx_responses tables
db/schema.rb Updates schema version and adds new indexes
touchpoints.yml Updates Redis service name, adds timeout and disk quota, improves secret management with comments, adds Rust buildpack URL
touchpoints-staging.yml Changes command to run db:schema:load and db:seed with corrected syntax, adds timeout
touchpoints-demo.yml Adds timeout and Rust buildpack URL
spec/requests/rack_attack_spec.rb Adds form factory and valid params to fix failing tests
spec/models/user_spec.rb Adds comprehensive specs for cx_collections method with organization hierarchy testing
spec/mailers/user_mailer_spec.rb Refactors to use time_threshold variable to avoid timing issues in tests
spec/features/admin/forms_spec.rb Adds sleep and increased wait time to handle stale element errors
.circleci/config.yml Adds Cargo caching, library linkage verification, and updates comments about prebuilt library
.circleci/deploy.sh Adds run_migrations function, manifest path parameter, Login.gov env sync, increased timeout
.circleci/deploy-sidekiq.sh Updates deployment strategy with app stop/start, health check updates, and Login.gov env sync
.circleci/sync-login-gov-env.sh Adds new script to synchronize Login.gov environment variables
.profile.d/build_widget_renderer.sh Simplifies to copy pre-built library instead of building at runtime
.gitignore Adds *.pem pattern to ignore certificate files
.cfignore Removes exclusions for keeping prebuilt Rust library in target directories
Gemfile.lock Updates widget_renderer version to 0.1.3
Cargo.toml Adds workspace.package configuration
Cargo.lock Updates widget_renderer version to 0.1.1
Comments suppressed due to low confidence (2)

ext/widget_renderer/src/template_renderer.rs:911

  • The modal_button_text (line 905), success_heading (line 910), and success_text (line 911) values are inserted directly into the JavaScript string without escaping. If these values contain quotes, backslashes, or newlines, they could break the JavaScript syntax or cause XSS vulnerabilities. These should be escaped similar to how CSS is escaped in lines 871-876.
            button_text = form.modal_button_text,
            selector = form.element_selector,
            css = escaped_css,
            delivery_method = form.delivery_method,
            load_css = form.load_css,
            success_heading = form.success_text_heading,
            success_text = form.success_text,

app/models/cx_collection.rb:146

  • The query eager loads 'cx_collection_details' on line 91 to optimize the N+1 query issue, but then line 146 calls '.size' which will still count the loaded associations. However, the code could instead use 'cx_collection_details.count' from the counter_cache or directly access the loaded size. Since the associations are already loaded, '.size' should work efficiently. Consider verifying this is using the preloaded data and not triggering additional queries.
      .includes(:organization, :service, :user, :cx_collection_details, service_provider: :organization)
      .references(:organization)
      .order(:fiscal_year, :quarter, 'organizations.name')

    attributes = %i[
      id
      name
      organization_id
      organization_name
      organization_abbreviation
      service_provider_id
      service_provider_name
      service_provider_organization_id
      service_provider_organization_name
      service_provider_organization_abbreviation
      service_id
      service_name
      user_email
      fiscal_year
      quarter
      created_at
      updated_at
      submitted_at
      rating
      aasm_state
      integrity_hash
      cx_collection_details_count
    ]

    CSV.generate(headers: true) do |csv|
      csv << attributes

      collections.each do |collection|
        csv << [
          collection.id,
          collection.name,
          collection.organization_id,
          collection.organization&.name,
          collection.organization&.abbreviation,
          collection.service_provider_id,
          collection.service_provider&.name,
          collection.service_provider&.organization_id,
          collection.service_provider&.organization&.name,
          collection.service_provider&.organization&.abbreviation,
          collection.service_id,
          collection.service&.name,
          collection.user&.email,
          collection.fiscal_year,
          collection.quarter,
          collection.created_at,
          collection.updated_at,
          collection.submitted_at,
          collection.rating,
          collection.aasm_state,
          collection.integrity_hash,
          collection.cx_collection_details.size,

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

}}
}}
// Ensure the custom button is also initialized if it exists (for 'custom-button-modal' delivery method)
const customButtonEl = document.getElementById('{element_selector}');
Copy link

Copilot AI Dec 22, 2025

Choose a reason for hiding this comment

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

The element_selector value is inserted directly into getElementById without escaping (line 968), which could lead to JavaScript syntax errors if the selector contains special characters like quotes or backslashes. The selector should be escaped similar to how CSS is escaped in lines 871-876.

Suggested change
const customButtonEl = document.getElementById('{element_selector}');
const customButtonEl = (window.CSS && typeof CSS.escape === 'function')
? document.querySelector('#' + CSS.escape('{element_selector}'))
: document.getElementById('{element_selector}');

Copilot uses AI. Check for mistakes.
Comment on lines 201 to 202
# Wait for form to finish updating before navigating away
sleep 0.5
Copy link

Copilot AI Dec 22, 2025

Choose a reason for hiding this comment

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

Using 'sleep 0.5' to wait for form updates is a brittle approach that can lead to flaky tests. Instead, use Capybara's built-in waiting mechanisms like 'expect(page).to have_content' or explicit wait conditions that verify the actual state change. This will make the test more reliable and faster.

Copilot uses AI. Check for mistakes.
cf_push_with_retry touchpoints
echo "Syncing Login.gov environment variables..."
./.circleci/sync-login-gov-env.sh touchpoints
cf_push_with_retry touchpoints touchpoints.yml false
Copy link

Copilot AI Dec 22, 2025

Choose a reason for hiding this comment

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

The production deployment (line 223) passes 'false' for the 'run_migrations' parameter, while demo and staging deployments (lines 235, 247) pass 'true'. This means migrations are not automatically run for production deployments, which could lead to schema mismatches after deployment. Production should either run migrations or have a clear documented process for running them separately.

Suggested change
cf_push_with_retry touchpoints touchpoints.yml false
cf_push_with_retry touchpoints touchpoints.yml true

Copilot uses AI. Check for mistakes.
Comment on lines 124 to 132
# Stop the app first to free memory for staging
echo "Stopping $app_name to free memory for staging..."
cf stop "$app_name" || true
sleep 5

# Push without rolling strategy (direct replacement since we stopped it)
# Let CF auto-detect buildpacks to avoid re-running supply phase (Rust already built in CircleCI)
if cf push "$app_name" \
-t 180 \
Copy link

Copilot AI Dec 22, 2025

Choose a reason for hiding this comment

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

The deploy script stops the Sidekiq app (line 126) before pushing, which causes downtime for background job processing. This means jobs will not be processed during the deployment window. For a worker process like Sidekiq, a better approach would be to allow the old instance to finish current jobs gracefully before replacing it, or ensure the deployment window is communicated to users.

Suggested change
# Stop the app first to free memory for staging
echo "Stopping $app_name to free memory for staging..."
cf stop "$app_name" || true
sleep 5
# Push without rolling strategy (direct replacement since we stopped it)
# Let CF auto-detect buildpacks to avoid re-running supply phase (Rust already built in CircleCI)
if cf push "$app_name" \
-t 180 \
# Do not stop the app to avoid downtime for Sidekiq background jobs.
# Keep a single instance running and rely on a rolling deploy to replace it gracefully.
# Push with rolling strategy (zero-downtime replacement of the running worker instance)
# Let CF auto-detect buildpacks to avoid re-running supply phase (Rust already built in CircleCI)
if cf push "$app_name" \
-t 180 \
--strategy rolling \

Copilot uses AI. Check for mistakes.
disk_quota: 2G
command: bundle exec rake cf:on_first_instance db:schema:load && rake db:seed && bundle exec rails s -b 0.0.0.0 -p $PORT -e $RAILS_ENV
timeout: 180
command: bundle exec rake cf:on_first_instance db:schema:load db:seed && bundle exec rails s -b 0.0.0.0 -p $PORT -e $RAILS_ENV
Copy link

Copilot AI Dec 22, 2025

Choose a reason for hiding this comment

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

The command runs two rake tasks sequentially using '&&' which means both will execute. However, the syntax 'rake cf:on_first_instance db:schema:load db:seed' will pass both 'db:schema:load' and 'db:seed' as arguments to the first task, not execute them as separate tasks. This should be 'rake cf:on_first_instance && rake db:schema:load && rake db:seed' or 'rake cf:on_first_instance db:schema:load && rake db:seed' to properly execute each task.

Copilot uses AI. Check for mistakes.
Comment on lines 386 to 406
def render_widget_css
controller = ApplicationController.new

# Set up a mock request with default URL options
default_options = Rails.application.config.action_controller.default_url_options ||
Rails.application.config.action_mailer.default_url_options ||
{}
host = default_options[:host] || 'localhost'
port = default_options[:port] || 3000
protocol = default_options[:protocol] || (port == 443 ? 'https' : 'http')

mock_request = ActionDispatch::Request.new(
'rack.url_scheme' => protocol,
'HTTP_HOST' => "#{host}#{":#{port}" if port != 80 && port != 443}",
'SERVER_NAME' => host,
'SERVER_PORT' => port.to_s,
)

controller.request = mock_request
controller.render_to_string(partial: 'components/widget/widget', formats: :css, locals: { form: self })
end
Copy link

Copilot AI Dec 22, 2025

Choose a reason for hiding this comment

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

The code for setting up the mock request is duplicated between 'render_touchpoints_js_string' (lines 361-382) and 'render_widget_css' (lines 386-406). This duplication should be extracted into a private helper method to improve maintainability and reduce the risk of inconsistencies.

Copilot uses AI. Check for mistakes.
Comment on lines 33 to 34
puts "Cleaning previous build artifacts..."
system("#{cargo_bin} clean 2>&1")
Copy link

Copilot AI Dec 22, 2025

Choose a reason for hiding this comment

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

Running 'cargo clean' before every build (line 34) removes cached artifacts, significantly increasing build time. Since the build is running in a clean CI environment or during gem installation, the clean step is unnecessary and should be removed to improve build performance.

Suggested change
puts "Cleaning previous build artifacts..."
system("#{cargo_bin} clean 2>&1")

Copilot uses AI. Check for mistakes.
@rileyseaburg
Copy link
Contributor Author

Verification of Current Production State

I checked the widget JS currently served from production (https://touchpoints.app.cloud.gov/touchpoints/92b47c29.js) and confirmed the issue:

Current Production Code (Missing Fix)

The USWDS initialization at the end of the widget script only initializes:

  • fbaUswds.ComboBox.on(fbaFormElement)
  • fbaUswds.DatePicker.on(fbaFormElement)
  • fbaUswds.Modal.on(fbaModalElement)

Missing: Initialization for #fba-button element

After This Release

The fix adds the missing button initialization:

const fbaButton = document.querySelector('#fba-button');
if (fbaButton) {
    if (fbaUswds.Modal) {
        fbaUswds.Modal.on(fbaButton);
        fbaButton.classList.add('fba-initialized');
    }
}

This will allow the USWDS Modal library to properly handle click events on the feedback button, enabling the modal to open when users click the toast button.

Testing Recommendation

After merge and deployment, verify on https://touchpoints.digital.gov that:

  1. The "How can we improve Touchpoints?" button appears in the bottom right corner
  2. Clicking the button opens the modal dialog
  3. The modal can be closed and reopened correctly

1. Fix element_selector handling in template_renderer.rs
   - Added null check for element_selector before calling getElementById
   - Prevents potential JS errors with empty/null selectors

2. Remove unnecessary cargo clean from extconf.rb
   - Removing cached artifacts significantly increases build time
   - Clean CI environment doesn't need cargo clean before build

3. Extract mock request setup to helper method in form.rb
   - Created build_controller_with_mock_request private method
   - Reduces code duplication between touchpoints_js_string and render_widget_css
   - Improves maintainability
rileyseaburg and others added 6 commits December 22, 2025 15:35
Empty string '' is truthy in JavaScript when interpolated from Rust.
Use length check to properly handle empty element_selector values.
Added wait_for_ajax after 'form title saved' message appears to ensure
the backend save completes before page refresh. Also removed duplicate
wait_for_builder call.
Documents the incident where the Rust widget renderer was missing
the #fba-button USWDS Modal initialization, causing modal widgets
to not open when users clicked the feedback button.

Zendesk ticket #37620
@rileyseaburg rileyseaburg merged commit 6845520 into production Dec 22, 2025
14 of 15 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants