Skip to content
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
cdbe118
MWPW-189463 MAS Studio: Quantity selector variation
Mar 20, 2026
82bd457
MWPW-189463 MAS Studio: Quantity selector variation
Mar 20, 2026
e684c0c
Merge branch 'main' into MWPW-189463
Mar 23, 2026
b3dc652
MWPW-189463 MAS Studio: Quantity selector variation
Mar 23, 2026
be07f76
MWPW-189463 MAS Studio: Quantity selector variation - unit tests
Mar 23, 2026
ac4845c
MWPW-189463 MAS Studio: Quantity selector variation - unit tests
Mar 23, 2026
80ac1cd
Merge branch 'main' into MWPW-189463
Roycethan Mar 25, 2026
6049614
Merge branch 'main' into MWPW-189463
mirafedas Mar 31, 2026
f1c7af3
Merge branch 'main' into MWPW-189463
Mar 31, 2026
e3860b9
MWPW-189463 MAS Studio: Quantity selector variation
Apr 1, 2026
879cd12
Merge branch 'main' into MWPW-189463
Apr 1, 2026
93b5dca
MWPW-189463 MAS Studio: Quantity selector variation - formatting
Apr 1, 2026
107524d
MWPW-189463 MAS Studio: Quantity selector variation - IO
Apr 1, 2026
70fe6c7
Merge branch 'main' into MWPW-189463
Apr 6, 2026
b366502
MWPW-189463 MAS Studio: Quantity selector variation
Apr 6, 2026
d838d20
MWPW-189463 MAS Studio: Quantity selector variation
Apr 6, 2026
46c3ed9
MWPW-189463 MAS Studio: Quantity selector variation - unit tests
Apr 6, 2026
e0755e6
Merge branch 'main' into MWPW-189463
Apr 14, 2026
0038338
MWPW-189463 MAS Studio: Quantity selector variation
Apr 14, 2026
3b7d4ec
Merge branch 'main' into MWPW-189463
Apr 14, 2026
29011ff
MWPW-189463 MAS Studio: Quantity selector variation
Apr 15, 2026
cb7ee1a
Merge branch 'main' into MWPW-189463
Apr 15, 2026
54f16e0
MWPW-189463 MAS Studio: Quantity selector variation
Apr 15, 2026
094e6de
Merge branch 'main' into MWPW-189463
Apr 15, 2026
001dd03
MWPW-189463 MAS Studio: Quantity selector variation - Nala
Apr 17, 2026
6533da5
Merge branch 'main' into MWPW-189463
Apr 17, 2026
ad960d2
MWPW-189463 MAS Studio: Quantity selector variation - formatting
Apr 17, 2026
ac3d929
Merge branch 'main' into MWPW-189463
Roycethan Apr 17, 2026
544e7d9
Merge branch 'main' into MWPW-189463
Apr 27, 2026
943d90a
Merge branch 'main' into MWPW-189463
May 4, 2026
380eada
Merge branch 'main' into MWPW-189463
May 5, 2026
62ee088
Merge branch 'main' into MWPW-189463
May 6, 2026
7f1e465
Trigger Build
May 6, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions studio/src/common/fields/quantity-select.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ export class QuantitySelectField extends LitElement {
step: { type: String, state: true },
layout: { type: String, reflect: true },
disabled: { type: Boolean, reflect: true },
renderQuantityComponentOverrideIndicator: { type: Function, attribute: false },
};

static styles = css`
Expand All @@ -63,6 +64,28 @@ export class QuantitySelectField extends LitElement {
sp-field-group {
width: 100%;
}

.field-status-indicator {
display: flex;
align-items: center;
gap: 6px;
margin-top: 6px;
font-size: 12px;
color: var(--spectrum-blue-700);
}

.field-status-indicator a {
color: var(--spectrum-blue-700);
text-decoration: none;
cursor: pointer;
display: inline-flex;
align-items: center;
gap: 4px;
}

.field-status-indicator a:hover {
text-decoration: underline;
}
`;

