Merge pull request #13531 from rioug/better-stimulus-name-for-component

Load component stimulus controller with a shorter name
This commit is contained in:
Maikel
2025-09-17 17:10:37 +10:00
committed by GitHub
14 changed files with 58 additions and 38 deletions

View File

@@ -1,4 +1,5 @@
// This controller will be called "example-component--example", ie "component-subdirectory--js-file-name"
// This controller will be called "example", ie "js-file-name" minus the "_controller.js"
// see controller/index.js for more info
import { Controller } from "stimulus";
export default class extends Controller {}

View File

@@ -1,19 +1,19 @@
%div{ "data-controller": "tag-list-input-component--tag-list-input" }
%div{ "data-controller": "tag-list-input" }
.tags-input
.tags
- # We use display:none instead of hidden field, so changes to the value can be picked up by the bulkFormController
= f.text_field method.to_sym, value: tags.join(","), "data-tag-list-input-component--tag-list-input-target": "tagList", "style": "display: none"
%ul.tag-list{"data-tag-list-input-component--tag-list-input-target": "list"}
%template{"data-tag-list-input-component--tag-list-input-target": "template"}
= f.text_field method.to_sym, value: tags.join(","), "data-tag-list-input-target": "tagList", "style": "display: none"
%ul.tag-list{"data-tag-list-input-target": "list"}
%template{"data-tag-list-input-target": "template"}
%li.tag-item
.tag-template
%span
%a.remove-button{ "data-action": "click->tag-list-input-component--tag-list-input#removeTag" }
%a.remove-button{ "data-action": "click->tag-list-input#removeTag" }
×
- tags.each do |tag|
%li.tag-item
.tag-template
%span=tag
%a.remove-button{ "data-action": "click->tag-list-input-component--tag-list-input#removeTag" }
%a.remove-button{ "data-action": "click->tag-list-input#removeTag" }
×
= text_field_tag "variant_add_tag_#{f.object.id}".to_sym, nil, class: "input", placeholder: placeholder, "data-action": "keydown.enter->tag-list-input-component--tag-list-input#addTag keyup->tag-list-input-component--tag-list-input#filterInput", "data-tag-list-input-component--tag-list-input-target": "newTag", **aria_label_option
= text_field_tag "variant_add_tag_#{f.object.id}".to_sym, nil, class: "input", placeholder: placeholder, "data-action": "keydown.enter->tag-list-input#addTag keyup->tag-list-input#filterInput", "data-tag-list-input-target": "newTag", **aria_label_option

View File

@@ -1,6 +0,0 @@
# frozen_string_literal: true
module VerticalEllipsisMenu
class Component < ViewComponent::Base
end
end

View File

@@ -0,0 +1,4 @@
# frozen_string_literal: true
class VerticalEllipsisMenuComponent < ViewComponent::Base
end

View File

@@ -1,4 +1,4 @@
.vertical-ellipsis-menu{ "data-controller": "vertical-ellipsis-menu--component" }
%i.fa.fa-ellipsis-v{ "data-action": "click->vertical-ellipsis-menu--component#toggle" }
.vertical-ellipsis-menu-content{ "data-vertical-ellipsis-menu--component-target": "content" }
.vertical-ellipsis-menu{ "data-controller": "vertical-ellipsis-menu" }
%i.fa.fa-ellipsis-v{ "data-action": "click->vertical-ellipsis-menu#toggle" }
.vertical-ellipsis-menu-content{ "data-vertical-ellipsis-menu-target": "content" }
= content

View File

