alchemy_cms 8.3.1 → 8.3.2

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.
@@ -1,6 +1,6 @@
1
1
  import { RemoteSelect } from "alchemy_admin/components/remote_select"
2
2
 
3
- class AttachmentSelect extends RemoteSelect {
3
+ export class AttachmentSelect extends RemoteSelect {
4
4
  _renderResult(item) {
5
5
  return this._renderListEntry(item)
6
6
  }
@@ -20,7 +20,7 @@ const formatItem = (icon, text, hint) => {
20
20
  `
21
21
  }
22
22
 
23
- class ElementSelect extends HTMLElement {
23
+ export class ElementSelect extends HTMLElement {
24
24
  #select2 = null
25
25
 
26
26
  connectedCallback() {
@@ -1,6 +1,6 @@
1
1
  import { RemoteSelect } from "alchemy_admin/components/remote_select"
2
2
 
3
- class PageSelect extends RemoteSelect {
3
+ export class PageSelect extends RemoteSelect {
4
4
  get pageId() {
5
5
  return this.selection ? JSON.parse(this.selection)["id"] : undefined
6
6
  }
@@ -1,6 +1,6 @@
1
1
  import { setupSelectLocale } from "alchemy_admin/i18n"
2
2
 
3
- class TagsAutocomplete extends HTMLElement {
3
+ export class TagsAutocomplete extends HTMLElement {
4
4
  #select2 = null
5
5
 
6
6
  async connectedCallback() {
@@ -4,7 +4,7 @@ import { currentLocale } from "alchemy_admin/i18n"
4
4
  const DARK_THEME = "alchemy-dark"
5
5
  const LIGHT_THEME = "alchemy"
6
6
 
7
- class Tinymce extends HTMLElement {
7
+ export class Tinymce extends HTMLElement {
8
8
  #min_height = null
9
9
 
10
10
  /**
@@ -53,6 +53,11 @@ document.addEventListener("turbo:load", Initializer)
53
53
 
54
54
  // Public API for extensions
55
55
  export { RemoteSelect } from "alchemy_admin/components/remote_select"
56
+ export { PageSelect } from "alchemy_admin/components/page_select"
57
+ export { AttachmentSelect } from "alchemy_admin/components/attachment_select"
58
+ export { ElementSelect } from "alchemy_admin/components/element_select"
59
+ export { TagsAutocomplete } from "alchemy_admin/components/tags_autocomplete"
60
+ export { Tinymce } from "alchemy_admin/components/tinymce"
56
61
  export { on } from "alchemy_admin/utils/events"
57
62
 
58
63
  // Page-specific modules - bundled to avoid dual-loading
@@ -42,7 +42,11 @@ module Alchemy
42
42
  has_many :related_elements, through: :related_ingredients, source: :element
43
43
  has_many :related_pages, through: :related_elements, source: :page
44
44
 
45
- after_touch :touch_related_ingredients, if: -> { related_ingredients.exists? }
45
+ # Fires on both updates and touches (touch flags the update callback),
46
+ # so element caches are invalidated whenever the resource changes.
47
+ # Deferring to after_commit avoids enqueuing on rollback and coalesces
48
+ # repeated touches within a transaction into a single job.
49
+ after_commit :touch_related_ingredients, on: :update, if: -> { related_ingredients.exists? }
46
50
  end
47
51
 
48
52
  # Returns true if object is not assigned to any ingredient.
@@ -65,6 +65,9 @@
65
65
  margin: 0;
66
66
  line-height: 21px;
67
67
  max-width: 90%;
68
+ overflow-x: hidden;
69
+ text-overflow: ellipsis;
70
+ white-space: nowrap;
68
71
  }
69
72
 
70
73
  > input {
@@ -151,10 +154,13 @@
151
154
  background: var(--tag-background-color);
152
155
  border: 0 none;
153
156
  border-radius: var(--border-radius_medium);
157
+ max-width: unset;
158
+ white-space: normal;
154
159
 
155
160
  .remove {
156
161
  display: inline-flex;
157
162
  align-items: center;
163
+ align-self: start;
158
164
  padding: 0 var(--spacing-0);
159
165
  color: var(--icon-color);
160
166
  text-decoration: none;
@@ -20,6 +20,8 @@ ul.list {
20
20
  margin-bottom: 0;
21
21
 
22
22
  &.list-header {
23
+ display: flex;
24
+ justify-content: space-between;
23
25
  overflow: hidden;
24
26
  background-color: transparent;
25
27
  font-weight: bold;
@@ -45,17 +47,15 @@ ul.list {
45
47
  display: inline-flex;
46
48
  overflow: hidden;
47
49
  text-overflow: ellipsis;
48
- vertical-align: middle;
50
+ align-items: center;
49
51
  }
50
52
 
51
53
  .list-primary {
52
- float: left;
53
54
  max-width: 65%;
54
55
  }
55
56
 
56
57
  .list-secondary,
57
58
  .right {
58
- float: right;
59
59
  text-align: left;
60
60
  width: 30%;
61
61
  color: var(--color-grey_dark);
@@ -151,7 +151,7 @@
151
151
  <select is="alchemy-select" class="full_width" data-allow-clear placeholder="Please choose">
152
152
  <option value="1">Option 1</option>
153
153
  <option value="2">Option 2</option>
154
- <option value="3">Option 3</option>
154
+ <option value="3">Option 3 with a very long name that exceeds the max-width very easily.</option>
155
155
  <option value="4">Option 4</option>
156
156
  <option value="5">Option 5</option>
157
157
  <option value="6">Option 6</option>
@@ -164,7 +164,7 @@
164
164
  <select is="alchemy-select" multiple class="full_width" placeholder="Pick Options" data-allow-clear>
165
165
  <option value="1">Option 1</option>
166
166
  <option value="2">Option 2</option>
167
- <option value="3">Option 3</option>
167
+ <option value="3">Option 3 with a very long name that exceeds the max-width very easily.</option>
168
168
  <option value="4">Option 4</option>
169
169
  <option value="5">Option 5</option>
170
170
  <option value="6">Option 6</option>
data/config/importmap.rb CHANGED
@@ -16,6 +16,11 @@ pin "tom-select", to: "tom-select.min.js", preload: true
16
16
 
17
17
  pin "alchemy_admin", to: "alchemy/alchemy_admin.min.js", preload: true
18
18
  pin "alchemy_admin/components/remote_select", to: "alchemy/alchemy_admin.min.js"
19
+ pin "alchemy_admin/components/page_select", to: "alchemy/alchemy_admin.min.js"
20
+ pin "alchemy_admin/components/attachment_select", to: "alchemy/alchemy_admin.min.js"
21
+ pin "alchemy_admin/components/element_select", to: "alchemy/alchemy_admin.min.js"
22
+ pin "alchemy_admin/components/tags_autocomplete", to: "alchemy/alchemy_admin.min.js"
23
+ pin "alchemy_admin/components/tinymce", to: "alchemy/alchemy_admin.min.js"
19
24
  pin "alchemy_admin/image_cropper", to: "alchemy/alchemy_admin.min.js"
20
25
  pin "alchemy_admin/image_overlay", to: "alchemy/alchemy_admin.min.js"
21
26
  pin "alchemy_admin/picture_selector", to: "alchemy/alchemy_admin.min.js"
@@ -3,16 +3,21 @@ RSpec.shared_examples_for "a relatable resource" do |args|
3
3
  it { is_expected.to have_many(:related_elements).through(:related_ingredients) }
4
4
  it { is_expected.to have_many(:related_pages).through(:related_elements) }
5
5
 
6
+ let(:resource_factory_name) { args[:resource_factory_name] || "alchemy_#{args[:resource_name]}" }
7
+ let(:ingredient_factory_name) { args[:ingredient_factory_name] || "alchemy_ingredient_#{args[:ingredient_type]}" }
8
+ let(:update_attribute) { args[:update_attribute] || :name }
9
+
6
10
  describe ".deletable" do
7
11
  subject { described_class.deletable }
8
12
 
9
- let!(:assigned_resource) { create(:"alchemy_#{args[:resource_name]}") }
10
- let!(:unassigned_resource) { create(:"alchemy_#{args[:resource_name]}") }
11
- let!(:ingredient1) { create(:"alchemy_ingredient_#{args[:ingredient_type]}", related_object: assigned_resource) }
12
- let!(:ingredient2) { create(:"alchemy_ingredient_#{args[:ingredient_type]}", related_object: nil) }
13
+ let!(:assigned_resource) { create(resource_factory_name) }
14
+ let!(:unassigned_resource) { create(resource_factory_name) }
15
+ let!(:ingredient1) { create(ingredient_factory_name, related_object: assigned_resource) }
16
+ let!(:ingredient2) { create(ingredient_factory_name, related_object: nil) }
13
17
 
14
18
  it "should return all records that are not assigned to an ingredient" do
15
- is_expected.to eq [unassigned_resource]
19
+ is_expected.to include(unassigned_resource)
20
+ is_expected.to_not include(assigned_resource)
16
21
  end
17
22
  end
18
23
 
@@ -20,9 +25,9 @@ RSpec.shared_examples_for "a relatable resource" do |args|
20
25
  subject { resource.related_ingredients }
21
26
 
22
27
  context "with other related resources with same id" do
23
- let!(:resource) { create(:"alchemy_#{args[:resource_name]}") }
24
- let!(:ingredient1) { create(:"alchemy_ingredient_#{args[:ingredient_type]}", related_object: resource) }
25
- let!(:ingredient2) { create(:"alchemy_ingredient_#{args[:ingredient_type]}", related_object_type: "Event", related_object_id: resource.id) }
28
+ let!(:resource) { create(resource_factory_name) }
29
+ let!(:ingredient1) { create(ingredient_factory_name, related_object: resource) }
30
+ let!(:ingredient2) { create(ingredient_factory_name, related_object_type: "Event", related_object_id: resource.id) }
26
31
 
27
32
  it "are not included" do
28
33
  is_expected.to eq [ingredient1]
@@ -30,9 +35,9 @@ RSpec.shared_examples_for "a relatable resource" do |args|
30
35
  end
31
36
 
32
37
  context "with other related resources with same type" do
33
- let!(:resource) { create(:"alchemy_#{args[:resource_name]}") }
34
- let!(:ingredient1) { create(:"alchemy_ingredient_#{args[:ingredient_type]}", related_object: resource) }
35
- let!(:ingredient2) { create(:"alchemy_ingredient_#{args[:ingredient_type]}", related_object_type: described_class) }
38
+ let!(:resource) { create(resource_factory_name) }
39
+ let!(:ingredient1) { create(ingredient_factory_name, related_object: resource) }
40
+ let!(:ingredient2) { create(ingredient_factory_name, related_object_type: described_class) }
36
41
 
37
42
  it "are not included" do
38
43
  is_expected.to eq [ingredient1]
@@ -41,12 +46,12 @@ RSpec.shared_examples_for "a relatable resource" do |args|
41
46
  end
42
47
 
43
48
  describe "#deletable?" do
44
- let(:resource) { create(:"alchemy_#{args[:resource_name]}") }
49
+ let(:resource) { create(resource_factory_name) }
45
50
 
46
51
  subject { resource.deletable? }
47
52
 
48
53
  context "if related to ingredient" do
49
- let!(:ingredient) { create(:"alchemy_ingredient_#{args[:ingredient_type]}", related_object: resource) }
54
+ let!(:ingredient) { create(ingredient_factory_name, related_object: resource) }
50
55
 
51
56
  it { is_expected.to be(false) }
52
57
  end
@@ -56,11 +61,11 @@ RSpec.shared_examples_for "a relatable resource" do |args|
56
61
  end
57
62
  end
58
63
 
59
- describe "after_touch" do
60
- let(:related_object) { create(:"alchemy_#{args[:resource_name]}") }
64
+ describe "after_commit on touch" do
65
+ let(:related_object) { create(resource_factory_name) }
61
66
 
62
67
  context "when related ingredients exist" do
63
- let!(:ingredient) { create(:"alchemy_ingredient_#{args[:ingredient_type]}", related_object:) }
68
+ let!(:ingredient) { create(ingredient_factory_name, related_object:) }
64
69
 
65
70
  it "enqueues InvalidateElementsCacheJob" do
66
71
  expect {
@@ -75,4 +80,24 @@ RSpec.shared_examples_for "a relatable resource" do |args|
75
80
  end
76
81
  end
77
82
  end
83
+
84
+ describe "after_commit on update" do
85
+ let(:related_object) { create(resource_factory_name) }
86
+
87
+ context "when related ingredients exist" do
88
+ let!(:ingredient) { create(ingredient_factory_name, related_object:) }
89
+
90
+ it "enqueues InvalidateElementsCacheJob" do
91
+ expect {
92
+ related_object.update(update_attribute => "Updated name")
93
+ }.to have_enqueued_job(Alchemy::InvalidateElementsCacheJob).with(described_class.name, related_object.id)
94
+ end
95
+ end
96
+
97
+ context "when no related ingredients exist" do
98
+ it "does not enqueue InvalidateElementsCacheJob" do
99
+ expect { related_object.update(update_attribute => "Updated name") }.to_not have_enqueued_job(Alchemy::InvalidateElementsCacheJob)
100
+ end
101
+ end
102
+ end
78
103
  end
@@ -3,7 +3,7 @@
3
3
  module Alchemy
4
4
  extend self
5
5
 
6
- VERSION = "8.3.1"
6
+ VERSION = "8.3.2"
7
7
 
8
8
  def version
9
9
  VERSION
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: alchemy_cms
3
3
  version: !ruby/object:Gem::Version
4
- version: 8.3.1
4
+ version: 8.3.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Thomas von Deyen