inline_forms 8.1.65 → 8.1.66

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: acc7bbb91a0ebefa61a500e74814588ffeed1ac4bb9c4da36098b09d47c8483e
4
- data.tar.gz: 448a2bd05b8ef8cf69d4a4268c29cfaa7c8955089f4b57675632334e5b2ef42c
3
+ metadata.gz: 7d798e9e7a6d981b422009a82073f77675148cfcd17263b501067d7d6e0a3b57
4
+ data.tar.gz: 76876893fbad74dd364b02c21f927bc414cb86b2b6e7a89b1caa5a3a941bab4d
5
5
  SHA512:
6
- metadata.gz: ead6649bf5d9c969bc93b4f937f2fe11e97be176184788ff884e94932471baf893408711a3556ef274f4a3eea2be0ffd3401e52f03848b69c3bf2a89ca454880
7
- data.tar.gz: 1f74c4a96abc1dfcb0215e6f465636e80d0512a3f9a4ab590a33ab3764d0d7d9f3377e39576cfcac2f9ad79b2890de33b7cd8ffd3bee4ba59d406a0f96d9dd6f
6
+ metadata.gz: d1c4624d5315833d86d2578112d2f494b8f7cd32219698de060afbc7570a638fb98c5bf07fd376de87854b264c85521033da2c88920093c3552b5925c5062c8b
7
+ data.tar.gz: dab2278a87d37e9005baafc360503a0a767cbd95ce1e20ad0484463098d65763547daac3f25c6e16ca1707ca8ee92ef7e9f922ffdfce996c74670af849f028bc
data/CHANGELOG.md CHANGED
@@ -4,6 +4,42 @@ All notable changes to this project are documented in this file.
4
4
 
5
5
  ## [Unreleased]
6
6
 
7
+ ### Known issues
8
+
9
+ - Known issues, parked (local note `stuff/2026-10-31-dropdown-rule-3-parked.md`) with dropdown order rule 3: `includes`, `group` or `select` in `inline_forms_list` make `dropdown` and `dropdown_with_other` raise while `/index` works, so 8.1.65's fix for a list order on a joined table holds only for `joins`, `left_joins` and `eager_load`; a has_many join gives duplicate options. Present since 8.1.60. No host's list scope has these today. Parked; not changed.
10
+
11
+ ## [8.1.66] - 2026-11-06
12
+
13
+ Requests from Papiamentu's move to the 8.1.65 admin (gem layout, engine model top bar, gem stylesheet); each was worked around in the apps.
14
+
15
+ ### Fixed
16
+
17
+ - **Top-bar submenus no longer flash open after a Turbo Drive visit.** Turbo replaced `<body>` and re-ran the inline_forms bundle, which loaded a fresh jQuery; until it finished, the new body's "more..." and user submenus showed as plain lists. StProject, Scouting and Papiamentu each copied the admin layout to fix it. Now `layouts/inline_forms` starts as `<html class="no-js">`; `inline_forms.js` re-initialises Foundation on `turbo:load`, behind a once-guard (Turbo still re-runs the bundle on each visit, so a visit from a layout without it works; the guard stops the listeners piling up and a second `foundation()` from the fresh jQuery); and `inline_forms.scss` hides a submenu until Foundation has marked it `is-dropdown-submenu`.
18
+ - **No more 404 for `/vendor/assets/stylesheets/tippy.css`.** `foundation_and_overrides.scss` imported `tippy.css` with its extension, which Dart Sass keeps as a plain CSS `@import`; the browser resolved it next to the compiled stylesheet. The vendored file is now inlined with `meta.load-css` (no new `@import` deprecation), in admin and devise CSS. Apps can delete their `public/vendor/assets/stylesheets/tippy.css`.
19
+ - **Version and file timestamps show the year.** The versions list, the file entries in it and the file trash used `l(time, format: :short)`, and rails-i18n's `:short` has no year. They now use the gem's `time.formats.inline_forms` (`"%d %b %Y %H:%M"` in en and nl; a host can override it). The versions list and file entries render a `<time>` with the ISO 8601 time as `datetime` and `title` (new helper `inline_forms_time`).
20
+ - **The generic search box is translated.** `_generic_search` had "zoek op naam...", "zoek" and "reset" hard-coded; now `inline_forms.search.placeholder` / `.submit` / `.reset` (en: "search...", "search", "reset"; nl: "zoeken...", "zoek", "wissen").
21
+ - **No placeholder titles.** The gem's en and nl locales defined `general.application_title: Application Title`, `devise.title_for_devise: Title for Devise` and `devise.welcome: Welcome for Devise`, shown in any locale the host did not override (the installer only writes en). The keys are gone; the application and devise layouts and the devise top bars fall back to the host's `application_name` translation, else the Rails module name (new helper `inline_forms_application_title`), and the sign-in page to `inline_forms.devise.welcome_to` ("Welcome to %{name}!" / "Welkom bij %{name}!"). A host's own translations still win.
22
+ - **`inline_forms_search_on` no longer touches the database when the model loads.** It quoted the table and column names in the class body, so loading the model needed a connection; the clause is now built when the scope runs.
23
+
24
+ ### Changed
25
+
26
+ - **Installer: the Dart Sass self-import guard no longer patches dartsass-rails.** The generated `config/initializers/inline_forms_dartsass_builds.rb` prepended the gem's stylesheet dir to `Dartsass::Runner.dartsass_load_paths`, an internal (a silent no-op if renamed; a boot error if `dartsass/runner` moved). The two `inline_forms_install/*_main.scss` entries now `@use "inline_forms/inline_forms.scss"` and `"inline_forms/devise.scss"` with the extension, which Dart Sass never resolves to a previous `app/assets/builds/*.css`, and which fails loudly if the gem renames the file. Existing apps keep their initializer until they change it.
27
+
28
+ ### Not done
29
+
30
+ - **Autocomplete in the generic search** (a `rails-jquery-autocomplete` field when the controller declares `autocomplete :<model>, :name`): that gem needs jQuery UI, which inline_forms dropped in 8.1.26, and it is not an inline_forms dependency. An app that wants it keeps its own `_generic_search` override.
31
+
32
+ ### Tests
33
+
34
+ - `turbo_menu_test` (new): the layout's `no-js`, the once-guarded `turbo:load` re-init, the submenu rule, tippy inlined.
35
+ - `locale_test`: timestamps with the year and an ISO `title`, the search box in en and nl, no placeholder titles (devise and application layouts, the devise top bar).
36
+ - `model_top_bar_test`: `inline_forms_search_on` declared on a model whose connection raises.
37
+ - Example app: `example_app_stylesheet_test` (new) checks the compiled CSS has no `@import`, inlines tippy and hides uninitialised submenus; `example_app_dartsass_build_test` checks the `.scss` extensions instead of the patched load path. `example_app_user_locale_test` and `example_app_layout_title_test` expect `<html lang="…" class="no-js">`.
38
+
39
+ ### Lockstep
40
+
41
+ - validation_hints 8.1.66, inline_forms_installer 8.1.66, inline_forms_schema_edit 8.1.66.
42
+
7
43
  ## [8.1.65] - 2026-10-31