@@ -24,7 +24,7 @@
%td.col-inherits_properties.align-left
.content= product.inherits_properties ? 'YES' : 'NO' #TODO: consider using https://github.com/RST-J/human_attribute_values, else use I18n.t (also below)
%td.align-right
= render(VerticalEllipsisMenu::Component.new) do
= render(VerticalEllipsisMenuComponent.new) do
= link_to t('admin.products_page.actions.edit'), edit_admin_product_path(product), 'data-turbo': false
= link_to t('admin.products_page.actions.clone'), admin_clone_product_path(product), 'data-turbo-method': :post
%a{ "data-controller": "modal-link", "data-action": "click->modal-link#setModalDataSetOnConfirm click->modal-link#open",

View File

@@ -84,7 +84,7 @@
%td.col-inherits_properties.align-left
-# empty
%td.align-right
= render(VerticalEllipsisMenu::Component.new) do
= render(VerticalEllipsisMenuComponent.new) do
- if variant.persisted?
= link_to t('admin.products_page.actions.edit'), edit_admin_product_variant_path(variant.product, variant)
- if variant.product.variants.size > 1

View File

@@ -6,14 +6,37 @@ import StimulusReflex from "stimulus_reflex";
import consumer from "../channels/consumer";
import controller from "../controllers/application_controller";
import CableReady from "cable_ready";
import RailsNestedForm from '@stimulus-components/rails-nested-form/dist/stimulus-rails-nested-form.umd.js' // the default module entry point is broken
import RailsNestedForm from "@stimulus-components/rails-nested-form/dist/stimulus-rails-nested-form.umd.js"; // the default module entry point is broken
const application = Application.start();
const context = require.context("controllers", true, /_controller\.js$/);
const contextComponents = require.context("../../components", true, /_controller\.js$/);
application.load(definitionsFromContext(context));
application.load(definitionsFromContext(context).concat(definitionsFromContext(contextComponents)));
application.register('nested-form', RailsNestedForm);
// Load component controller, but generate a shorter controller name than "definitionsFromContext" would
// - for controller in a component subdirectory, get rid of the component folder and use
// the controller name, ie:
// ./tag_rule_group_form_component/tag_rule_group_form_controller.js -> tag-rule-group-form
// - for controller that don't match the pattern above, replace "_" by "-" and "/" by "--", ie:
// ./vertical_ellipsis_menu/component_controller.js -> vertical-ellipsis-menu--component
//
const contextComponents = require.context("../../components", true, /_controller\.js$/);
contextComponents.keys().forEach((path) => {
const module = contextComponents(path);
// Check whether a module has the default export defined
if (!module.default) return;
const identifier = path
.replace(/^\.\//, "")
.replace(/^\w+_component\//, "")
.replace(/_controller\.js$/, "")
.replace(/\//g, "--")
.replace(/_/g, "-");
application.register(identifier, module.default);
});
application.register("nested-form", RailsNestedForm);
application.consumer = consumer;
StimulusReflex.initialize(application, { controller, isolate: true });

View File

@@ -127,7 +127,7 @@
@import "components/tom_select"; // admin_v3
@import "app/components/modal_component/modal_component";
@import "app/components/vertical_ellipsis_menu/component"; // admin_v3 and only V3
@import "app/components/vertical_ellipsis_menu_component/vertical_ellipsis_menu_component"; // admin_v3 and only V3
@import "app/components/tag_list_input_component/tag_list_input_component";
@import "app/webpacker/css/admin/trix.scss";

View File

@@ -2,7 +2,7 @@
require "spec_helper"
RSpec.describe VerticalEllipsisMenu::Component, type: :component do
RSpec.describe VerticalEllipsisMenuComponent, type: :component do
it "displays the included links" do
content = "<a href>Edit</a>"
render_inline(described_class.new.with_content(content.html_safe))

View File

@@ -8,28 +8,28 @@ import tag_list_input_controller from "../../../app/components/tag_list_input_co
describe("TagListInputController", () => {
beforeAll(() => {
const application = Application.start();
application.register("tag-list-input-component--tag-list-input", tag_list_input_controller);
application.register("tag-list-input", tag_list_input_controller);
});
beforeEach(() => {
document.body.innerHTML = `
<div data-controller="tag-list-input-component--tag-list-input">
<div data-controller="tag-list-input">
<input
value="tag 1,tag 2,tag 3"
data-tag-list-input-component--tag-list-input-target="tagList"
data-tag-list-input-target="tagList"
type="hidden"
name="variant_tag_list" id="variant_tag_list"
>
<div class="tags-input">
<div class="tags">
<ul class="tag-list" data-tag-list-input-component--tag-list-input-target="list">
<template data-tag-list-input-component--tag-list-input-target="template">
<ul class="tag-list" data-tag-list-input-target="list">
<template data-tag-list-input-target="template">
<li class="tag-item">
<div class="tag-template">
<span></span>
<a
class="remove-button"
data-action="click->tag-list-input-component--tag-list-input#removeTag"
data-action="click->tag-list-input#removeTag"
>✖</a>
</div>
</li>
@@ -39,7 +39,7 @@ describe("TagListInputController", () => {
<span>tag 1</span>
<a
class="remove-button"
data-action="click->tag-list-input-component--tag-list-input#removeTag"
data-action="click->tag-list-input#removeTag"
>✖</a>
</div>
</li>
@@ -48,7 +48,7 @@ describe("TagListInputController", () => {
<span>tag 2</span>
<a
class="remove-button"
data-action="click->tag-list-input-component--tag-list-input#removeTag"
data-action="click->tag-list-input#removeTag"
>✖</a>
</div>
</li>
@@ -57,7 +57,7 @@ describe("TagListInputController", () => {
<span>tag 3</span>
<a
class="remove-button"
data-action="click->tag-list-input-component--tag-list-input#removeTag"
data-action="click->tag-list-input#removeTag"
>✖</a>
</div>
</li>
@@ -67,7 +67,7 @@ describe("TagListInputController", () => {
name="variant_add_tag"
id="variant_add_tag"
placeholder="Add a tag"
data-action="keydown.enter->tag-list-input-component--tag-list-input#addTag keyup->tag-list-input-component--tag-list-input#filterInput" data-tag-list-input-component--tag-list-input-target="newTag"
data-action="keydown.enter->tag-list-input#addTag keyup->tag-list-input#filterInput" data-tag-list-input-target="newTag"
>
</div>
</div>

View File

@@ -3,7 +3,7 @@
*/
import { Application } from "stimulus";
import vertical_ellipsis_menu_controller from "../../../app/components/vertical_ellipsis_menu/component_controller";
import vertical_ellipsis_menu_controller from "../../../app/components/vertical_ellipsis_menu_component/vertical_ellipsis_menu_controller";
describe("VerticalEllipsisMenuController test", () => {
beforeAll(() => {
@@ -30,7 +30,6 @@ describe("VerticalEllipsisMenuController test", () => {
expect(content.classList.contains("show")).toBe(true);
});
it("remove show class from content when clicking button", () => {
button.click();
expect(content.classList.contains("show")).toBe(true);
@@ -38,7 +37,6 @@ describe("VerticalEllipsisMenuController test", () => {
expect(content.classList.contains("show")).toBe(false);
});
it("remove show class from content when clicking outside", () => {
button.click();
expect(content.classList.contains("show")).toBe(true);