@capillarytech/creatives-library 9.0.57-alpha.1 → 9.0.57-alpha.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@capillarytech/creatives-library",
3
3
  "author": "meharaj",
4
- "version": "9.0.57-alpha.1",
4
+ "version": "9.0.57-alpha.2",
5
5
  "description": "Capillary creatives ui",
6
6
  "main": "./index.js",
7
7
  "module": "./index.es.js",
@@ -275,8 +275,14 @@ const FormBuilderShell = (props) => {
275
275
  const isEcho = isEqual(parentFormData, lastEmittedRef.current) || isEqual(parentFormData, current);
276
276
  if (!isEcho) {
277
277
  dispatch(hydrate(fromLegacy(parentFormData, { channel })));
278
- // Classic mirrors the tabCount prop only alongside a parent formData sync.
279
- emittedTabCountRef.current = propsRef.current.tabCount || emittedTabCountRef.current;
278
+ // Classic mirrors the tabCount prop on a formData sync only in EDIT mode
279
+ // (Classic.js:446-447); its generic mirror (415) needs the prop VALUE to
280
+ // change, which never happens with the containers' hardcoded tabCount={2}.
281
+ // Adopting 2 in the create flow makes the containers' content-emptiness
282
+ // check (CAP-189913) scan the untouched iOS tab -> Done/Preview stuck disabled.
283
+ if (propsRef.current.isEdit) {
284
+ emittedTabCountRef.current = propsRef.current.tabCount || emittedTabCountRef.current;
285
+ }
280
286
  // Classic validates on every deep-unequal parent push (Classic.js:516-537) —
281
287
  // this is what turns the container's isFormValid true before Create/Done.
282
288
  if (validateOnExternalChange) {
@@ -626,6 +632,7 @@ FormBuilderShell.propTypes = {
626
632
  tagModule: PropTypes.string,
627
633
  tags: PropTypes.array,
628
634
  checkValidation: PropTypes.bool,
635
+ isEdit: PropTypes.bool,
629
636
  injectedTags: PropTypes.object,
630
637
  onContextChange: PropTypes.func,
631
638
  // Shapes match TagList's own contract (array of offer objects / keyed-by-blockId map).
@@ -122,6 +122,28 @@ describe.each([
122
122
  const contentCalls = props.onContentValidityChange.mock.calls.map((c) => c[0]);
123
123
  expect(contentCalls[contentCalls.length - 1]).toEqual({ isContentEmpty: false });
124
124
 
125
+ // user repro: image-upload write-back (a genuine parent push) then CTA check/uncheck.
126
+ // The shell must NOT adopt the hardcoded tabCount={2} prop in the create flow
127
+ // (Classic.js:415/446-447), or the content check scans the empty iOS tab and
128
+ // Done/Preview stick disabled.
129
+ act(() => {
130
+ containerRef.setState((prev) => ({
131
+ formData: { ...prev.formData, 0: { ...prev.formData[0], image: 'blob:uploaded' } },
132
+ }));
133
+ });
134
+ act(() => { jest.advanceTimersByTime(1000); });
135
+ const pane = document.querySelectorAll('.ant-tabs-tabpane')[0] || document;
136
+ const label = Array.from(pane.querySelectorAll('label')).find((l) => /action link/i.test(l.textContent || ''));
137
+ const cta = (label && label.querySelector('input[type="checkbox"]')) || pane.querySelector('input[type="checkbox"]');
138
+ act(() => { fireEvent.click(cta); });
139
+ act(() => { jest.advanceTimersByTime(1000); });
140
+ act(() => { fireEvent.click(cta); });
141
+ act(() => { jest.advanceTimersByTime(1000); });
142
+
143
+ expect(containerRef.state.tabCount).toBe(1); // create flow never adopts the prop
144
+ const finalCalls = props.onContentValidityChange.mock.calls.map((c) => c[0]);
145
+ expect(finalCalls[finalCalls.length - 1]).toEqual({ isContentEmpty: false }); // buttons stay enabled
146
+
125
147
  utils.unmount();
126
148
  });
127
149
  });
@@ -161,4 +161,43 @@ describe('MPUSH shell event bridge', () => {
161
161
  expect(legacy[0]).toEqual(expect.objectContaining({ 'message-title': '' }));
162
162
  expect(legacy[1]).toEqual(expect.objectContaining({ 'message-title2': '' }));
163
163
  });
164
+
165
+ // Classic mirrors the tabCount prop on a formData push only in EDIT mode
166
+ // (Classic.js:446-447; the generic mirror at 415 needs the prop VALUE to change,
167
+ // never true with the containers' hardcoded tabCount={2}). Adopting 2 in the
168
+ // create flow made getMobilePushEmbeddedContentValidity (CAP-189913) scan the
169
+ // untouched iOS tab after an image-upload write-back push -> Done/Preview stuck.
170
+ it('create flow keeps emitting tabCount 1 after a genuine parent push (image write-back); edit adopts the prop', () => {
171
+ const writeBack = (base) => ({
172
+ ...base,
173
+ 0: { ...base[0], image: 'blob:uploaded' }, // container's in-place upload write-back
174
+ });
175
+
176
+ const typeTitle = (value) => {
177
+ const el = document.getElementById('message-title');
178
+ fireEvent.change(el, { target: { value } });
179
+ };
180
+
181
+ // CREATE (no isEdit): tabCount must stay 1
182
+ const createProps = buildProps(buildSchema(), { formData: {} });
183
+ const createMount = mount(createProps);
184
+ act(() => {
185
+ createMount.rerender(createMount.ui({ ...createProps, formData: writeBack(buildFormData()) }));
186
+ });
187
+ act(() => { typeTitle('after push'); }); // next emission carries the tabCount
188
+ const createEmit = createProps.onChange.mock.calls[createProps.onChange.mock.calls.length - 1];
189
+ expect(createEmit[1]).toBe(1);
190
+ createMount.unmount();
191
+
192
+ // EDIT: Classic adopts the prop on the data sync (Classic.js:447)
193
+ const editProps = buildProps(buildSchema(), { formData: {}, isEdit: true });
194
+ const editMount = mount(editProps);
195
+ act(() => {
196
+ editMount.rerender(editMount.ui({ ...editProps, formData: writeBack(buildFormData()) }));
197
+ });
198
+ act(() => { typeTitle('after push'); });
199
+ const editEmit = editProps.onChange.mock.calls[editProps.onChange.mock.calls.length - 1];
200
+ expect(editEmit[1]).toBe(2);
201
+ editMount.unmount();
202
+ });
164
203
  });