8
44
 
9
45
  Fixes from a review of 8.1.60 to 8.1.64.
@@ -14,14 +14,30 @@
14
14
  // dropdown_with_other combobox is an <input list> + <datalist>, and
15
15
  // slider_with_values is a native <input type="range">. jQuery itself stays
16
16
  // only because Foundation 6's JS requires it.
17
- $(function(){
18
- $(document).foundation();
19
- initInlineFormsWidgets(document);
20
- });
17
+ // Once per page load. Turbo Drive re-runs this bundle on every visit (it sits
18
+ // in <body>), and each run brings a fresh jQuery: without the guard each run
19
+ // added another turbo:load listener, and a ready-handler `foundation()` from
20
+ // the fresh copy (its own plugin data) would initialise the menus twice.
21
+ if (!window.inlineFormsBooted) {
22
+ window.inlineFormsBooted = true;
21
23
 
22
- document.addEventListener("turbo:load", function() {
23
- initInlineFormsWidgets(document);
24
- });
24
+ $(function(){
25
+ $(document).foundation();
26
+ initInlineFormsWidgets(document);
27
+ });
28
+
29
+ // Turbo Drive replaces <body> on a visit: the new top bar's DropdownMenu
30
+ // has to be initialised again, or its submenus show as plain lists.
31
+ // Foundation's reflow skips elements that already carry a plugin, so the
32
+ // call on the first page (turbo:load fires there too) is harmless. jQuery
33
+ // is looked up at call time.
34
+ document.addEventListener("turbo:load", function() {
35
+ if (window.jQuery && window.jQuery.fn.foundation) {
36
+ window.jQuery(document).foundation();
37
+ }
38
+ initInlineFormsWidgets(document);
39
+ });
40
+ }
25
41
 
26
42
  // Widget init: one path for first paint, turbo:load and turbo:frame-load.
27
43
  // Form element helpers emit class hooks only — no inline <script> tags.
