Skip to content

Commit fd0e6e6

Browse files
committed
issue-171 many-to-many replacement (without increase)
1 parent 59a2480 commit fd0e6e6

File tree

2 files changed

+95
-50
lines changed

2 files changed

+95
-50
lines changed

tests/adapter.replace.spec.ts

+74-39
Original file line numberDiff line numberDiff line change
@@ -13,28 +13,31 @@ const baseSettings = {
1313

1414
interface ICustom {
1515
token: 'first' | 'last' | 'middle';
16-
indexToReplace?: number;
17-
indexesToReplace?: number[];
16+
indexesToReplace: number[]; // indexes to remove
17+
amount: number; // how many items to insert
1818
increase?: boolean;
1919
}
2020

2121
const configList: TestBedConfig[] = [{
2222
datasourceSettings: { ...baseSettings },
2323
custom: {
24-
indexToReplace: baseSettings.minIndex + 1,
25-
token: 'middle'
26-
}
24+
indexesToReplace: [baseSettings.minIndex + 1],
25+
token: 'middle',
26+
amount: 1
27+
} as ICustom
2728
}, {
2829
datasourceSettings: { ...baseSettings },
2930
custom: {
30-
indexToReplace: baseSettings.minIndex,
31-
token: 'first'
31+
indexesToReplace: [baseSettings.minIndex],
32+
token: 'first',
33+
amount: 1
3234
} as ICustom
3335
}, {
3436
datasourceSettings: { ...baseSettings, startIndex: baseSettings.maxIndex },
3537
custom: {
36-
indexToReplace: baseSettings.maxIndex,
37-
token: 'last'
38+
indexesToReplace: [baseSettings.maxIndex],
39+
token: 'last',
40+
amount: 1
3841
} as ICustom
3942
}].map(config => ({
4043
...config,
@@ -49,7 +52,8 @@ const manyToOneConfigList: TestBedConfig[] = [{
4952
baseSettings.minIndex + 2,
5053
baseSettings.minIndex + 3
5154
],
52-
token: 'middle'
55+
token: 'middle',
56+
amount: 1
5357
} as ICustom
5458
}, {
5559
datasourceSettings: { ...baseSettings },
@@ -59,7 +63,8 @@ const manyToOneConfigList: TestBedConfig[] = [{
5963
baseSettings.minIndex + 1,
6064
baseSettings.minIndex + 2
6165
],
62-
token: 'first'
66+
token: 'first',
67+
amount: 1
6368
} as ICustom
6469
}, {
6570
datasourceSettings: { ...baseSettings, startIndex: baseSettings.maxIndex },
@@ -69,7 +74,8 @@ const manyToOneConfigList: TestBedConfig[] = [{
6974
baseSettings.maxIndex - 1,
7075
baseSettings.maxIndex
7176
],
72-
token: 'last'
77+
token: 'last',
78+
amount: 1
7379
} as ICustom
7480
}].map(config => ({
7581
...config,
@@ -81,38 +87,53 @@ const manyToOneIncreaseConfigList = manyToOneConfigList.map(config => ({
8187
custom: {
8288
...config.custom,
8389
increase: true
84-
}
90+
} as ICustom
8591
}));
8692

93+
const manyToManyConfigList = [
94+
...manyToOneConfigList.map(config => ({
95+
...config, custom: { ...config.custom, amount: 2 } as ICustom
96+
})),
97+
...manyToOneConfigList.map(config => ({
98+
...config, custom: { ...config.custom, amount: 3 } as ICustom
99+
})),
100+
...manyToOneConfigList.map(config => ({
101+
...config, custom: { ...config.custom, amount: 4 } as ICustom
102+
})),
103+
];
104+
87105
const shouldReplace = (config: TestBedConfig) => (misc: Misc) => async (done: Function) => {
88106
await misc.relaxNext();
89107
const { adapter } = misc;
90-
const { indexToReplace: index, indexesToReplace: indexes, token, increase } = config.custom;
108+
const { indexesToReplace: indexes, amount, token, increase } = config.custom;
91109
const { datasourceSettings: { minIndex, itemSize } } = config;
92-
const diff = indexes ? indexes.length : 1;
110+
const diff = amount - indexes.length; // inserted - removed
93111
const viewportSize = misc.getScrollableSize();
94-
const sizeToRemove = (diff - 1) * misc.getItemSize();
95-
const maxScrollPosition = misc.getMaxScrollPosition() - sizeToRemove;
96-
const newIndex = indexes ? indexes[increase ? indexes.length - 1 : 0] : index;
97-
const newMinIndex = increase ? minIndex + diff - 1 : minIndex;
98-
const position = token === 'last' ? maxScrollPosition : (newIndex - newMinIndex) * itemSize;
99-
const newItem = generateItem(newIndex);
100-
newItem.text += '*';
112+
const sizeToChange = diff * misc.getItemSize();
113+
const maxScrollPosition = misc.getMaxScrollPosition() + sizeToChange;
114+
const newIndexFirst = indexes[increase ? indexes.length - 1 : 0];
115+
const newIndexLast = newIndexFirst + amount - 1;
116+
const newAbsMinIndex = increase ? minIndex - diff : minIndex;
117+
const position = token === 'last' ? maxScrollPosition : (newIndexFirst - newAbsMinIndex) * itemSize;
118+
const items = Array.from({ length: amount }).map((j, i) => generateItem(newIndexFirst + i, false, '*'));
101119

102120
// replace at the Datasource level (component)
103-
if (index) {
104-
(misc.datasource as any).replaceOneToOne(index, newItem);
105-
} else if (indexes) {
106-
(misc.datasource as any).replaceManyToOne(indexes, newItem, increase);
121+
if (indexes.length === 1 && amount === 1) {
122+
(misc.datasource as any).replaceOneToOne(indexes[0], items[0]);
123+
} else if (indexes.length > 1 && amount === 1) {
124+
(misc.datasource as any).replaceManyToOne(indexes, items[0], increase);
125+
} else if (indexes.length > 1 && amount > 1) {
126+
(misc.datasource as any).replaceManyToMany(indexes, items, increase);
107127
}
108128

109129
// replace at the Viewport level (scroller)
110130
await adapter.replace({
111-
predicate: ({ $index }) => (indexes || [index]).includes($index),
112-
items: [newItem],
131+
predicate: ({ $index }) => indexes.includes($index),
132+
items,
113133
increase
114134
});
115135

136+
// refresh the view via scroll to edges
116137
await misc.scrollMinMax();
117138

118139
// scroll to replaced item
@@ -121,31 +142,35 @@ const shouldReplace = (config: TestBedConfig) => (misc: Misc) => async (done: Fu
121142
await misc.relaxNext();
122143
}
123144

124-
// check replaced item
145+
// check edge replaced items
125146
if (token === 'last') {
126-
expect(adapter.lastVisible.$index).toEqual(newIndex);
147+
expect(adapter.lastVisible.$index).toEqual(newIndexLast);
127148
} else {
128-
expect(adapter.firstVisible.$index).toEqual(newIndex);
149+
expect(adapter.firstVisible.$index).toEqual(newIndexFirst);
129150
}
130-
expect(misc.getElementText(newIndex)).toEqual(newIndex + ': ' + newItem.text);
151+
expect(misc.getElementText(newIndexFirst)).toEqual(newIndexFirst + ': ' + items[0].text);
152+
expect(misc.getElementText(newIndexLast)).toEqual(newIndexLast + ': ' + items[items.length - 1].text);
131153

132-
// check the next item
154+
// check the item next to the last replaced one
133155
if (token === 'last') {
134-
expect(misc.checkElementContent(newIndex - 1, newIndex - (increase ? diff : 1))).toEqual(true);
156+
expect(misc.checkElementContent(newIndexFirst - 1, newIndexFirst - (increase ? 1 - diff : 1))).toEqual(true);
135157
} else {
136-
expect(misc.checkElementContent(newIndex + 1, newIndex + (increase ? 1 : diff))).toEqual(true);
158+
expect(misc.checkElementContent(newIndexLast + 1, newIndexLast + (increase ? 1 : 1 - diff))).toEqual(true);
137159
}
138160

139-
expect(misc.getScrollableSize()).toBe(viewportSize - sizeToRemove);
161+
expect(misc.getScrollableSize()).toBe(viewportSize + sizeToChange);
140162
done();
141163
};
142164

143165
describe('Adapter Replace Spec', () => {
144166

167+
const getTitle = ({ custom: { token, indexesToReplace: { length }, amount } }: TestBedConfig) =>
168+
`should replace ${token} ${length === 1 ? 'one' : length} to ${amount === 1 ? 'one' : amount}`;
169+
145170
describe('one-to-ne replacement', () =>
146171
configList.forEach(config =>
147172
makeTest({
148-
title: `should work (${config.custom.token})`,
173+
title: getTitle(config),
149174
config,
150175
it: shouldReplace(config)
151176
})
@@ -155,7 +180,7 @@ describe('Adapter Replace Spec', () => {
155180
describe('many-to-one replacement', () =>
156181
manyToOneConfigList.forEach(config =>
157182
makeTest({
158-
title: `should work (${config.custom.token})`,
183+
title: getTitle(config),
159184
config,
160185
it: shouldReplace(config)
161186
})
@@ -165,7 +190,17 @@ describe('Adapter Replace Spec', () => {
165190
describe('many-to-one increase replacement', () =>
166191
manyToOneIncreaseConfigList.forEach(config =>
167192
makeTest({
168-
title: `should work (${config.custom.token})`,
193+
title: getTitle(config),
194+
config,
195+
it: shouldReplace(config)
196+
})
197+
)
198+
);
199+
200+
describe('many-to-many replacement', () =>
201+
manyToManyConfigList.filter((i, j) => j >= 0).forEach(config =>
202+
makeTest({
203+
title: getTitle(config),
169204
config,
170205
it: shouldReplace(config)
171206
})

tests/scaffolding/datasources/class.ts

+21-11
Original file line numberDiff line numberDiff line change
@@ -76,20 +76,30 @@ export const getDatasourceReplacementsClass = (settings: Settings) =>
7676
}
7777

7878
replaceManyToOne(idsToReplace: number[], item: Item, increase: boolean) {
79+
this.replaceManyToMany(idsToReplace, [item], increase);
80+
}
81+
82+
replaceManyToMany(idsToReplace: number[], items: Item[], increase: boolean) {
7983
idsToReplace.sort((a, b) => a - b);
80-
const minId = idsToReplace[0];
81-
const maxId = idsToReplace.slice(1).reduce((acc, id) =>
82-
// only continuous series allowed
83-
id === acc + 1 ? id : acc, minId
84+
const minRem = idsToReplace[0];
85+
const maxRem = idsToReplace.slice(1).reduce((acc, id) =>
86+
id === acc + 1 ? id : acc, minRem // only continuous series allowed
8487
);
85-
const diff = maxId - minId;
86-
this.data = this.data.reduce((acc, _item: Item) => {
87-
if ((!increase && _item.id < minId) || (increase && _item.id > maxId)) {
88-
acc.push(_item);
89-
} else if ((increase && _item.id === minId) || (!increase && _item.id === maxId)) {
88+
const itemsToRemove = maxRem - minRem + 1;
89+
const diff = itemsToRemove - items.length;
90+
91+
let inserted = false;
92+
this.data = this.data.reduce((acc, item: Item) => {
93+
if ((!increase && item.id < minRem) || (increase && item.id > maxRem)) {
94+
// below (or above if increase): persist
9095
acc.push(item);
91-
} else if ((!increase && _item.id > maxId) || (increase && _item.id < minId)) {
92-
acc.push({ ..._item, id: _item.id + (increase ? 1 : -1) * diff });
96+
} else if ((!increase && item.id > maxRem) || (increase && item.id < minRem)) {
97+
// above (or below if increase): shift
98+
acc.push({ ...item, id: item.id + (!increase ? -1 : 1) * diff });
99+
} else if (!inserted) {
100+
// in the middle: replace
101+
acc.push(...items);
102+
inserted = true;
93103
}
94104
return acc;
95105
}, [] as Item[]);

0 commit comments

Comments
 (0)