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`.