@@ -1,5 +1,7 @@
1
1
  @charset 'utf-8';
2
2
 
3
+ @use 'sass:meta';
4
+
3
5
  @import 'settings/settings';
4
6
 
5
7
  // Match F5 visual proportions: F5 `$row-width` was 62.5rem (1000px); F6
@@ -58,7 +60,12 @@ $body-font-family: 'Open Sans', 'Helvetica Neue', Helvetica, Roboto, Arial, sans
58
60
  // Stays @import: see the module-system note in inline_forms.scss.
59
61
  @import 'theme';
60
62
 
61
- @import '../../../vendor/assets/stylesheets/tippy.css';
63
+ // Tippy's vendored CSS, inlined. It used to be `@import '…/tippy.css'`,
64
+ // which Dart Sass passes through as a plain CSS @import: the browser then
65
+ // fetched /vendor/assets/stylesheets/tippy.css (resolved next to the compiled
66
+ // stylesheet), a 404 on every page. load-css (not an extension-less @import,
67
+ // deprecated for Dart Sass 3) emits it here, in the same place.
68
+ @include meta.load-css('../../../vendor/assets/stylesheets/tippy');
62
69
 
63
70
  .validation-hints-source[hidden] {
64
71
  display: none !important;
@@ -113,6 +113,16 @@
113
113
  }
114
114
  }
115
115
 
116
+ // A top-bar submenu shows as a plain list until Foundation's DropdownMenu
117
+ // marks it `is-dropdown-submenu` (whose own rules then hide it). On the first
118
+ // page `html.no-js` covers that gap (layouts/inline_forms.html.erb), but
119
+ // Foundation removes the class and Turbo Drive never restores it, so after a
120
+ // visit the new body's submenus flashed open until inline_forms.js
121
+ // re-initialised them on turbo:load.
122
+ .dropdown.menu > li > ul.menu:not(.is-dropdown-submenu) {
123
+ display: none;
124
+ }
125
+
116
126
  // Match: model top bar is the wider gold band (height 90px, padded so its
117
127
  // usable area is in the lower 45px). Override Foundation 6's default
118
128
  // `.top-bar { padding: 0.5rem }` so our 45/90px math holds.
@@ -394,6 +394,21 @@ module InlineFormsHelper
394
394
  values
395
395
  end
396
396
 
397
+ # A version or file timestamp: the date with its year
398
+ # (time.formats.inline_forms; rails-i18n's :short has none, so edits years
399
+ # apart read the same), the exact ISO 8601 time on hover.
400
+ def inline_forms_time(time)
401
+ tag.time(l(time, format: :inline_forms), datetime: time.iso8601, title: time.iso8601)
402
+ end
403
+
404
+ # The application's name for the gem's titles and the Devise pages: the
405
+ # host's `application_name` translation, else its Rails module name. The
406
+ # gem used to ship "Application Title" / "Title for Devise" placeholders,
407
+ # shown in every locale the host did not override (nl, in all of them).
408
+ def inline_forms_application_title
409
+ t("application_name", default: Rails.application.class.module_parent_name.titleize)
410
+ end
411
+
397
412
  def version_modified_by(id)
398
413
  return "Unknown" if id.blank?
399
414
 
@@ -6,7 +6,8 @@
6
6
  and the contents will be displayed here.
7
7
  </p>
8
8
  <p>
9
- Change application title, title_for_devise, welcome_for_devise in<br />
10
- <em>config/locales/inline_forms.en.yml</em><br />
9
+ The titles come from the <em>application_name</em> translation; override
10
+ inline_forms.general.application_title, inline_forms.devise.title_for_devise
11
+ or inline_forms.devise.welcome in <em>config/locales</em> to change one.<br />
11
12
  </p>
12
13
 
@@ -3,7 +3,7 @@
3
3
  <div class="top-bar-left">
4
4
  <ul class="menu">
5
5
  <li class="menu-text">
6
- <h1 style="margin: 0; font-size: inherit;"><a href='/'><%= t('inline_forms.devise.title_for_devise') %> <%= InlineForms::VERSION %></a></h1>
6
+ <h1 style="margin: 0; font-size: inherit;"><a href='/'><%= t('inline_forms.devise.title_for_devise', default: inline_forms_application_title) %> <%= InlineForms::VERSION %></a></h1>
7
7
  </li>
8
8
  </ul>
9
9
  </div>
@@ -3,7 +3,7 @@
3
3
  <div class="top-bar-left">
4
4
  <ul class="menu">
5
5
  <li class="menu-text">
