decap-cms-widget-list 3.6.2 → 3.6.3-beta.1

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": "decap-cms-widget-list",
3
3
  "description": "Widget for editing lists in Decap CMS.",
4
- "version": "3.6.2",
4
+ "version": "3.6.3-beta.1",
5
5
  "homepage": "https://www.decapcms.org/docs/widgets/#list",
6
6
  "repository": {
7
7
  "type": "git",
@@ -33,9 +33,9 @@
33
33
  "react": "^19.1.0",
34
34
  "react-immutable-proptypes": "^2.1.0",
35
35
  "uuid": "^8.3.2",
36
- "decap-cms-lib-widgets": "3.4.2",
37
- "decap-cms-ui-default": "3.9.2",
38
- "decap-cms-widget-object": "3.7.2"
36
+ "decap-cms-lib-widgets": "3.4.3-beta.0",
37
+ "decap-cms-ui-default": "3.9.3-beta.0",
38
+ "decap-cms-widget-object": "3.7.3-beta.1"
39
39
  },
40
40
  "scripts": {
41
41
  "develop": "pnpm run build:esm --watch",
@@ -6,6 +6,7 @@ import { css, ClassNames } from '@emotion/react';
6
6
  import { List, Map, fromJS } from 'immutable';
7
7
  import partial from 'lodash/partial';
8
8
  import isEmpty from 'lodash/isEmpty';
9
+ import memoize from 'lodash/memoize';
9
10
  import uniqueId from 'lodash/uniqueId';
10
11
  import DecapCmsWidgetObject from 'decap-cms-widget-object';
11
12
  import {
@@ -255,10 +256,18 @@ export default class ListControl extends Component {
255
256
 
256
257
  uniqueFieldId = uniqueId(`${this.props.field.get('name')}-field-`);
257
258
  /**
259
+ * Old comment:
260
+ *
258
261
  * Always update so that each nested widget has the option to update. This is
259
262
  * required because ControlHOC provides a default `shouldComponentUpdate`
260
263
  * which only updates if the value changes, but every widget must be allowed
261
264
  * to override this.
265
+ *
266
+ * New comment:
267
+ *
268
+ * Each Widget is wrapped with EditorControl which already tries to update every time.
269
+ * Is there a specific reason we need to always rerender the list?
270
+ * This seems overkill.
262
271
  */
263
272
  shouldComponentUpdate() {
264
273
  return true;
@@ -418,15 +427,18 @@ export default class ListControl extends Component {
418
427
  */
419
428
  getObjectValue = idx => this.props.value.get(idx) || Map();
420
429
 
421
- handleChangeFor(index) {
422
- const key = this.state.keys[index];
423
-
430
+ /**
431
+ * Memoized on the item's key rather than its position, so each item keeps a
432
+ * stable `onChangeObject` reference across renders and reorders.
433
+ */
434
+ handleChangeFor = memoize(key => {
424
435
  return (f, newValue, newMetadata) => {
425
436
  const { value, metadata, onChange, field } = this.props;
426
437
 
427
438
  // Resolve the item's position when the change fires rather than when this
428
- // handler was created. If an item has been removed or moved in between,
429
- // `index` now points at a different item, or past the end of the list.
439
+ // handler was created. If the item has been removed or moved in between,
440
+ // its old position now points at a different item, or past the end of the
441
+ // list.
430
442
  const currentIndex = this.state.keys.indexOf(key);
431
443
 
432
444
  if (currentIndex === -1) {
@@ -446,7 +458,7 @@ export default class ListControl extends Component {
446
458
  };
447
459
  onChange(value.set(currentIndex, newObjectValue), parsedMetadata);
448
460
  };
449
- }
461
+ });
450
462
 
451
463
  handleRemove = (index, event) => {
452
464
  event.preventDefault();
@@ -641,6 +653,11 @@ export default class ListControl extends Component {
641
653
  }
642
654
  }
643
655
 
656
+ getStableParentIds = memoize(
657
+ (parentIds, forID, key) => [...parentIds, forID, key],
658
+ (parentIds, forID, key) => JSON.stringify([...parentIds, forID, key]),
659
+ );
660
+
644
661
  // eslint-disable-next-line react/display-name
645
662
  renderItem = (item, index) => {
646
663
  const {
@@ -711,7 +728,7 @@ export default class ListControl extends Component {
711
728
  })}
712
729
  value={item}
713
730
  field={field}
714
- onChangeObject={this.handleChangeFor(index)}
731
+ onChangeObject={this.handleChangeFor(key)}
715
732
  editorControl={editorControl}
716
733
  resolveWidget={resolveWidget}
717
734
  metadata={metadata}
@@ -725,7 +742,7 @@ export default class ListControl extends Component {
725
742
  collapsed={collapsed}
726
743
  data-testid={`object-control-${key}`}
727
744
  hasError={hasError}
728
- parentIds={[...parentIds, forID, key]}
745
+ parentIds={this.getStableParentIds(parentIds, forID, key)}
729
746
  />
730
747
  )}
731
748
  </ClassNames>
@@ -811,7 +811,7 @@ describe('ListControl', () => {
811
811
  <ListControl ref={ref => (control = ref)} {...props} field={typedField} value={value} />,
812
812
  );
813
813
 
814
- const changeThirdItem = control.handleChangeFor(2);
814
+ const changeThirdItem = control.handleChangeFor(control.state.keys[2]);
815
815
 
816
816
  fireEvent.click(getAllByText('Remove')[0]);
817
817
  const afterRemoval = props.onChange.mock.calls[0][0];
@@ -842,7 +842,7 @@ describe('ListControl', () => {
842
842
  <ListControl ref={ref => (control = ref)} {...props} field={typedField} value={value} />,
843
843
  );
844
844
 
845
- const changeSecondItem = control.handleChangeFor(1);
845
+ const changeSecondItem = control.handleChangeFor(control.state.keys[1]);
846
846
 
847
847
  fireEvent.click(getAllByText('Remove')[1]);
848
848
  const afterRemoval = props.onChange.mock.calls[0][0];