You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@causeway.apache.org by da...@apache.org on 2026/06/07 14:48:56 UTC

(causeway) 05/50: CAUSEWAY-4012: adds selector action styling

This is an automated email from the ASF dual-hosted git repository.

danhaywood pushed a commit to branch CAUSEWAY-4014
in repository https://gitbox.apache.org/repos/asf/causeway.git

commit 2ace4fd0e27ac73687f4257018a442f25be8ccc8
Author: Dan Haywood <da...@haywood-associates.co.uk>
AuthorDate: Sun Jun 7 06:29:55 2026 +0100

    CAUSEWAY-4012: adds selector action styling
---
 ...CssClassFacetForParentedCollectionSelector.java | 36 +++++++++++++++++++++
 .../FaFacetForParentedCollectionSelector.java      | 37 ++++++++++++++++++++++
 .../specimpl/ObjectSpecificationAbstract.java      |  4 +++
 .../ParentedCollectionSelectorActionUtilTest.java  | 18 +++++++++++
 .../changes/add-selector-action-styling/design.md  |  6 ++--
 .../add-selector-action-styling/proposal.md        |  2 +-
 .../parented-collection-selector-actions/spec.md   |  4 +--
 .../changes/add-selector-action-styling/tasks.md   | 16 +++++-----
 8 files changed, 109 insertions(+), 14 deletions(-)

diff --git a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/actions/synthetic/CssClassFacetForParentedCollectionSelector.java b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/actions/synthetic/CssClassFacetForParentedCollectionSelector.java
new file mode 100644
index 00000000000..1571b147274
--- /dev/null
+++ b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/actions/synthetic/CssClassFacetForParentedCollectionSelector.java
@@ -0,0 +1,36 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.apache.causeway.core.metamodel.facets.actions.synthetic;
+
+import org.apache.causeway.core.metamodel.facetapi.FacetHolder;
+import org.apache.causeway.core.metamodel.facets.members.cssclass.CssClassFacetSimple;
+
+/**
+ * Provides default button styling for synthetic parented collection selector actions.
+ */
+public class CssClassFacetForParentedCollectionSelector
+extends CssClassFacetSimple {
+
+    public static final String CSS_CLASS = "btn-outline-secondary";
+
+    public CssClassFacetForParentedCollectionSelector(final FacetHolder holder) {
+        super(CSS_CLASS, holder);
+    }
+
+}
diff --git a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/actions/synthetic/FaFacetForParentedCollectionSelector.java b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/actions/synthetic/FaFacetForParentedCollectionSelector.java
new file mode 100644
index 00000000000..92ea5cbc50e
--- /dev/null
+++ b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/actions/synthetic/FaFacetForParentedCollectionSelector.java
@@ -0,0 +1,37 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.apache.causeway.core.metamodel.facets.actions.synthetic;
+
+import org.apache.causeway.applib.layout.component.CssClassFaPosition;
+import org.apache.causeway.core.metamodel.facetapi.FacetHolder;
+import org.apache.causeway.core.metamodel.facets.members.iconfa.FaStaticFacetAbstract;
+
+/**
+ * Provides default icon styling for synthetic parented collection selector actions.
+ */
+public class FaFacetForParentedCollectionSelector
+extends FaStaticFacetAbstract {
+
+    public static final String CSS_CLASS_FA = "hand-point-left";
+
+    public FaFacetForParentedCollectionSelector(final FacetHolder holder) {
+        super(CSS_CLASS_FA, CssClassFaPosition.LEFT, holder);
+    }
+
+}
diff --git a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/specloader/specimpl/ObjectSpecificationAbstract.java b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/specloader/specimpl/ObjectSpecificationAbstract.java
index f414c974f36..10e75e760e7 100644
--- a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/specloader/specimpl/ObjectSpecificationAbstract.java
+++ b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/specloader/specimpl/ObjectSpecificationAbstract.java
@@ -34,7 +34,9 @@
 import org.apache.causeway.core.metamodel.facets.actions.synthetic.ActionParameterDefaultsFacetForParentedCollectionSelectorParent;
 import org.apache.causeway.core.metamodel.facets.actions.synthetic.ActionSemanticsFacetForParentedCollectionSelector;
 import org.apache.causeway.core.metamodel.facets.actions.synthetic.ActionValidationFacetForParentedCollectionSelector;