6
- <h1 style="margin: 0; font-size: inherit;"><a href='/'><%= t('inline_forms.devise.title_for_devise') %> <%= InlineForms::VERSION %></a></h1>
6
+ <h1 style="margin: 0; font-size: inherit;"><a href='/'><%= t('inline_forms.devise.title_for_devise', default: inline_forms_application_title) %> <%= InlineForms::VERSION %></a></h1>
7
7
  </li>
8
8
  </ul>
9
9
  </div>
@@ -4,7 +4,7 @@
4
4
 
5
5
  <div class="row" id='inline_forms_devise_header_row'>
6
6
  <div class="small-11 centered column">
7
- <h1><%= t('inline_forms.devise.welcome') %></h1>
7
+ <h1><%= t('inline_forms.devise.welcome', default: t('inline_forms.devise.welcome_to', name: inline_forms_application_title)) %></h1>
8
8
  </div>
9
9
  </div>
10
10
 
@@ -1,13 +1,13 @@
1
1
  <%= form_tag request.path, method: :get do %>
2
2
  <div class="row collapse">
3
3
  <div class="large-1 small-1 columns">
4
- <%= link_to "<i class='fi-x'></i>".html_safe, request.path, title: "reset", class: "button expanded inline_forms_model_top_bar_buttons" %>
4
+ <%= link_to tag.i(class: "fi-x"), request.path, title: t("inline_forms.search.reset"), class: "button expanded inline_forms_model_top_bar_buttons" %>
5
5
  </div>
6
6
  <div class="large-6 small-6 columns">
7
- <%= text_field_tag :search, params[:search], placeholder: "zoek op naam...", id: "input_search" %>
7
+ <%= text_field_tag :search, params[:search], placeholder: t("inline_forms.search.placeholder"), id: "input_search" %>
8
8
  </div>
9
9
  <div class="large-3 small-3 columns">
10
- <%= submit_tag "zoek", class: "button expanded inline_forms_model_top_bar_buttons" %>
10
+ <%= submit_tag t("inline_forms.search.submit"), class: "button expanded inline_forms_model_top_bar_buttons" %>
11
11
  </div>
12
12
  <div class="large-2 small-2 columns">
13
13
  &nbsp;
@@ -23,7 +23,7 @@
23
23
  <%= t("inline_forms.files.event.#{entry[:file_event]}") %>
24
24
  </div>
25
25
  <div class="small-2 column">
26
- <%= l(entry[:at], format: :short) %>
26
+ <%= inline_forms_time(entry[:at]) %>
27
27
  </div>
28
28
  <div class="small-2 column">
29
29
  <%= inline_forms_file_user_name(entry[:by]) %>
@@ -95,7 +95,7 @@
95
95
  <%= t("inline_forms.view.version_events.#{version.event}", default: version.event) -%><% if entry[:kind] == :rich_text %> <em>(<%= t("inline_forms.view.versions_rich_text") %>)</em><% end %>
96
96
  </div>
97
97
  <div class="small-2 column">
98
- <%= l(version.created_at, format: :short) -%>
98
+ <%= inline_forms_time(version.created_at) -%>
99
99
  </div>
100
100
  <div class="small-2 column">
101
101
  <%= version_modified_by version.whodunnit -%>
@@ -14,7 +14,7 @@
14
14
  <% elsif @last_sweep.error.present? %>
15
15
  <%= t("inline_forms.files.global.sweep_failed", error: @last_sweep.error) %>
16
16
  <% else %>
17
- <%= t("inline_forms.files.global.sweep_last", at: l(@last_sweep.ran_at, format: :short), count: @last_sweep.purged_count) %>
17
+ <%= t("inline_forms.files.global.sweep_last", at: l(@last_sweep.ran_at, format: :inline_forms), count: @last_sweep.purged_count) %>
18
18
  <% end %>
19
19
  </p>
20
20
 
@@ -78,10 +78,10 @@
78
78
  <span class="inline_forms-file-name"><%= inline_forms_file_entry_name(entry) %></span>
79
79
  <span class="inline_forms-file-meta"><%= inline_forms_file_size(entry.byte_size) %> &middot; <%= t("inline_forms.files.reason.#{entry.reason}") %></span>
80
80
  </td>
81
- <td><%= t("inline_forms.files.by_on", user: inline_forms_file_user_name(entry.trashed_by_id), at: l(entry.trashed_at, format: :short)) %></td>
81
+ <td><%= t("inline_forms.files.by_on", user: inline_forms_file_user_name(entry.trashed_by_id), at: l(entry.trashed_at, format: :inline_forms)) %></td>
82
82
  <td>
