Skip to content

Commit 0ce0e30

Browse files
authored
CIF-2921 - Improve individual product detail sections authoring experience (#931)
* improve authoring experience of product v3 component * use multi select for the sections, add area titles * fix cart actions style for bundle and gift card in product v3 * fix quantity * fix unit tests * format code * fix typo * fix variant selection sector * fix karma tests * changed field description * fix labels * update .content.xml
1 parent c635a11 commit 0ce0e30

22 files changed

Lines changed: 415 additions & 164 deletions

File tree

bundles/core/src/main/java/com/adobe/cq/commerce/core/components/internal/models/v1/product/ProductImpl.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ public class ProductImpl extends DataLayerComponent implements Product {
108108
* Name of the boolean policy property indicating if the product component
109109
* should show an add to wish list button or not.
110110
*/
111-
private static final String PN_STYLE_ENABLE_ADD_TO_WISHLIST = "enableAddToWishList";
111+
static final String PN_STYLE_ENABLE_ADD_TO_WISHLIST = "enableAddToWishList";
112112
/**
113113
* Name of a boolean configuration properties used by the CIF Configuration to
114114
* store if the endpoint has wish lists enabled.

bundles/core/src/main/java/com/adobe/cq/commerce/core/components/internal/models/v3/product/ProductImpl.java

Lines changed: 35 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
1616
package com.adobe.cq.commerce.core.components.internal.models.v3.product;
1717

18+
import java.util.Arrays;
19+
import java.util.Collections;
1820
import java.util.HashMap;
1921
import java.util.List;
2022
import java.util.Map;
@@ -25,6 +27,8 @@
2527

2628
import org.apache.sling.api.SlingHttpServletRequest;
2729
import org.apache.sling.models.annotations.Model;
30+
import org.apache.sling.models.annotations.injectorspecific.InjectionStrategy;
31+
import org.apache.sling.models.annotations.injectorspecific.ValueMapValue;
2832

2933
import com.adobe.cq.commerce.core.components.internal.models.v1.product.VariantAttributeImpl;
3034
import com.adobe.cq.commerce.core.components.internal.models.v1.product.VariantValueImpl;
@@ -37,36 +41,54 @@
3741
import com.adobe.cq.commerce.magento.graphql.ConfigurableProductOptionsValues;
3842
import com.adobe.cq.commerce.magento.graphql.ConfigurableVariant;
3943

40-
@Model(adaptables = SlingHttpServletRequest.class, adapters = Product.class, resourceType = ProductImpl.RESOURCE_TYPE)
44+
@Model(
45+
adaptables = SlingHttpServletRequest.class,
46+
adapters = Product.class,
47+
resourceType = ProductImpl.RESOURCE_TYPE)
4148
public class ProductImpl extends com.adobe.cq.commerce.core.components.internal.models.v2.product.ProductImpl
4249
implements Product {
4350

4451
public static final String RESOURCE_TYPE = "core/cif/components/commerce/product/v3/product";
4552

46-
protected static final Map<String, String> SECTIONS_MAP = new HashMap<String, String>() {
53+
protected static final String PN_VISIBLE_SECTIONS = "visibleSections";
54+
55+
protected static final Map<String, String> SECTIONS_MAP = Collections.unmodifiableMap(new HashMap<String, String>() {
4756
{
48-
put(TITLE_SECTION, "showTitle");
49-
put(PRICE_SECTION, "showPrice");
50-
put(SKU_SECTION, "showSku");
51-
put(IMAGE_SECTION, "showImage");
52-
put(OPTIONS_SECTION, "showOptions");
53-
put(QUANTITY_SECTION, "showQuantity");
54-
put(ACTIONS_SECTION, "showActions");
55-
put(DESCRIPTION_SECTION, "showDescription");
56-
put(DETAILS_SECTION, "showDetails");
57+
put("title", TITLE_SECTION);
58+
put("price", PRICE_SECTION);
59+
put("sku", SKU_SECTION);
60+
put("images", IMAGE_SECTION);
61+
put("options", OPTIONS_SECTION);
62+
put("quantity", QUANTITY_SECTION);
63+
put("actions", ACTIONS_SECTION);
64+
put("description", DESCRIPTION_SECTION);
65+
put("details", DETAILS_SECTION);
5766
}
58-
};
67+
});
68+
69+
private static final String[] VISIBLE_SECTIONS_DEFAULT = SECTIONS_MAP.keySet().toArray(new String[0]);
70+
71+
@ValueMapValue(injectionStrategy = InjectionStrategy.OPTIONAL)
72+
private String[] visibleSections;
73+
74+
private Set<String> visibleSectionsSet;
5975

6076
@PostConstruct
6177
protected void initModel() {
6278
super.initModel();
79+
6380
if (productRetriever != null) {
6481
productRetriever.extendProductQueryWith(p -> p.onConfigurableProduct(cp -> cp
6582
.configurableOptions(o -> o
6683
.values(v -> v.uid()))
6784
.variants(v -> v
6885
.attributes(a -> a.uid()))));
6986
}
87+
88+
if (visibleSections == null || visibleSections.length == 0) {
89+
visibleSections = currentStyle.get(PN_VISIBLE_SECTIONS, VISIBLE_SECTIONS_DEFAULT);
90+
}
91+
visibleSectionsSet = Collections.unmodifiableSet(Arrays.stream(visibleSections).map(SECTIONS_MAP::get).collect(Collectors.toSet()));
7092
}
7193

7294
@Override
@@ -129,7 +151,6 @@ protected Variant mapVariant(ConfigurableVariant variant) {
129151

130152
@Override
131153
public Set<String> getVisibleSections() {
132-
return SECTIONS_MAP.keySet().stream().filter(k -> currentStyle.get(SECTIONS_MAP.get(k), true))
133-
.collect(Collectors.toSet());
154+
return visibleSectionsSet;
134155
}
135156
}

bundles/core/src/test/java/com/adobe/cq/commerce/core/components/internal/models/v1/product/ProductImplTest.java

Lines changed: 12 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -70,16 +70,17 @@
7070
import io.wcm.testing.mock.aem.junit.AemContext;
7171

7272
import static com.adobe.cq.commerce.core.testing.TestContext.buildAemContext;
73+
import static org.assertj.core.api.Assertions.assertThat;
7374
import static org.junit.Assert.assertEquals;
7475
import static org.junit.Assert.assertFalse;
7576
import static org.junit.Assert.assertNotNull;
7677
import static org.junit.Assert.assertNull;
7778
import static org.junit.Assert.assertTrue;
7879
import static org.mockito.Matchers.any;
7980
import static org.mockito.Matchers.anyBoolean;
80-
import static org.mockito.Matchers.anyString;
8181
import static org.mockito.Matchers.eq;
8282
import static org.mockito.Mockito.RETURNS_DEEP_STUBS;
83+
import static org.mockito.Mockito.doAnswer;
8384
import static org.mockito.Mockito.mock;
8485
import static org.mockito.Mockito.never;
8586
import static org.mockito.Mockito.spy;
@@ -110,9 +111,9 @@ public class ProductImplTest {
110111
})
111112
.build();
112113

113-
private Resource productResource;
114-
private Resource pageResource;
115-
private GraphqlClient graphqlClient;
114+
protected Resource productResource;
115+
protected Resource pageResource;
116+
protected GraphqlClient graphqlClient;
116117

117118
protected ProductInterface product;
118119
protected Product productModel;
@@ -154,7 +155,9 @@ public void setUp() throws Exception {
154155
slingBindings.put(WCMBindingsConstants.NAME_PROPERTIES, productResource.getValueMap());
155156

156157
style = mock(Style.class);
157-
when(style.get(anyString(), anyBoolean())).then(i -> i.getArgumentAt(1, Boolean.class));
158+
doAnswer(i -> i.getArguments()[1]).when(style).get(eq("loadClientPrice"), anyBoolean());
159+
doAnswer(i -> i.getArguments()[1]).when(style).get(eq("enableAddToWishList"), anyBoolean());
160+
doAnswer(i -> i.getArguments()[1]).when(style).get(eq("visibleSections"), any(String[].class));
158161
slingBindings.put("currentStyle", style);
159162

160163
SightlyWCMMode wcmMode = mock(SightlyWCMMode.class);
@@ -634,19 +637,10 @@ public void testAddToWishListDisabled() {
634637
}
635638

636639
@Test
637-
public void testVisibleSectionsWithoutStyle() {
640+
public void testVisibleSectionsDefault() {
638641
adaptToProduct();
639-
Set<String> expectedSections = new HashSet<>();
640-
expectedSections.add(Product.TITLE_SECTION);
641-
expectedSections.add(Product.PRICE_SECTION);
642-
expectedSections.add(Product.SKU_SECTION);
643-
expectedSections.add(Product.IMAGE_SECTION);
644-
expectedSections.add(Product.OPTIONS_SECTION);
645-
expectedSections.add(Product.QUANTITY_SECTION);
646-
expectedSections.add(Product.ACTIONS_SECTION);
647-
expectedSections.add(Product.DESCRIPTION_SECTION);
648-
expectedSections.add(Product.DETAILS_SECTION);
649-
650-
assertEquals(expectedSections, productModel.getVisibleSections());
642+
assertThat(productModel.getVisibleSections()).containsOnly(Product.ACTIONS_SECTION, Product.DESCRIPTION_SECTION,
643+
Product.DETAILS_SECTION, Product.IMAGE_SECTION, Product.PRICE_SECTION, Product.QUANTITY_SECTION, Product.OPTIONS_SECTION,
644+
Product.SKU_SECTION, Product.TITLE_SECTION);
651645
}
652646
}

bundles/core/src/test/java/com/adobe/cq/commerce/core/components/internal/models/v3/product/ProductImplTest.java

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,13 @@
1616
package com.adobe.cq.commerce.core.components.internal.models.v3.product;
1717

1818
import java.io.IOException;
19-
import java.util.HashSet;
2019
import java.util.List;
21-
import java.util.Set;
2220

21+
import org.apache.sling.api.scripting.SlingBindings;
2322
import org.junit.Before;
2423
import org.junit.Test;
2524

25+
import com.adobe.cq.commerce.core.components.models.product.Product;
2626
import com.adobe.cq.commerce.core.components.models.product.Variant;
2727
import com.adobe.cq.commerce.core.components.models.product.VariantAttribute;
2828
import com.adobe.cq.commerce.core.components.models.product.VariantValue;
@@ -31,8 +31,10 @@
3131
import com.adobe.cq.commerce.magento.graphql.ConfigurableProductOptions;
3232
import com.adobe.cq.commerce.magento.graphql.ConfigurableProductOptionsValues;
3333
import com.adobe.cq.commerce.magento.graphql.Query;
34+
import com.day.cq.wcm.scripting.WCMBindingsConstants;
3435
import com.fasterxml.jackson.databind.ObjectMapper;
3536

37+
import static org.assertj.core.api.Assertions.assertThat;
3638
import static org.junit.Assert.assertEquals;
3739
import static org.junit.Assert.assertNotNull;
3840
import static org.junit.Assert.assertTrue;
@@ -41,6 +43,8 @@
4143

4244
public class ProductImplTest extends com.adobe.cq.commerce.core.components.internal.models.v2.product.ProductImplTest {
4345

46+
private static final String PRODUCT_WITH_VISIBLE_SECTIONS = "/content/pageA/jcr:content/root/responsivegrid/productwithvisiblesections";
47+
4448
@Override
4549
protected void adaptToProduct() {
4650
// This ensures we re-run all the unit tests with version 3 of ProductImpl
@@ -120,11 +124,21 @@ public void testSwatchDataInVariantAttributes() throws IOException {
120124

121125
@Test
122126
public void testVisibleSectionsWithStyle() {
123-
when(style.get(anyString(), anyBoolean())).thenReturn(Boolean.FALSE);
124-
when(style.get(eq("showSku"), anyBoolean())).thenReturn(Boolean.TRUE);
127+
when(style.get(eq("visibleSections"), any(String[].class))).thenReturn(new String[] { "price", "title" });
128+
adaptToProduct();
129+
assertThat(productModel.getVisibleSections()).containsOnly(Product.PRICE_SECTION, Product.TITLE_SECTION);
130+
}
131+
132+
@Test
133+
public void testVisibleSections() {
134+
context.currentResource(PRODUCT_WITH_VISIBLE_SECTIONS);
135+
context.request().setServletPath(PRODUCT_WITH_VISIBLE_SECTIONS + ".beaumont-summit-kit.html");
136+
productResource = context.resourceResolver().getResource(PRODUCT_WITH_VISIBLE_SECTIONS);
137+
SlingBindings slingBindings = (SlingBindings) context.request().getAttribute(SlingBindings.class.getName());
138+
slingBindings.setResource(productResource);
139+
slingBindings.put(WCMBindingsConstants.NAME_PROPERTIES, productResource.getValueMap());
140+
125141
adaptToProduct();
126-
Set<String> expected = new HashSet<>();
127-
expected.add("SKU");
128-
assertEquals(expected, productModel.getVisibleSections());
142+
assertThat(productModel.getVisibleSections()).containsOnly(Product.PRICE_SECTION, Product.TITLE_SECTION, Product.SKU_SECTION);
129143
}
130144
}

bundles/core/src/test/resources/context/jcr-content.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,14 @@
126126
"sling:resourceType": "venia/components/commerce/product",
127127
"id": "custom-id"
128128
},
129+
"productwithvisiblesections": {
130+
"sling:resourceType": "venia/components/commerce/product",
131+
"visibleSections": [
132+
"title",
133+
"price",
134+
"sku"
135+
]
136+
},
129137
"productlist": {
130138
"loadClientPrice": true,
131139
"showImage": true,

react-components/src/components/BundleProductOptions/__test__/__snapshots__/bundleProductOptions.test.js.snap

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -356,7 +356,7 @@ exports[`BundleProductOptions renders add to wish list button 1`] = `
356356
</div>
357357
</section>
358358
<section
359-
class="productFullDetail__cartActions productFullDetail__section"
359+
class="productFullDetail__cartActions productFullDetail__actions productFullDetail__section"
360360
>
361361
<button
362362
class="button__root_highPriority button__root clickable__root button__filled"
@@ -742,7 +742,7 @@ exports[`BundleProductOptions renders the component with full options 1`] = `
742742
</div>
743743
</section>
744744
<section
745-
class="productFullDetail__cartActions productFullDetail__section"
745+
class="productFullDetail__cartActions productFullDetail__actions productFullDetail__section"
746746
>
747747
<button
748748
class="button__root_highPriority button__root clickable__root button__filled"

react-components/src/components/BundleProductOptions/bundleProductOptions.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -311,7 +311,7 @@ const BundleProductOptions = () => {
311311
<p className="message-root"></p>
312312
</div>
313313
</section>
314-
<section className="productFullDetail__cartActions productFullDetail__section">
314+
<section className="productFullDetail__cartActions productFullDetail__actions productFullDetail__section">
315315
<button
316316
className="button__root_highPriority button__root clickable__root button__filled"
317317
type="button"

react-components/src/components/GiftCardOptions/__test__/__snapshots__/giftCardOptions.test.js.snap

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -314,7 +314,7 @@ exports[`GiftCardProductOptions renders add to wish list button 1`] = `
314314
</div>
315315
</section>
316316
<section
317-
class="productFullDetail__cartActions productFullDetail__section"
317+
class="productFullDetail__cartActions productFullDetail__actions productFullDetail__section"
318318
>
319319
<button
320320
class="button__root_highPriority button__root clickable__root button__filled"
@@ -659,7 +659,7 @@ exports[`GiftCardProductOptions renders the component with full options 1`] = `
659659
</div>
660660
</section>
661661
<section
662-
class="productFullDetail__cartActions productFullDetail__section"
662+
class="productFullDetail__cartActions productFullDetail__actions productFullDetail__section"
663663
>
664664
<button
665665
class="button__root_highPriority button__root clickable__root button__filled"
@@ -1007,7 +1007,7 @@ exports[`GiftCardProductOptions renders the component with sku 1`] = `
10071007
</div>
10081008
</section>
10091009
<section
1010-
class="productFullDetail__cartActions productFullDetail__section"
1010+
class="productFullDetail__cartActions productFullDetail__actions productFullDetail__section"
10111011
>
10121012
<button
10131013
class="button__root_highPriority button__root clickable__root button__filled"
@@ -1340,7 +1340,7 @@ exports[`GiftCardProductOptions renders the component with wishlist 1`] = `
13401340
</div>
13411341
</section>
13421342
<section
1343-
class="productFullDetail__cartActions productFullDetail__section"
1343+
class="productFullDetail__cartActions productFullDetail__actions productFullDetail__section"
13441344
>
13451345
<button
13461346
class="button__root_highPriority button__root clickable__root button__filled"

react-components/src/components/GiftCardOptions/giftCardOptions.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ const messages = defineMessages({
4848
}
4949
});
5050

51-
const GiftCartOptions = props => {
51+
const GiftCardOptions = props => {
5252
const { sku, showAddToWishList, useUid } = props;
5353
const [
5454
giftCardState,
@@ -235,7 +235,7 @@ const GiftCartOptions = props => {
235235
<p className="message-root"></p>
236236
</div>
237237
</section>
238-
<section className="productFullDetail__cartActions productFullDetail__section">
238+
<section className="productFullDetail__cartActions productFullDetail__actions productFullDetail__section">
239239
<button
240240
className="button__root_highPriority button__root clickable__root button__filled"
241241
type="button"
@@ -265,10 +265,10 @@ const GiftCartOptions = props => {
265265
);
266266
};
267267

268-
GiftCartOptions.propTypes = {
268+
GiftCardOptions.propTypes = {
269269
sku: PropTypes.string.required,
270270
showAddToWishList: PropTypes.bool,
271271
useUid: PropTypes.bool
272272
};
273273

274-
export default GiftCartOptions;
274+
export default GiftCardOptions;

0 commit comments

Comments
 (0)