+import org.apache.causeway.core.metamodel.facets.actions.synthetic.CssClassFacetForParentedCollectionSelector;
 import org.apache.causeway.core.metamodel.facets.actions.synthetic.DisabledFacetForParentedCollectionSelectorParent;
+import org.apache.causeway.core.metamodel.facets.actions.synthetic.FaFacetForParentedCollectionSelector;
 import org.apache.causeway.core.metamodel.facets.actions.synthetic.LayoutGroupFacetForParentedCollectionSelector;
 import org.apache.causeway.core.metamodel.facets.actions.synthetic.ParamNamedFacetForParentedCollectionSelector;
 import org.apache.causeway.core.metamodel.facets.actions.synthetic.ParentedCollectionSelectorFacetDefault;
@@ -1167,6 +1169,8 @@ private static void installActionFacets(
                 final Can<ObjectAssociation> filterProperties,
                 final FacetedMethod facetedMethod) {
             FacetUtil.addFacet(new MemberNamedFacetForStaticMemberName("Select", facetedMethod));
+            FacetUtil.addFacet(new CssClassFacetForParentedCollectionSelector(facetedMethod));
+            FacetUtil.addFacet(new FaFacetForParentedCollectionSelector(facetedMethod));
             FacetUtil.addFacet(new LayoutGroupFacetForParentedCollectionSelector(
                     collection.getId(), collection.getCanonicalFriendlyName(), facetedMethod));
             FacetUtil.addFacet(new ParentedCollectionSelectorFacetDefault(collection, facetedMethod));
diff --git a/core/metamodel/src/test/java/org/apache/causeway/core/metamodel/specloader/specimpl/ParentedCollectionSelectorActionUtilTest.java b/core/metamodel/src/test/java/org/apache/causeway/core/metamodel/specloader/specimpl/ParentedCollectionSelectorActionUtilTest.java
index 3540023021e..d4e7fc33aeb 100644
--- a/core/metamodel/src/test/java/org/apache/causeway/core/metamodel/specloader/specimpl/ParentedCollectionSelectorActionUtilTest.java
+++ b/core/metamodel/src/test/java/org/apache/causeway/core/metamodel/specloader/specimpl/ParentedCollectionSelectorActionUtilTest.java
@@ -63,7 +63,9 @@
 import org.apache.causeway.core.metamodel.facets.actions.action.invocation.ActionInvocationFacet;
 import org.apache.causeway.core.metamodel.facets.actions.action.invocation.IdentifierUtil;
 import org.apache.causeway.core.metamodel.facets.actions.validate.ActionValidationFacet;
+import org.apache.causeway.core.metamodel.facets.members.cssclass.CssClassFacet;
 import org.apache.causeway.core.metamodel.facets.members.disabled.DisabledFacet;
+import org.apache.causeway.core.metamodel.facets.members.iconfa.FaFacet;
 import org.apache.causeway.core.metamodel.facets.members.layout.group.LayoutGroupFacet;
 import org.apache.causeway.core.metamodel.facets.members.publish.command.CommandPublishingFacet;
 import org.apache.causeway.core.metamodel.facets.param.defaults.ActionParameterDefaultsFacet;
@@ -254,6 +256,11 @@ void names_selector_action_select_while_preserving_deterministic_id() {
         assertThat(selectorAction.getCanonicalFriendlyName(), is("Select"));
     }
 
+    @Test
+    void styles_selector_action_as_secondary_select_navigation() {
+        assertSelectorActionStyling(selectorAction);
+    }
+
     @Test
     void participates_in_safe_action_command_publishing_when_enabled() {
         mmc.getConfiguration().getExtensions().getCommandLog().setRecordingSupport(RecordingSupport.ENABLED);
@@ -275,6 +282,7 @@ void synthesizes_selector_action_for_mixed_in_collection_when_enabled() {
                 instanceOf(ParentedCollectionSelectorFacetDefault.class));
         assertThat(layoutGroupFacet.getGroupId(), is("mixinItems"));
         assertThat(layoutGroupFacet.getGroupName(), is("Mixin Items"));
+        assertSelectorActionStyling(mixinSelectorAction);
         assertThat(mixinSelectorAction.getParameters().getElseFail(0).getElementType().getCorrespondingClass(), is(Lease.class));
         assertThat(mixinSelectorAction.getParameters().stream().anyMatch(parameter -> parameter.getId().equals("name")), is(true));
     }
@@ -577,6 +585,16 @@ void selector_invocation_does_not_prepare_command_when_safe_action_publishing_di
         Mockito.verify(commandPublisher).ready(command);
     }
 