83
- <% if entry.state == :restored %><%= t("inline_forms.files.state_restored", user: inline_forms_file_user_name(entry.restored_by_id), at: l(entry.restored_at, format: :short)) %>
84
- <% elsif entry.state == :purged %><%= t("inline_forms.files.state_purged", user: inline_forms_file_user_name(entry.purged_by_id), at: l(entry.purged_at, format: :short)) %>
83
+ <% if entry.state == :restored %><%= t("inline_forms.files.state_restored", user: inline_forms_file_user_name(entry.restored_by_id), at: l(entry.restored_at, format: :inline_forms)) %>
84
+ <% elsif entry.state == :purged %><%= t("inline_forms.files.state_purged", user: inline_forms_file_user_name(entry.purged_by_id), at: l(entry.purged_at, format: :inline_forms)) %>
85
85
  <% elsif entry.state == :expired %><%= t("inline_forms.files.state_expired") %>
86
86
  <% else %>
87
87
  <% if entry.purge_after %><%= t("inline_forms.files.expires_in", count: ((entry.purge_after - Time.current) / 1.day).ceil, date: l(entry.purge_after.to_date)) %><% else %><%= t("inline_forms.files.global.states.trashed") %><% end %>
@@ -41,13 +41,13 @@
41
41
  <span class="inline_forms-file-meta">
42
42
  <%= inline_forms_file_size(entry.byte_size) %> &middot;
43
43
  <%= t("inline_forms.files.reason.#{entry.reason}") %>
44
- <%= t("inline_forms.files.by_on", user: inline_forms_file_user_name(entry.trashed_by_id), at: l(entry.trashed_at, format: :short)) %>
44
+ <%= t("inline_forms.files.by_on", user: inline_forms_file_user_name(entry.trashed_by_id), at: l(entry.trashed_at, format: :inline_forms)) %>
45
45
  </span>
46
46
  <span class="inline_forms-file-meta">
47
47
  <% if entry.state == :restored %>
48
- <%= t("inline_forms.files.state_restored", user: inline_forms_file_user_name(entry.restored_by_id), at: l(entry.restored_at, format: :short)) %>
48
+ <%= t("inline_forms.files.state_restored", user: inline_forms_file_user_name(entry.restored_by_id), at: l(entry.restored_at, format: :inline_forms)) %>
49
49
  <% elsif entry.state == :purged %>
50
- <%= t("inline_forms.files.state_purged", user: inline_forms_file_user_name(entry.purged_by_id), at: l(entry.purged_at, format: :short)) %>
50
+ <%= t("inline_forms.files.state_purged", user: inline_forms_file_user_name(entry.purged_by_id), at: l(entry.purged_at, format: :inline_forms)) %>
51
51
  <% elsif entry.state == :expired %>
52
52
  <%= t("inline_forms.files.state_expired") %>
53
53
  <% else %>
@@ -4,7 +4,7 @@
4
4
  <meta charset="utf-8" />
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
6
  <title>
7
- <%= t('inline_forms.general.application_title') %> v<%= InlineForms::VERSION %>
7
+ <%= t('inline_forms.general.application_title', default: inline_forms_application_title) %> v<%= InlineForms::VERSION %>
8
8
  </title>
9
9
  <%= stylesheet_link_tag "application" %>
10
10
  <%= stylesheet_link_tag "inline_forms_fonts" %>
@@ -4,7 +4,7 @@
4
4
  <meta charset="utf-8" />
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
6
  <title>
7
- <%= t('inline_forms.general.application_title') %> v<%= InlineForms::VERSION %>
7
+ <%= t('inline_forms.general.application_title', default: inline_forms_application_title) %> v<%= InlineForms::VERSION %>
8
8
  </title>
9
9
  <%= stylesheet_link_tag "inline_forms_fonts" %>
10
10
  <%= stylesheet_link_tag "inline_forms/devise" %>
@@ -1,5 +1,10 @@
1
1
  <!DOCTYPE html>
2
- <html lang="<%= I18n.locale %>">
2
+ <%# class="no-js": Foundation's CSS hides the top-bar submenus while it is set %>
3
+ <%# and its JS removes it on the first init, so the submenus do not show as %>
4
+ <%# plain lists before then. Turbo Drive never puts it back, so later visits %>
5
+ <%# rely on the `ul.menu:not(.is-dropdown-submenu)` rule in inline_forms.scss %>
6
+ <%# and on the turbo:load handler in inline_forms.js. %>
7
+ <html lang="<%= I18n.locale %>" class="no-js">
3
8
  <head>
4
9
  <meta charset="utf-8" />