constructor() {
Expand All @@ -73,6 +96,7 @@ export class QuantitySelectField extends LitElement {
this.step = '1';
this.layout = 'grid';
this.disabled = false;
this.renderQuantityComponentOverrideIndicator = () => {};
}

willUpdate(changedProperties) {
Expand Down Expand Up @@ -130,6 +154,7 @@ export class QuantitySelectField extends LitElement {
@change=${this.#suppressNativeChange}
@input=${this.#handleTitleChange}
></sp-textfield>
${this.renderQuantityComponentOverrideIndicator('title')}
</sp-field-group>
<sp-field-group>
<sp-field-label>Start quantity</sp-field-label>
Expand All @@ -142,6 +167,7 @@ export class QuantitySelectField extends LitElement {
@change=${this.#suppressNativeChange}
@input=${this.#handleMinChange}
></sp-textfield>
${this.renderQuantityComponentOverrideIndicator('min')}
</sp-field-group>
</div>
<sp-field-group>
Expand All @@ -155,6 +181,7 @@ export class QuantitySelectField extends LitElement {
@change=${this.#suppressNativeChange}
@input=${this.#handleStepChange}
></sp-textfield>
${this.renderQuantityComponentOverrideIndicator('step')}
</sp-field-group>
`;
}
Expand Down
46 changes: 28 additions & 18 deletions studio/src/editors/merch-card-editor.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import { getGlobalSettingsDefaults } from '../settings/settings-store.js';

const QUANTITY_MODEL = 'quantitySelect';
const WHAT_IS_INCLUDED = 'whatsIncluded';
const QUANTITY_EMPTY = '</merch-quantity-select>';
Comment thread
yesil marked this conversation as resolved.
Outdated

const VARIANT_RTE_MARKS = {
[VARIANT_NAMES.MINI]: {
Expand Down Expand Up @@ -74,6 +75,7 @@ class MerchCardEditor extends LitElement {
this.fieldsReady = false;
this.localeSearch = '';
this.reactiveController = new ReactiveController(this, []);
this.renderQuantityComponentOverrideIndicator = this.renderQuantityComponentOverrideIndicator.bind(this);
}

createRenderRoot() {
Expand Down Expand Up @@ -575,7 +577,9 @@ class MerchCardEditor extends LitElement {
}

get fragmentQuantityValue() {
return this.fragment?.fields.find((f) => f.name === QUANTITY_MODEL)?.values[0] || '';
const value = this.getEffectiveFieldValue(QUANTITY_MODEL, 0) || '';
if (value === QUANTITY_EMPTY) return '';
return value;
}

get quantitySelectorDisplayed() {
Expand All @@ -596,10 +600,11 @@ class MerchCardEditor extends LitElement {
if (e.target.checked) {
html = this.quantityValue || createQuantitySelectValue({ title: '', min: '1', step: '1' });
} else {
const qsValues = this.fragmentStore.get().getField(QUANTITY_MODEL)?.values;
this.quantitySelectorValues = qsValues?.length ? qsValues[0] : '';
this.quantitySelectorValues = this.fragmentQuantityValue;
if (this.effectiveIsVariation) html = QUANTITY_EMPTY;
}
this.fragmentStore.updateField(QUANTITY_MODEL, [html]);
if (!e.target.checked) this.requestUpdate();
};

showQuantityFields(show) {
Expand Down Expand Up @@ -851,10 +856,6 @@ class MerchCardEditor extends LitElement {
width: 100%;
}

.quantity-component-restores {
margin-top: 8px;
}

sp-field-group sp-textfield {
width: 100%;
}
Expand Down Expand Up @@ -1236,19 +1237,14 @@ class MerchCardEditor extends LitElement {
?disabled=${this.disabled}
>Show quantity selector</sp-checkbox
>
${this.renderFieldStatusIndicator('quantitySelect')}
${this.renderQuantityComponentOverrideIndicator()}
<div id="quantitySelector" style="display: ${this.quantitySelectorDisplayed ? 'block' : 'none'};">
<quantity-select-field
data-field-state="${this.getFieldState('quantitySelect')}"
.value=${this.quantityValue}
?disabled=${this.disabled}
@change=${this.#handleQuantityFieldChange}
.renderQuantityComponentOverrideIndicator=${this.renderQuantityComponentOverrideIndicator}
></quantity-select-field>
<div class="quantity-component-restores">
${this.renderQuantityComponentOverrideIndicator('title')}
${this.renderQuantityComponentOverrideIndicator('min')}
${this.renderQuantityComponentOverrideIndicator('step')}
</div>
</div>
</sp-field-group>
<div class="two-column-grid">
Expand Down Expand Up @@ -1987,18 +1983,32 @@ class MerchCardEditor extends LitElement {

renderQuantityComponentOverrideIndicator(component) {
if (!this.effectiveIsVariation) return nothing;
if (this.getQuantityComponentState(component) !== 'overridden') return nothing;
if (component && this.getQuantityComponentState(component) !== 'overridden') return nothing;
if (!component) {
const parentHtml = this.localeDefaultFragment?.getFieldValue(QUANTITY_MODEL, 0) || '';
const ownHtml = this.fragment?.getFieldValue(QUANTITY_MODEL, 0) || '';
if (!ownHtml) return nothing;
if (ownHtml.startsWith('<merch-quantity-select ') && parentHtml.startsWith('<merch-quantity-select '))
return nothing;
}

return this.#renderOverrideIndicatorLink(() => this.resetQuantityComponentToParent(component));
}

async resetQuantityComponentToParent(component) {
const parentHtml = this.localeDefaultFragment?.getFieldValue(QUANTITY_MODEL, 0) || '';
if (!component && !parentHtml) {
this.fragmentStore.updateField(QUANTITY_MODEL, [parentHtml]);
this.quantitySelectorValues = parentHtml;
showToast('Field restored to parent value', 'positive');
return;
}
const parentValues = parseQuantitySelectValue(parentHtml);
const currentValues = parseQuantitySelectValue(this.quantityValue);
const html = createQuantitySelectValue({
title: component === 'title' ? parentValues.title : currentValues.title,
min: component === 'min' ? parentValues.min : currentValues.min,
step: component === 'step' ? parentValues.step : currentValues.step,
title: !component || component === 'title' ? parentValues.title : currentValues.title,
min: !component || component === 'min' ? parentValues.min : currentValues.min,
step: !component || component === 'step' ? parentValues.step : currentValues.step,
});
Comment thread
bozojovicic marked this conversation as resolved.
this.fragmentStore.updateField(QUANTITY_MODEL, [html]);
this.quantitySelectorValues = html;
Expand Down
61 changes: 61 additions & 0 deletions studio/test/editors/merch-card-editor.test.html
Original file line number Diff line number Diff line change
Expand Up @@ -1089,6 +1089,67 @@
});
});

it('adds quantity to variation and removes it via override indicator', async () => {
await setupTestEnvironment();

const qsDiv = editor.querySelector('sp-field-group#quantitySelect div#quantitySelector');
expect(qsDiv.style.display).to.equal('none');

const qsCheckboxEl = editor.querySelector('sp-field-group#quantitySelect sp-checkbox');
expect(qsCheckboxEl.closest('sp-field-group').querySelector('.field-status-indicator a')).to.not.exist;
qsCheckboxEl.checked = true;
qsCheckboxEl.dispatchEvent(new CustomEvent('change', { bubbles: true, composed: true }));
expect(qsDiv.style.display).to.equal('block');

const qsTitleEl = qsDiv
.querySelector('quantity-select-field')
.shadowRoot.querySelector('sp-textfield#quantity-selector-title');
const qsStartEl = qsDiv
.querySelector('quantity-select-field')
.shadowRoot.querySelector('sp-textfield#quantity-selector-start');
const qsStepEl = qsDiv
.querySelector('quantity-select-field')
.shadowRoot.querySelector('sp-textfield#quantity-selector-step');

qsTitleEl.value = 'QS';
qsTitleEl.dispatchEvent(new CustomEvent('input', { bubbles: true, composed: true }));
qsStartEl.value = '3';
qsStartEl.dispatchEvent(new CustomEvent('input', { bubbles: true, composed: true }));
qsStepEl.value = '2';
qsStepEl.dispatchEvent(new CustomEvent('input', { bubbles: true, composed: true }));

Comment thread
yesil marked this conversation as resolved.
await editor.updateComplete;

let titleOverrideEl = qsTitleEl.closest('sp-field-group').querySelector('.field-status-indicator a');
expect(titleOverrideEl).to.exist;
let startOverrideEl = qsStartEl.closest('sp-field-group').querySelector('.field-status-indicator a');
expect(startOverrideEl).to.exist;
let stepOverrideEl = qsStepEl.closest('sp-field-group').querySelector('.field-status-indicator a');
expect(stepOverrideEl).to.exist;
let qsOverrideEl = qsCheckboxEl.closest('sp-field-group').querySelector('.field-status-indicator a');
expect(qsOverrideEl).to.exist;

titleOverrideEl.click();
startOverrideEl.click();
stepOverrideEl.click();

await editor.updateComplete;

titleOverrideEl = qsTitleEl.closest('sp-field-group').querySelector('.field-status-indicator a');
expect(titleOverrideEl).to.not.exist;
startOverrideEl = qsStartEl.closest('sp-field-group').querySelector('.field-status-indicator a');
expect(startOverrideEl).to.not.exist;
stepOverrideEl = qsStepEl.closest('sp-field-group').querySelector('.field-status-indicator a');
expect(stepOverrideEl).to.not.exist;

qsOverrideEl.click();
await editor.updateComplete;

qsOverrideEl = qsCheckboxEl.closest('sp-field-group').querySelector('.field-status-indicator a');
expect(qsOverrideEl).to.not.exist;
expect(qsDiv.style.display).to.equal('none');
});

describe('getWhatsIncludedProps', () => {
it('extracts icon, alt, and description from a merch-icon element', async () => {
await setupTestEnvironment({}, 'gwip-merch-icon');
Expand Down
7 changes: 7 additions & 0 deletions studio/test/mocks/adobe/sites/cf/fragments/cc-all-apps
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,13 @@
"mimeType": "text/html",
"values": []
},
{
"name": "quantitySelect",
"type": "text",
"multiple": false,
"locked": false,
"values": []
},
{
"name": "secure",
"type": "boolean",
Expand Down
Loading