+    private void assertSelectorActionStyling(final ObjectAction action) {
+        val cssClassFacet = action.getFacet(CssClassFacet.class);
+        assertThat(cssClassFacet, instanceOf(CssClassFacetForParentedCollectionSelector.class));
+        assertThat(cssClassFacet.cssClass(null), is(CssClassFacetForParentedCollectionSelector.CSS_CLASS));
+
+        val faFacet = action.getFacet(FaFacet.class);
+        assertThat(faFacet, instanceOf(FaFacetForParentedCollectionSelector.class));
+        assertThat(faFacet.getSpecialization().leftIfAny().getLayers().toQuickNotation(), is(FaFacetForParentedCollectionSelector.CSS_CLASS_FA));
+    }
+
     private MetaModelContext_forTesting newMetamodelContext(final Class<?>... mixinTypes) {
         return MetaModelContext_forTesting.builder()
                 .memberExecutor(Mockito.mock(MemberExecutorService.class))
diff --git a/openspec/changes/add-selector-action-styling/design.md b/openspec/changes/add-selector-action-styling/design.md
index c549fbb5500..99370ac60b1 100644
--- a/openspec/changes/add-selector-action-styling/design.md
+++ b/openspec/changes/add-selector-action-styling/design.md
@@ -9,7 +9,7 @@ This change only needs to make the synthetic selector action install the same st
 
 **Goals:**
 
-- Install a static action CSS class of `btn-secondary` on every synthetic parented collection selector action.
+- Install a static action CSS class of `btn-outline-secondary` on every synthetic parented collection selector action.
 - Install a static Font Awesome icon of `hand-point-left` on every synthetic parented collection selector action.
 - Keep the existing selector action id, name, association, semantics, command publishing, parameters, validation, and invocation behavior unchanged.
 - Cover the new facets with metamodel tests so viewer-independent behavior is specified.
@@ -28,14 +28,14 @@ This change only needs to make the synthetic selector action install the same st
   Dedicated classes can extend the same common facet bases, `CssClassFacetSimple` and `FaStaticFacetAbstract`, to expose equivalent metamodel semantics without fabricating an annotation.
 - Install the style facets in `installActionFacets(...)` alongside the existing name and layout group facets.
   This keeps all static synthetic action layout metadata in one place and ensures mixed-in collection selectors receive the same styling path as ordinary collection selectors.
-- Use literal constants for `btn-secondary` and `hand-point-left` in the synthetic facet classes or action synthesis utility.
+- Use literal constants for `btn-outline-secondary` and `hand-point-left` in the synthetic facet classes or action synthesis utility.
   The values are part of the default generated selector action contract, and there is no proposal requirement for runtime configuration.
 - Validate through `ParentedCollectionSelectorActionUtilTest` by asserting the selector action exposes `CssClassFacet` and static `FaFacet` values.
   This verifies metamodel behavior without coupling the change to a particular viewer implementation.
 
 ## Risks / Trade-offs
 
-- [Risk] Viewers may interpret `btn-secondary` differently or ignore it entirely.
+- [Risk] Viewers may interpret `btn-outline-secondary` differently or ignore it entirely.
   → Mitigation: expose standard layout facets only, matching existing `@ActionLayout` metadata, and leave rendering decisions to viewers.
 - [Risk] Font Awesome quick notation may require the same value shape as annotations.
   → Mitigation: use the annotation-equivalent quick notation `hand-point-left` and test the resulting static facet quick notation.
diff --git a/openspec/changes/add-selector-action-styling/proposal.md b/openspec/changes/add-selector-action-styling/proposal.md
index 015cb70f084..bdc1ee2667b 100644
--- a/openspec/changes/add-selector-action-styling/proposal.md
+++ b/openspec/changes/add-selector-action-styling/proposal.md
@@ -5,7 +5,7 @@ Adding default styling makes the generated `Select` action easier to recognize i
 
 ## What Changes
 
-- Add layout facets to synthetic parented collection selector actions equivalent to `@ActionLayout(cssClass = "btn-secondary", cssClassFa = "hand-point-left", ...)`.
+- Add layout facets to synthetic parented collection selector actions equivalent to `@ActionLayout(cssClass = "btn-outline-secondary", cssClassFa = "hand-point-left", ...)`.
 - Preserve the existing selector action name, collection association, invocation, validation, and parameter behavior.
 - Ensure the styling metadata is part of the synthesized action metamodel so viewers and layout exporters can consume it consistently.
 - No breaking changes.
diff --git a/openspec/changes/add-selector-action-styling/specs/parented-collection-selector-actions/spec.md b/openspec/changes/add-selector-action-styling/specs/parented-collection-selector-actions/spec.md
index 927fbdb061c..0afd0f1e260 100644
--- a/openspec/changes/add-selector-action-styling/specs/parented-collection-selector-actions/spec.md
+++ b/openspec/changes/add-selector-action-styling/specs/parented-collection-selector-actions/spec.md
@@ -13,7 +13,7 @@ The synthetic action identifier SHALL use the reserved prefix `__causeway_select
 The synthetic action SHALL be distinguishable from developer-authored actions by framework metadata.
 The synthetic action SHALL be associated with the parented collection through layout metadata equivalent to `@ActionLayout(associateWith=...)`.
 The synthetic action SHALL have the display name `Select` through name metadata equivalent to `@ActionLayout(named="Select")`.
-The synthetic action SHALL have action layout CSS class metadata equivalent to `@ActionLayout(cssClass="btn-secondary")`.
+The synthetic action SHALL have action layout CSS class metadata equivalent to `@ActionLayout(cssClass="btn-outline-secondary")`.
 The synthetic action SHALL have action layout Font Awesome metadata equivalent to `@ActionLayout(cssClassFa="hand-point-left")`.
 
 #### Scenario: Synthetic action is not available by default
@@ -56,7 +56,7 @@ The synthetic action SHALL have action layout Font Awesome metadata equivalent t
 - **GIVEN** an entity type `Lease` has a parented collection `items`
 - **AND** command-log recording support is `ENABLED`
 - **WHEN** the framework synthesizes the selector action for `items`
-- **THEN** the action has CSS class metadata of `btn-secondary`
+- **THEN** the action has CSS class metadata of `btn-outline-secondary`
 
 #### Scenario: Synthetic action uses select navigation icon styling
 - **GIVEN** an entity type `Lease` has a parented collection `items`
diff --git a/openspec/changes/add-selector-action-styling/tasks.md b/openspec/changes/add-selector-action-styling/tasks.md
index caab487e014..2ff1b3d7712 100644
--- a/openspec/changes/add-selector-action-styling/tasks.md
+++ b/openspec/changes/add-selector-action-styling/tasks.md
@@ -1,16 +1,16 @@
 ## 1. Metamodel Facets
 
-- [ ] 1.1 Add a synthetic action CSS class facet that exposes `btn-secondary` using the existing `CssClassFacet` contract.
-- [ ] 1.2 Add a synthetic action Font Awesome facet that exposes static quick notation `hand-point-left` using the existing `FaFacet` contract.
-- [ ] 1.3 Install both styling facets when `ParentedCollectionSelectorActionUtil` creates a synthetic selector action.
+- [x] 1.1 Add a synthetic action CSS class facet that exposes `btn-outline-secondary` using the existing `CssClassFacet` contract.
+- [x] 1.2 Add a synthetic action Font Awesome facet that exposes static quick notation `hand-point-left` using the existing `FaFacet` contract.
+- [x] 1.3 Install both styling facets when `ParentedCollectionSelectorActionUtil` creates a synthetic selector action.
 
 ## 2. Tests
 
-- [ ] 2.1 Add metamodel test coverage that a normal selector action exposes CSS class `btn-secondary`.
-- [ ] 2.2 Add metamodel test coverage that a normal selector action exposes Font Awesome quick notation `hand-point-left`.
-- [ ] 2.3 Add or adjust coverage to ensure mixed-in collection selector actions receive the same styling facets if needed.
+- [x] 2.1 Add metamodel test coverage that a normal selector action exposes CSS class `btn-outline-secondary`.
+- [x] 2.2 Add metamodel test coverage that a normal selector action exposes Font Awesome quick notation `hand-point-left`.
+- [x] 2.3 Add or adjust coverage to ensure mixed-in collection selector actions receive the same styling facets if needed.
 
 ## 3. Validation
 
-- [ ] 3.1 Run the focused `ParentedCollectionSelectorActionUtilTest` Maven test set.
-- [ ] 3.2 Run `openspec validate add-selector-action-styling --strict`.
+- [x] 3.1 Run the focused `ParentedCollectionSelectorActionUtilTest` Maven test set.
+- [x] 3.2 Run `openspec validate add-selector-action-styling --strict`.