5
10
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
@@ -37,6 +42,9 @@
37
42
  </div>
38
43
  <%# Tippy.js (v6) needs Popper.js as a global before its UMD factory runs %>
39
44
  <%# (`tippy-bundle.umd.min` only bundles default themes, not Popper itself). %>
45
+ <%# Turbo Drive re-runs these on every visit (no data-turbo-eval="false": %>
46
+ <%# a visit from a page whose layout lacks them must still load them). %>
47
+ <%# inline_forms.js boots once and re-initialises Foundation on turbo:load. %>
40
48
  <%= javascript_include_tag 'popper.min' %>
41
49
  <%= javascript_include_tag 'tippy-bundle.umd.min' %>
42
50
  <%= javascript_include_tag 'inline_forms/inline_forms' %>
@@ -19,11 +19,13 @@ module InlineForms
19
19
  end
20
20
 
21
21
  @inline_forms_searchable = true
22
- table = table_name
23
- clause = cols.map { |c|
24
- "#{connection.quote_table_name(table)}.#{connection.quote_column_name(c)} LIKE :q"
25
- }.join(" OR ")
22
+ # The clause is built when the scope runs, not here: quoting needs a
23
+ # database connection, and the class body runs whenever the model
24
+ # loads (eager loading, assets:precompile without a database).
26
25
  scope :inline_forms_search, ->(q) {
26
+ clause = cols.map { |c|
27
+ "#{connection.quote_table_name(table_name)}.#{connection.quote_column_name(c)} LIKE :q"
28
+ }.join(" OR ")
27
29
  where(clause, q: "%#{sanitize_sql_like(q.to_s)}%")
28
30
  }
29
31
  end
@@ -1,5 +1,5 @@
1
1
  # -*- encoding : utf-8 -*-
2
2
 
3
3
  module InlineForms
4
- VERSION = "8.1.65"
4
+ VERSION = "8.1.66"
5
5
  end
@@ -1,10 +1,16 @@
1
1
  en:
2
2
  inline_forms:
3
- general:
4
- application_title: Application Title
3
+ # The generic list search (inline_forms/_generic_search), shown for
4
+ # models that declare inline_forms_search_on.
5
+ search:
6
+ placeholder: "search..."
7
+ submit: search
8
+ reset: reset
9
+ # general.application_title, devise.title_for_devise and devise.welcome
10
+ # are not defined here: without a host override the views use the
11
+ # host's application_name translation (InlineFormsHelper#inline_forms_application_title).
5
12
  devise:
6
- title_for_devise: Title for Devise
7
- welcome: Welcome for Devise
13
+ welcome_to: "Welcome to %{name}!"
8
14
  passwords:
9
15
  forgot_your_password: Forgot Your Password?
10
16
  change_your_password: Change your password, if you want!
@@ -152,3 +158,8 @@ en:
152
158
  messages:
153
159
  inline_forms_file_too_large: "is too large (maximum %{count} MB)"
154
160
  inline_forms_file_missing: "no file chosen"
161
+ # Version and file timestamps (InlineFormsHelper#inline_forms_time and the
162
+ # file trash): rails-i18n's :short has no year.
163
+ time:
164
+ formats:
165
+ inline_forms: "%d %b %Y %H:%M"
@@ -1,10 +1,16 @@
1
1
  nl:
2
2
  inline_forms:
3
- general:
4
- application_title: Application Title
3
+ # The generic list search (inline_forms/_generic_search), shown for
4
+ # models that declare inline_forms_search_on.
5
+ search:
6
+ placeholder: "zoeken..."
7
+ submit: zoek
8
+ reset: wissen
9
+ # general.application_title, devise.title_for_devise and devise.welcome
10
+ # are not defined here: without a host override the views use the
11
+ # host's application_name translation (InlineFormsHelper#inline_forms_application_title).
5
12
  devise:
6
- title_for_devise: Title for Devise
7
- welcome: Welcome for Devise
13
+ welcome_to: "Welkom bij %{name}!"
8
14
  passwords:
9
15
  forgot_your_password: Wachtwoord Vergeten?
10
16
  change_your_password: Verander wachtwoord, als u wilt!
@@ -152,3 +158,8 @@ nl:
152
158
  messages:
153
159
  inline_forms_file_too_large: "is te groot (maximaal %{count} MB)"
154
160
  inline_forms_file_missing: "geen bestand gekozen"
