inline_forms 8.1.66 → 8.1.67
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 +4 -4
- data/CHANGELOG.md +30 -0
- data/app/assets/javascripts/inline_forms/inline_forms.js +28 -29
- data/app/assets/stylesheets/inline_forms/inline_forms.scss +6 -3
- data/app/helpers/inline_forms_helper.rb +12 -6
- data/app/views/devise/shared/_header_and_errors.html.erb +1 -1
- data/app/views/inline_forms/file_trash/index.html.erb +4 -4
- data/app/views/inline_forms/file_trash_panel.html.erb +3 -3
- data/app/views/layouts/inline_forms.html.erb +15 -14
- data/lib/inline_forms/searchable.rb +12 -7
- data/lib/inline_forms/version.rb +1 -1
- data/lib/inline_forms.rb +24 -1
- data/lib/locales/inline_forms_titles.rb +16 -0
- data/test/integration/locale_test.rb +39 -9
- data/test/integration/model_top_bar_test.rb +2 -0
- data/test/integration/turbo_menu_test.rb +33 -19
- metadata +2 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: faafc3188dc88f06ee65cc379452eb7dde62753fe63b488b0250841c4cdb641d
|
|
4
|
+
data.tar.gz: 880f68f083664c8bf6de77fb522577365c43069523da92ca42a01bdd2fa7378a
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: e378f136a965b95f21f0e09e55776b1019eb3fa8a3ada7c1117ab101f2f2a117d654ebfe1b3cbf2c02b01cfd01c7e2096de3cf3d00ffa1d73b6c5474b0b81c3d
|
|
7
|
+
data.tar.gz: 86d6a6d5553a2b98227a3367b0f8cbdc703646bfa01b5a5f55a59dcf798a1c9bf029a0dfcfe7506d2a18e0eaf960155743266e146e4b9ab62d5678ac45b1bf51
|
data/CHANGELOG.md
CHANGED
|
@@ -8,6 +8,36 @@ All notable changes to this project are documented in this file.
|
|
|
8
8
|
|
|
9
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
10
|
|
|
11
|
+
## [8.1.67] - 2026-11-06
|
|
12
|
+
|
|
13
|
+
Fixes from a code review of 8.1.66.
|
|
14
|
+
|
|
15
|
+
### Fixed
|
|
16
|
+
|
|
17
|
+
- **Version and file trash pages no longer crash in a locale without `time.formats.inline_forms`.** 8.1.66 defined the format in en and nl only, and a bare `l(time, format: :inline_forms)` raises `I18n::MissingTranslationData` in any other locale when the host has no i18n fallbacks (rails-i18n's `:short`, used before, exists in every locale). The versions list, the file entries and the file trash now go through `inline_forms_time_text`, which passes the same format (`InlineForms::TIME_FORMAT`) as the default; the month names still come from the locale.
|
|
18
|
+
- **No more document listeners piling up on Turbo visits.** 8.1.66's once-guard in `inline_forms.js` left the `turbo:frame-load` listener and the `.translation_missing` mouseover handler outside it, so a layout that re-runs the bundle added another of each per visit (after 10 visits every frame load rebuilt each tooltip 11 times). Both are inside the guard now (the mouseover handler is a plain DOM listener).
|
|
19
|
+
- **Foundation and the widgets initialise once per page.** The first page ran `foundation()` and `initInlineFormsWidgets` from both the ready event and `turbo:load`, which destroyed and rebuilt every tooltip; one boot function now runs once per `<body>`.
|
|
20
|
+
- **The submenu rule no longer hides drilldown submenus.** 8.1.66 hid every `.dropdown.menu` submenu without `is-dropdown-submenu`, which beats Foundation's own `.is-drilldown-submenu` rules for a `data-responsive-menu` on small screens. It now applies to `[data-dropdown-menu]` menus only.
|
|
21
|
+
- **The title keys exist again, and say the application's name.** 8.1.66 removed `inline_forms.general.application_title`, `inline_forms.devise.title_for_devise` and `inline_forms.devise.welcome`, so a host view calling one bare (Papiamentu has several) showed "translation missing" in any locale it did not translate and that had no fallbacks. In en and nl they are now lambdas (`lib/locales/inline_forms_titles.rb`) that return `InlineForms.application_title`; a host's own translation still wins (its locale files load after the gem's; the example app checks the order).
|
|
22
|
+
- **One source for the application's name.** `InlineForms.application_title` is the host's `application_name` translation, else its `ApplicationHelper#application_name` (8.1.66 skipped that and used the titleized module name, so an app could say "MyApp" in en and "My App" in nl), else the Rails module name. `inline_forms_application_title`, the admin layout's `<title>` and the Devise header use it.
|
|
23
|
+
|
|
24
|
+
### Changed
|
|
25
|
+
|
|
26
|
+
- **The admin layout loads Popper, Tippy and `inline_forms.js` in `<head>`.** In `<body>` Turbo Drive re-ran them on every visit with a fresh jQuery, the cause of 8.1.66's menu flash; Turbo runs head scripts once and appends missing ones in order on a visit from another layout. `no-js`, the once-guard and the submenu rule stay for host layouts that still load the bundle in `<body>`.
|
|
27
|
+
- **Installer: the generated `config/locales/inline_forms_local.en.yml` has only `application_name`.** It also wrote the three title keys, in en only.
|
|
28
|
+
- **`inline_forms_search_on` builds its clause once,** on the first search (`inline_forms_search_clause`), instead of on every search.
|
|
29
|
+
|
|
30
|
+
### Tests
|
|
31
|
+
|
|
32
|
+
- `turbo_menu_test`: the scripts in `<head>` in order, no document listener outside the guard, one boot per body, the `[data-dropdown-menu]` rule.
|
|
33
|
+
- `locale_test`: the title keys resolve to the app's name in en and nl (translation, then the helper), a host translation wins, timestamps render in a locale without the gem's format.
|
|
34
|
+
- `model_top_bar_test`: the search clause is built once.
|
|
35
|
+
- Example app: `example_app_layout_title_test` checks the host's locale files load after the gem's title keys and that the keys say the app name in en and nl; `example_app_stylesheet_test` expects the scoped rule.
|
|
36
|
+
|
|
37
|
+
### Lockstep
|
|
38
|
+
|
|
39
|
+
- validation_hints 8.1.67, inline_forms_installer 8.1.67, inline_forms_schema_edit 8.1.67.
|
|
40
|
+
|
|
11
41
|
## [8.1.66] - 2026-11-06
|
|
12
42
|
|
|
13
43
|
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,28 +14,41 @@
|
|
|
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
|
-
//
|
|
18
|
-
// in <
|
|
19
|
-
//
|
|
20
|
-
//
|
|
17
|
+
// Listeners are added once per page load. The gem's layouts load this bundle
|
|
18
|
+
// in <head>, which Turbo Drive keeps across visits, but a host layout may
|
|
19
|
+
// still have it in <body>, which Turbo re-runs on every visit (each run with a
|
|
20
|
+
// fresh jQuery): without the guard every run would add another set.
|
|
21
21
|
if (!window.inlineFormsBooted) {
|
|
22
22
|
window.inlineFormsBooted = true;
|
|
23
23
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
// is looked up at call time.
|
|
34
|
-
document.addEventListener("turbo:load", function() {
|
|
24
|
+
// Foundation and the widgets, once per <body>. Turbo Drive replaces <body>
|
|
25
|
+
// on a visit, and the new top bar's DropdownMenu needs initialising or its
|
|
26
|
+
// submenus show as plain lists. The first page gets both the ready event
|
|
27
|
+
// and turbo:load (and a layout without Turbo only the first), so the body
|
|
28
|
+
// check keeps it to one init. jQuery is looked up at call time.
|
|
29
|
+
var inlineFormsBootedBody = null;
|
|
30
|
+
var bootInlineFormsPage = function() {
|
|
31
|
+
if (!document.body || document.body === inlineFormsBootedBody) { return; }
|
|
32
|
+
inlineFormsBootedBody = document.body;
|
|
35
33
|
if (window.jQuery && window.jQuery.fn.foundation) {
|
|
36
34
|
window.jQuery(document).foundation();
|
|
37
35
|
}
|
|
38
36
|
initInlineFormsWidgets(document);
|
|
37
|
+
};
|
|
38
|
+
$(bootInlineFormsPage);
|
|
39
|
+
document.addEventListener("turbo:load", bootInlineFormsPage);
|
|
40
|
+
|
|
41
|
+
// Re-bind widgets (tooltips) after Turbo Frame swaps.
|
|
42
|
+
document.addEventListener("turbo:frame-load", function(event) {
|
|
43
|
+
var root = event.target;
|
|
44
|
+
if (!root || !root.querySelectorAll) { return; }
|
|
45
|
+
initInlineFormsWidgets(root);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
// get rid of translation_missing tooltips
|
|
49
|
+
document.addEventListener("mouseover", function(event) {
|
|
50
|
+
var el = event.target.closest && event.target.closest(".translation_missing");
|
|
51
|
+
if (el) { el.setAttribute("title", ""); }
|
|
39
52
|
});
|
|
40
53
|
}
|
|
41
54
|
|
|
@@ -104,17 +117,3 @@ function initValidationHintTooltips(root) {
|
|
|
104
117
|
}
|
|
105
118
|
});
|
|
106
119
|
}
|
|
107
|
-
|
|
108
|
-
// get rid of translation_missing tooltips
|
|
109
|
-
$(document).ready(function() {
|
|
110
|
-
$(this).on('mouseover', '.translation_missing', function() {
|
|
111
|
-
$(this).attr('title', '');
|
|
112
|
-
});
|
|
113
|
-
});
|
|
114
|
-
|
|
115
|
-
// Re-bind widgets (tooltips) after Turbo Frame swaps.
|
|
116
|
-
document.addEventListener("turbo:frame-load", function(event) {
|
|
117
|
-
var root = event.target;
|
|
118
|
-
if (!root || !root.querySelectorAll) { return; }
|
|
119
|
-
initInlineFormsWidgets(root);
|
|
120
|
-
});
|
|
@@ -117,9 +117,12 @@
|
|
|
117
117
|
// marks it `is-dropdown-submenu` (whose own rules then hide it). On the first
|
|
118
118
|
// page `html.no-js` covers that gap (layouts/inline_forms.html.erb), but
|
|
119
119
|
// Foundation removes the class and Turbo Drive never restores it, so after a
|
|
120
|
-
// visit the new body's submenus
|
|
121
|
-
// re-initialised them on turbo:load
|
|
122
|
-
|
|
120
|
+
// visit the new body's submenus could flash open until inline_forms.js
|
|
121
|
+
// re-initialised them on turbo:load (most of all from a host layout that
|
|
122
|
+
// still loads the bundle in <body>). Only for menus marked
|
|
123
|
+
// `data-dropdown-menu`: a `data-responsive-menu` turns its submenus into
|
|
124
|
+
// `is-drilldown-submenu` on small screens, which this rule would hide.
|
|
125
|
+
.dropdown.menu[data-dropdown-menu] > li > ul.menu:not(.is-dropdown-submenu) {
|
|
123
126
|
display: none;
|
|
124
127
|
}
|
|
125
128
|
|
|
@@ -398,15 +398,21 @@ module InlineFormsHelper
|
|
|
398
398
|
# (time.formats.inline_forms; rails-i18n's :short has none, so edits years
|
|
399
399
|
# apart read the same), the exact ISO 8601 time on hover.
|
|
400
400
|
def inline_forms_time(time)
|
|
401
|
-
tag.time(
|
|
401
|
+
tag.time(inline_forms_time_text(time), datetime: time.iso8601, title: time.iso8601)
|
|
402
402
|
end
|
|
403
403
|
|
|
404
|
-
# The
|
|
405
|
-
#
|
|
406
|
-
#
|
|
407
|
-
#
|
|
404
|
+
# The text alone. The gem defines time.formats.inline_forms only in en and
|
|
405
|
+
# nl; in another locale without it (and without i18n fallbacks) a bare
|
|
406
|
+
# l(format: :inline_forms) raises, so the same format is the default. The
|
|
407
|
+
# month names still come from that locale (rails-i18n).
|
|
408
|
+
def inline_forms_time_text(time)
|
|
409
|
+
l(time, format: :inline_forms, default: InlineForms::TIME_FORMAT)
|
|
410
|
+
end
|
|
411
|
+
|
|
412
|
+
# The application's name for the gem's titles and the Devise pages
|
|
413
|
+
# (InlineForms.application_title).
|
|
408
414
|
def inline_forms_application_title
|
|
409
|
-
|
|
415
|
+
InlineForms.application_title
|
|
410
416
|
end
|
|
411
417
|
|
|
412
418
|
def version_modified_by(id)
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<%# Unused by the gem's own devise views; kept for host apps that render it. %>
|
|
2
2
|
<%# Same title source as layouts/inline_forms; Devise 5 dropped %>
|
|
3
3
|
<%# devise_error_messages!, so errors use Devise's own error_messages partial. %>
|
|
4
|
-
<div id="large_title"><%=
|
|
4
|
+
<div id="large_title"><%= inline_forms_application_title %></div>
|
|
5
5
|
<div id="flash_message">
|
|
6
6
|
<% if resource.errors.any? %>
|
|
7
7
|
<%= render "devise/shared/error_messages", resource: resource %>
|
|
@@ -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:
|
|
17
|
+
<%= t("inline_forms.files.global.sweep_last", at: inline_forms_time_text(@last_sweep.ran_at), 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) %> · <%= 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:
|
|
81
|
+
<td><%= t("inline_forms.files.by_on", user: inline_forms_file_user_name(entry.trashed_by_id), at: inline_forms_time_text(entry.trashed_at)) %></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:
|
|
84
|
-
<% elsif entry.state == :purged %><%= t("inline_forms.files.state_purged", user: inline_forms_file_user_name(entry.purged_by_id), at:
|
|
83
|
+
<% if entry.state == :restored %><%= t("inline_forms.files.state_restored", user: inline_forms_file_user_name(entry.restored_by_id), at: inline_forms_time_text(entry.restored_at)) %>
|
|
84
|
+
<% elsif entry.state == :purged %><%= t("inline_forms.files.state_purged", user: inline_forms_file_user_name(entry.purged_by_id), at: inline_forms_time_text(entry.purged_at)) %>
|
|
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) %> ·
|
|
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:
|
|
44
|
+
<%= t("inline_forms.files.by_on", user: inline_forms_file_user_name(entry.trashed_by_id), at: inline_forms_time_text(entry.trashed_at)) %>
|
|
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:
|
|
48
|
+
<%= t("inline_forms.files.state_restored", user: inline_forms_file_user_name(entry.restored_by_id), at: inline_forms_time_text(entry.restored_at)) %>
|
|
49
49
|
<% elsif entry.state == :purged %>
|
|
50
|
-
<%= t("inline_forms.files.state_purged", user: inline_forms_file_user_name(entry.purged_by_id), at:
|
|
50
|
+
<%= t("inline_forms.files.state_purged", user: inline_forms_file_user_name(entry.purged_by_id), at: inline_forms_time_text(entry.purged_at)) %>
|
|
51
51
|
<% elsif entry.state == :expired %>
|
|
52
52
|
<%= t("inline_forms.files.state_expired") %>
|
|
53
53
|
<% else %>
|
|
@@ -1,18 +1,17 @@
|
|
|
1
1
|
<!DOCTYPE html>
|
|
2
2
|
<%# class="no-js": Foundation's CSS hides the top-bar submenus while it is set %>
|
|
3
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
|
|
5
|
-
<%#
|
|
6
|
-
<%#
|
|
4
|
+
<%# plain lists before then. Turbo Drive never puts it back; after a visit %>
|
|
5
|
+
<%# inline_forms.js initialises the new body on turbo:load, and the %>
|
|
6
|
+
<%# `[data-dropdown-menu]` rule in inline_forms.scss covers any gap. %>
|
|
7
7
|
<html lang="<%= I18n.locale %>" class="no-js">
|
|
8
8
|
<head>
|
|
9
9
|
<meta charset="utf-8" />
|
|
10
10
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
11
11
|
<%# A missing translation must not reach <title>: a bare t() returns an %>
|
|
12
12
|
<%# html_safe <span class="translation_missing"> that shows up as markup. %>
|
|
13
|
-
<%#
|
|
14
|
-
|
|
15
|
-
<title><%= t('application_name', default: application_name) %> v<%= InlineForms::VERSION %></title>
|
|
13
|
+
<%# inline_forms_application_title always has a plain-text fallback. %>
|
|
14
|
+
<title><%= inline_forms_application_title %> v<%= InlineForms::VERSION %></title>
|
|
16
15
|
<%= stylesheet_link_tag "application" %>
|
|
17
16
|
<%= stylesheet_link_tag "inline_forms_fonts" %>
|
|
18
17
|
<%= stylesheet_link_tag "inline_forms/inline_forms" %>
|
|
@@ -22,6 +21,16 @@
|
|
|
22
21
|
<%# Publishes the CSP nonce (nothing without a nonce generator) for the %>
|
|
23
22
|
<%# scripts Turbo inserts. %>
|
|
24
23
|
<%= csp_meta_tag %>
|
|
24
|
+
<%# In <head>, so Turbo Drive runs them once: it keeps head scripts across %>
|
|
25
|
+
<%# visits (a body script runs again on every visit, with a fresh jQuery), %>
|
|
26
|
+
<%# and appends them, in order, on a visit from a page that lacks them. %>
|
|
27
|
+
<%# Tippy.js (v6) needs Popper.js as a global before its UMD factory runs %>
|
|
28
|
+
<%# (`tippy-bundle.umd.min` only bundles default themes, not Popper itself). %>
|
|
29
|
+
<%# inline_forms.js initialises Foundation on the ready event and on %>
|
|
30
|
+
<%# turbo:load, once per <body>. %>
|
|
31
|
+
<%= javascript_include_tag 'popper.min' %>
|
|
32
|
+
<%= javascript_include_tag 'tippy-bundle.umd.min' %>
|
|
33
|
+
<%= javascript_include_tag 'inline_forms/inline_forms' %>
|
|
25
34
|
</head>
|
|
26
35
|
|
|
27
36
|
<body class="<%= inline_forms_body_theme_class %>">
|
|
@@ -40,14 +49,6 @@
|
|
|
40
49
|
<div id="outer_container">
|
|
41
50
|
<%= yield %>
|
|
42
51
|
</div>
|
|
43
|
-
<%# Tippy.js (v6) needs Popper.js as a global before its UMD factory runs %>
|
|
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. %>
|
|
48
|
-
<%= javascript_include_tag 'popper.min' %>
|
|
49
|
-
<%= javascript_include_tag 'tippy-bundle.umd.min' %>
|
|
50
|
-
<%= javascript_include_tag 'inline_forms/inline_forms' %>
|
|
51
52
|
<%# Turbo (Hotwire) is loaded as an ES module so it does not collide with %>
|
|
52
53
|
<%# the classic-script Sprockets bundle above. Drive stays at the default %>
|
|
53
54
|
<%# (enabled); inline interactions use `<turbo-frame>` + HTML responses. %>
|
|
@@ -19,16 +19,21 @@ module InlineForms
|
|
|
19
19
|
end
|
|
20
20
|
|
|
21
21
|
@inline_forms_searchable = true
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
# loads (eager loading, assets:precompile without a database).
|
|
22
|
+
@inline_forms_search_columns = cols
|
|
23
|
+
model = self
|
|
25
24
|
scope :inline_forms_search, ->(q) {
|
|
26
|
-
|
|
27
|
-
"#{connection.quote_table_name(table_name)}.#{connection.quote_column_name(c)} LIKE :q"
|
|
28
|
-
}.join(" OR ")
|
|
29
|
-
where(clause, q: "%#{sanitize_sql_like(q.to_s)}%")
|
|
25
|
+
where(model.inline_forms_search_clause, q: "%#{sanitize_sql_like(q.to_s)}%")
|
|
30
26
|
}
|
|
31
27
|
end
|
|
28
|
+
|
|
29
|
+
# Built on the first search, not in inline_forms_search_on: quoting
|
|
30
|
+
# needs a database connection, and the class body runs whenever the
|
|
31
|
+
# model loads (eager loading, assets:precompile without a database).
|
|
32
|
+
def inline_forms_search_clause
|
|
33
|
+
@inline_forms_search_clause ||= @inline_forms_search_columns.map { |c|
|
|
34
|
+
"#{connection.quote_table_name(table_name)}.#{connection.quote_column_name(c)} LIKE :q"
|
|
35
|
+
}.join(" OR ")
|
|
36
|
+
end
|
|
32
37
|
end
|
|
33
38
|
end
|
|
34
39
|
end
|
data/lib/inline_forms/version.rb
CHANGED
data/lib/inline_forms.rb
CHANGED
|
@@ -187,6 +187,28 @@ module InlineForms
|
|
|
187
187
|
# programmatically (generator / GUI); the user edits it to real values after.
|
|
188
188
|
PLACEHOLDER_VALUES = { 1 => "one", 2 => "two" }.freeze
|
|
189
189
|
|
|
190
|
+
# Version and file timestamps: time.formats.inline_forms in the gem's en
|
|
191
|
+
# and nl locales, and the default where a locale lacks it.
|
|
192
|
+
TIME_FORMAT = "%d %b %Y %H:%M"
|
|
193
|
+
|
|
194
|
+
# The application's name for the gem's titles and the Devise pages: the
|
|
195
|
+
# host's `application_name` translation, else its
|
|
196
|
+
# ApplicationHelper#application_name (the installer writes both, and the
|
|
197
|
+
# gem's header partial uses the helper), else its Rails module name. The
|
|
198
|
+
# gem's title keys (inline_forms.general.application_title,
|
|
199
|
+
# inline_forms.devise.title_for_devise and .welcome) resolve to this in en
|
|
200
|
+
# and nl unless the host translates them (lib/locales/inline_forms_titles.rb).
|
|
201
|
+
def self.application_title
|
|
202
|
+
I18n.t("application_name", default: application_name_from_helper || Rails.application.class.module_parent_name)
|
|
203
|
+
end
|
|
204
|
+
|
|
205
|
+
def self.application_name_from_helper
|
|
206
|
+
return unless defined?(::ApplicationController)
|
|
207
|
+
|
|
208
|
+
helpers = ::ApplicationController.helpers
|
|
209
|
+
helpers.application_name if helpers.respond_to?(:application_name)
|
|
210
|
+
end
|
|
211
|
+
|
|
190
212
|
def self.plain_text_form_element?(form_element)
|
|
191
213
|
PLAIN_TEXT_FORM_ELEMENTS.include?(form_element.to_sym)
|
|
192
214
|
rescue NoMethodError
|
|
@@ -408,7 +430,8 @@ module InlineForms
|
|
|
408
430
|
end
|
|
409
431
|
end
|
|
410
432
|
|
|
411
|
-
|
|
433
|
+
# .rb too: inline_forms_titles.rb holds the title keys as lambdas.
|
|
434
|
+
I18n.load_path << Dir[File.join(File.expand_path(File.dirname(__FILE__) + "/locales"), "*.{yml,rb}")]
|
|
412
435
|
I18n.load_path.flatten!
|
|
413
436
|
end
|
|
414
437
|
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# The gem's title keys, resolved to the application's name
|
|
4
|
+
# (InlineForms.application_title) instead of a placeholder. Lambdas, so a
|
|
5
|
+
# host view that calls a key bare gets the real name too. A host translation
|
|
6
|
+
# of the same key still wins: the host's locale files load after the gem's.
|
|
7
|
+
title = ->(_key, **_options) { InlineForms.application_title }
|
|
8
|
+
welcome = ->(_key, **_options) { I18n.t("inline_forms.devise.welcome_to", name: InlineForms.application_title) }
|
|
9
|
+
keys = {
|
|
10
|
+
inline_forms: {
|
|
11
|
+
general: { application_title: title },
|
|
12
|
+
devise: { title_for_devise: title, welcome: welcome }
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
{ en: keys, nl: keys }
|
|
@@ -143,26 +143,56 @@ class LocaleTest < InlineFormsIntegrationTestCase
|
|
|
143
143
|
|
|
144
144
|
# The gem shipped "Application Title" / "Title for Devise" / "Welcome for
|
|
145
145
|
# Devise" in en and nl; any locale the host did not override showed them.
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
refute I18n.exists?(key, locale), "#{locale}.#{key} is a placeholder; the views fall back to application_name"
|
|
150
|
-
end
|
|
151
|
-
end
|
|
152
|
-
|
|
146
|
+
# The keys now resolve to the application's name, also when a host view
|
|
147
|
+
# calls them bare.
|
|
148
|
+
test "the gem's title keys resolve to the application's name" do
|
|
153
149
|
get widgets_path
|
|
154
150
|
I18n.backend.store_translations(:nl, application_name: "Werkplaats")
|
|
155
151
|
strictly(:nl) do
|
|
152
|
+
assert_equal "Werkplaats", I18n.t("inline_forms.general.application_title")
|
|
153
|
+
assert_equal "Werkplaats", I18n.t("inline_forms.devise.title_for_devise")
|
|
154
|
+
assert_equal "Welkom bij Werkplaats!", I18n.t("inline_forms.devise.welcome")
|
|
156
155
|
devise = controller.render_to_string(template: "stats/show", layout: "devise")
|
|
157
156
|
assert_includes devise, "Werkplaats v#{InlineForms::VERSION}"
|
|
158
157
|
top_bar = controller.render_to_string(partial: "devise/sessions/top-bar")
|
|
159
158
|
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
159
|
end
|
|
163
160
|
I18n.with_locale(:en) do
|
|
161
|
+
# No en application_name translation here: the host's
|
|
162
|
+
# ApplicationHelper#application_name.
|
|
163
|
+
assert_equal "Dummy", I18n.t("inline_forms.general.application_title")
|
|
164
|
+
assert_equal "Welcome to Dummy!", I18n.t("inline_forms.devise.welcome")
|
|
164
165
|
assert_includes controller.render_to_string(template: "stats/show", layout: "application"), "Dummy v#{InlineForms::VERSION}"
|
|
165
166
|
end
|
|
167
|
+
ensure
|
|
168
|
+
I18n.backend.reload!
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
test "a host translation of a title key wins" do
|
|
172
|
+
I18n.t("inline_forms.general.application_title") # loads the gem's files first, as a host app does
|
|
173
|
+
I18n.backend.store_translations(:nl, inline_forms: { general: { application_title: "Banko" } })
|
|
174
|
+
|
|
175
|
+
assert_equal "Banko", I18n.t("inline_forms.general.application_title", locale: :nl)
|
|
176
|
+
ensure
|
|
177
|
+
I18n.backend.reload!
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
# time.formats.inline_forms is defined in en and nl only; a bare
|
|
181
|
+
# l(format: :inline_forms) raises in any other locale without fallbacks.
|
|
182
|
+
test "version and trash timestamps render in a locale without the gem's format" do
|
|
183
|
+
original = I18n.available_locales
|
|
184
|
+
I18n.available_locales = original + [ :de ]
|
|
185
|
+
months = %w[Jan Feb Mär Apr Mai Jun Jul Aug Sep Okt Nov Dez]
|
|
186
|
+
I18n.backend.store_translations(:de, date: { abbr_month_names: [ nil, *months ] })
|
|
187
|
+
time = Time.zone.local(2019, 3, 5, 14, 7)
|
|
188
|
+
|
|
189
|
+
I18n.with_locale(:de) do
|
|
190
|
+
assert_raises(I18n::MissingTranslationData) { I18n.l(time, format: :inline_forms, raise: true) }
|
|
191
|
+
assert_equal "05 Mär 2019 14:07", ApplicationController.helpers.send(:inline_forms_time_text, time)
|
|
192
|
+
end
|
|
193
|
+
ensure
|
|
194
|
+
I18n.available_locales = original
|
|
195
|
+
I18n.backend.reload!
|
|
166
196
|
end
|
|
167
197
|
|
|
168
198
|
test "the versions list in en keeps its English labels" do
|
|
@@ -79,5 +79,7 @@ class ModelTopBarTest < InlineFormsIntegrationTestCase
|
|
|
79
79
|
%i[connection lease_connection with_connection].each { |m| model.singleton_class.remove_method(m) }
|
|
80
80
|
Widget.create!(name: "Gamma")
|
|
81
81
|
assert_equal [ "Gamma" ], model.inline_forms_search("amm").pluck(:name)
|
|
82
|
+
# Built once, on the first search.
|
|
83
|
+
assert_same model.inline_forms_search_clause, model.inline_forms_search_clause
|
|
82
84
|
end
|
|
83
85
|
end
|
|
@@ -2,38 +2,52 @@
|
|
|
2
2
|
|
|
3
3
|
require_relative "../integration_test_helper"
|
|
4
4
|
|
|
5
|
-
# Top-bar submenus after a Turbo Drive visit.
|
|
6
|
-
#
|
|
7
|
-
# and until that finished the new body's submenus showed as plain
|
|
8
|
-
# StProject, Scouting and Papiamentu each carried the same layout copy
|
|
9
|
-
# hide them (html.no-js, a turbo:load re-init, a CSS rule). Now the gem
|
|
5
|
+
# Top-bar submenus after a Turbo Drive visit. The bundle sat in <body>, so
|
|
6
|
+
# Turbo re-ran it on every visit (a fresh jQuery, its own Foundation plugin
|
|
7
|
+
# data), and until that finished the new body's submenus showed as plain
|
|
8
|
+
# lists; StProject, Scouting and Papiamentu each carried the same layout copy
|
|
9
|
+
# to hide them (html.no-js, a turbo:load re-init, a CSS rule). Now the gem
|
|
10
|
+
# loads the bundle in <head>, which Turbo runs once.
|
|
10
11
|
class TurboMenuTest < InlineFormsIntegrationTestCase
|
|
11
12
|
ROOT = File.expand_path("../..", __dir__)
|
|
12
13
|
|
|
13
|
-
|
|
14
|
+
def boot_block
|
|
15
|
+
js = File.read(File.join(ROOT, "app/assets/javascripts/inline_forms/inline_forms.js"))
|
|
16
|
+
js[/^if \(!window\.inlineFormsBooted\) \{.*?\n\}\n/m]
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
test "the layout starts as no-js and loads the bundle in head" do
|
|
14
20
|
get widgets_path
|
|
15
21
|
|
|
16
22
|
assert_response :success
|
|
17
23
|
assert_match %r{<html lang="en" class="no-js">}, response.body
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
24
|
+
head = response.body[%r{<head>.*</head>}m]
|
|
25
|
+
scripts = %w[popper.min tippy-bundle.umd.min inline_forms/inline_forms].map { |name|
|
|
26
|
+
head.index(%r{<script src="[^"]*/#{Regexp.escape(name)}[^"]*\.js"></script>})
|
|
27
|
+
}
|
|
28
|
+
assert scripts.all?, "popper, tippy and inline_forms.js are in <head>"
|
|
29
|
+
assert_equal scripts.sort, scripts, "in that order"
|
|
30
|
+
refute_match %r{<body.*inline_forms/inline_forms[^"]*\.js}m, response.body
|
|
21
31
|
end
|
|
22
32
|
|
|
23
|
-
test "inline_forms.js
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
33
|
+
test "inline_forms.js wires every document listener once, and boots once per body" do
|
|
34
|
+
boot = boot_block
|
|
35
|
+
|
|
36
|
+
assert boot, "the listeners sit behind the once-guard"
|
|
37
|
+
rest = File.read(File.join(ROOT, "app/assets/javascripts/inline_forms/inline_forms.js")).sub(boot, "")
|
|
38
|
+
refute_match(/addEventListener|\$\(document\)|\.ready\(/, rest.gsub(%r{^\s*//.*$}, "").gsub(/el\.addEventListener\("input"/, ""),
|
|
39
|
+
"no document listener outside the guard: a host layout with the bundle in <body> re-runs it on every visit")
|
|
40
|
+
%w[turbo:load turbo:frame-load mouseover].each do |event|
|
|
41
|
+
assert_includes boot, "document.addEventListener(\"#{event}\""
|
|
42
|
+
end
|
|
43
|
+
assert_includes boot, "document.body === inlineFormsBootedBody"
|
|
44
|
+
assert_includes boot, "window.jQuery(document).foundation();"
|
|
31
45
|
end
|
|
32
46
|
|
|
33
|
-
test "uninitialised submenus stay hidden
|
|
47
|
+
test "uninitialised dropdown submenus stay hidden, drilldown menus are left alone" do
|
|
34
48
|
scss = File.read(File.join(ROOT, "app/assets/stylesheets/inline_forms/inline_forms.scss"))
|
|
35
49
|
|
|
36
|
-
assert_match(/^\.dropdown\.menu > li > ul\.menu:not\(\.is-dropdown-submenu\) \{\n display: none;\n\}/, scss)
|
|
50
|
+
assert_match(/^\.dropdown\.menu\[data-dropdown-menu\] > li > ul\.menu:not\(\.is-dropdown-submenu\) \{\n display: none;\n\}/, scss)
|
|
37
51
|
end
|
|
38
52
|
|
|
39
53
|
# Compiled CSS is checked in the example app (example_app_stylesheet_test).
|
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.
|
|
4
|
+
version: 8.1.67
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Ace Suares
|
|
@@ -273,6 +273,7 @@ files:
|
|
|
273
273
|
- lib/inline_forms/version.rb
|
|
274
274
|
- lib/locales/inline_forms.en.yml
|
|
275
275
|
- lib/locales/inline_forms.nl.yml
|
|
276
|
+
- lib/locales/inline_forms_titles.rb
|
|
276
277
|
- lib/otherstuff/add_roles.sql
|
|
277
278
|
- lib/otherstuff/mkrole
|
|
278
279
|
- lib/otherstuff/roles_users.sql
|