161
+ # Version and file timestamps (InlineFormsHelper#inline_forms_time and the
162
+ # file trash): rails-i18n's :short has no year.
163
+ time:
164
+ formats:
165
+ inline_forms: "%d %b %Y %H:%M"
@@ -19,6 +19,8 @@ class LocaleTest < InlineFormsIntegrationTestCase
19
19
  inline_forms.view.version_events.update inline_forms.view.version_events.destroy
20
20
  inline_forms.devise.login common.more common.logout inline_forms.view.create_failed
21
21
  inline_forms.files.preview inline_forms.files.download_pdf
22
+ inline_forms.search.placeholder inline_forms.search.submit inline_forms.search.reset
23
+ inline_forms.devise.welcome_to time.formats.inline_forms
22
24
  ].freeze
23
25
 
24
26
  setup do
@@ -107,7 +109,60 @@ class LocaleTest < InlineFormsIntegrationTestCase
107
109
  end
108
110
  refute_includes body, "<strong>Event</strong>"
109
111
  version = @machine.versions.last
110
- assert_includes body, ERB::Util.h(I18n.l(version.created_at, format: :short, locale: :nl))
112
+ assert_includes body, ERB::Util.h(I18n.l(version.created_at, format: :inline_forms, locale: :nl))
113
+ end
114
+
115
+ # rails-i18n's :short ("%d %b %H:%M") has no year: edits years apart read
116
+ # the same next to Restore.
117
+ test "version timestamps show the year, and the exact time on hover" do
118
+ version = @machine.versions.last
119
+ version.update_columns(created_at: Time.zone.local(2019, 3, 5, 14, 7))
120
+ row = "machine_#{@machine.id}"
121
+ strictly(:nl) do
122
+ get list_versions_machine_path(@machine, update: row), headers: frame_headers(row)
123
+ end
124
+
125
+ assert_response :success
126
+ iso = version.reload.created_at.iso8601
127
+ assert_includes response.body, %(<time datetime="#{iso}" title="#{iso}">05 mrt 2019 14:07</time>)
128
+ end
129
+
130
+ test "the generic search box is translated" do
131
+ I18n.backend.store_translations(:en, widgets: "widgets")
132
+ I18n.backend.store_translations(:nl, widgets: "widgets")
133
+ { en: [ "search...", "search", "reset" ], nl: [ "zoeken...", "zoek", "wissen" ] }.each do |locale, (placeholder, submit, reset)|
134
+ strictly(locale) { get widgets_path }
135
+
136
+ assert_response :success
137
+ assert_includes response.body, %(placeholder="#{placeholder}")
138
+ assert_includes response.body, %(value="#{submit}")
139
+ assert_includes response.body, %(title="#{reset}")
140
+ refute_includes response.body, "zoek op naam" if locale == :en
141
+ end
142
+ end
143
+
144
+ # The gem shipped "Application Title" / "Title for Devise" / "Welcome for
145
+ # Devise" in en and nl; any locale the host did not override showed them.
146
+ test "no placeholder titles: the gem's titles use the host's application name" do
147
+ %i[en nl].each do |locale|
148
+ %w[inline_forms.general.application_title inline_forms.devise.title_for_devise inline_forms.devise.welcome].each do |key|
149
+ refute I18n.exists?(key, locale), "#{locale}.#{key} is a placeholder; the views fall back to application_name"
150
+ end
151
+ end
152
+
153
+ get widgets_path
154
+ I18n.backend.store_translations(:nl, application_name: "Werkplaats")
155
+ strictly(:nl) do
156
+ devise = controller.render_to_string(template: "stats/show", layout: "devise")
157
+ assert_includes devise, "Werkplaats v#{InlineForms::VERSION}"
158
+ top_bar = controller.render_to_string(partial: "devise/sessions/top-bar")
159
+ assert_includes top_bar, "Werkplaats #{InlineForms::VERSION}"
160
+ assert_equal "Welkom bij Werkplaats!",
161
+ I18n.t("inline_forms.devise.welcome", default: I18n.t("inline_forms.devise.welcome_to", name: "Werkplaats"))
162
+ end
163
+ I18n.with_locale(:en) do
164
+ assert_includes controller.render_to_string(template: "stats/show", layout: "application"), "Dummy v#{InlineForms::VERSION}"
165
+ end
111
166
  end
112
167
 
113
168
  test "the versions list in en keeps its English labels" do
@@ -62,4 +62,22 @@ class ModelTopBarTest < InlineFormsIntegrationTestCase
62
62
  refute_includes response.body, "new_button"
63
63
  refute_includes response.body, %(id="input_search")
64
64
  end
65
+
66
+ # The class body runs whenever the model loads (eager loading,
67
+ # assets:precompile with no database); quoting there needed a connection.
68
+ test "inline_forms_search_on needs no database connection until the search runs" do
69
+ model = Class.new(ApplicationRecord) do
70
+ self.table_name = "widgets"
71
+ def self.name = "OfflineWidget"
72
+ end
73
+ no_db = ->(*) { raise "database touched at declaration" }
74
+ %i[connection lease_connection with_connection].each { |m| model.define_singleton_method(m, &no_db) }
75
+
76
+ model.inline_forms_search_on :name
77
+
78
+ assert model.inline_forms_searchable?
79
+ %i[connection lease_connection with_connection].each { |m| model.singleton_class.remove_method(m) }
80
+ Widget.create!(name: "Gamma")
81
+ assert_equal [ "Gamma" ], model.inline_forms_search("amm").pluck(:name)
82
+ end
65
83
  end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../integration_test_helper"
4
+
5
+ # Top-bar submenus after a Turbo Drive visit. Turbo replaced <body> and re-ran
6
+ # the inline_forms bundle (a fresh jQuery, its own Foundation plugin data),
7
+ # and until that finished the new body's submenus showed as plain lists;
8
+ # StProject, Scouting and Papiamentu each carried the same layout copy to
9
+ # hide them (html.no-js, a turbo:load re-init, a CSS rule). Now the gem does.
10
+ class TurboMenuTest < InlineFormsIntegrationTestCase
11
+ ROOT = File.expand_path("../..", __dir__)
12
+
13
+ test "the layout starts as no-js" do
14
+ get widgets_path
15
+
16
+ assert_response :success
17
+ assert_match %r{<html lang="en" class="no-js">}, response.body
18
+ # Not data-turbo-eval="false": a Turbo visit from a page whose layout
19
+ # lacks the bundle has to run it.
20
+ assert_match %r{<script src="[^"]*/inline_forms/inline_forms[^"]*\.js"></script>}, response.body
21
+ end
22
+
23
+ test "inline_forms.js boots once and re-initialises Foundation on turbo:load" do
24
+ js = File.read(File.join(ROOT, "app/assets/javascripts/inline_forms/inline_forms.js"))
25
+ boot = js[/if \(!window\.inlineFormsBooted\) \{.*?\n\}\n/m]
26
+
27
+ assert boot, "the DOM-ready and turbo:load wiring sits behind the once-guard"
28
+ turbo_load = boot[/addEventListener\("turbo:load".*?\n \}\);/m]
29
+ assert_includes turbo_load, "window.jQuery(document).foundation();"
30
+ assert_includes turbo_load, "initInlineFormsWidgets(document);"
31
+ end
32
+
33
+ test "uninitialised submenus stay hidden after a visit" do
34
+ scss = File.read(File.join(ROOT, "app/assets/stylesheets/inline_forms/inline_forms.scss"))
35
+
36
+ assert_match(/^\.dropdown\.menu > li > ul\.menu:not\(\.is-dropdown-submenu\) \{\n display: none;\n\}/, scss)
37
+ end
38
+
39
+ # Compiled CSS is checked in the example app (example_app_stylesheet_test).
40
+ test "tippy.css is inlined, not left as a plain CSS @import the browser 404s on" do
41
+ scss = File.read(File.join(ROOT, "app/assets/stylesheets/inline_forms/foundation_and_overrides.scss"))
42
+
43
+ refute_match(/^\s*@import\s+['"][^'"]*tippy\.css['"]/, scss)
44
+ assert_includes scss, "@include meta.load-css('../../../vendor/assets/stylesheets/tippy');"
45
+ end
46
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: inline_forms
3
3
  version: !ruby/object:Gem::Version
4
- version: 8.1.65
4
+ version: 8.1.66
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ace Suares
@@ -334,6 +334,7 @@ files:
334
334
  - test/integration/schema_batch_pipeline_test.rb
335
335
  - test/integration/schema_edit_test.rb
336
336
  - test/integration/schema_preview_test.rb
337
+ - test/integration/turbo_menu_test.rb
337
338
  - test/integration_test_helper.rb
338
339
  - test/plain_text_configuration_test.rb
339
340
  - test/schema_apply_test.rb
@@ -424,6 +425,7 @@ test_files:
424
425
  - test/integration/schema_batch_pipeline_test.rb
425
426
  - test/integration/schema_edit_test.rb
426
427
  - test/integration/schema_preview_test.rb
428
+ - test/integration/turbo_menu_test.rb
427
429
  - test/integration_test_helper.rb
428
430
  - test/plain_text_configuration_test.rb
429
431
  - test/schema_apply_test.rb