Skip to content

Commit

Permalink
fix: ensure panel stays closed with many pending requests
Browse files Browse the repository at this point in the history
  • Loading branch information
sarahdayan committed Dec 8, 2021
1 parent 43107e5 commit ac2b33f
Show file tree
Hide file tree
Showing 2 changed files with 61 additions and 60 deletions.
117 changes: 58 additions & 59 deletions packages/autocomplete-core/src/__tests__/concurrency.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,30 +14,12 @@ beforeEach(() => {

describe('concurrency', () => {
test('resolves the responses in order from getSources', async () => {
// These delays make the second query come back after the third one.
const sourcesDelays = [100, 150, 200];
const itemsDelays = [0, 150, 0];
let deferSourcesCount = -1;
let deferItemsCount = -1;

const getSources = ({ query }) => {
deferSourcesCount++;

return defer(() => {
return [
createSource({
getItems() {
deferItemsCount++;

return defer(
() => [{ label: query }],
itemsDelays[deferItemsCount]
);
},
}),
];
}, sourcesDelays[deferSourcesCount]);
};
const { timeout, delayedGetSources: getSources } = createDelayedGetSources({
// These delays make the second query come back after the third one.
sources: [100, 150, 200],
items: [0, 150, 0],
});

const onStateChange = jest.fn();
const autocomplete = createAutocomplete({ getSources, onStateChange });
const { onChange } = autocomplete.getInputProps({ inputElement: null });
Expand All @@ -49,10 +31,6 @@ describe('concurrency', () => {
userEvent.type(input, 'b');
userEvent.type(input, 'c');

const timeout = Math.max(
...sourcesDelays.map((delay, index) => delay + itemsDelays[index])
);

await defer(() => {}, timeout);

let stateHistory: Array<
Expand Down Expand Up @@ -98,27 +76,21 @@ describe('concurrency', () => {

describe('closing the panel with pending requests', () => {
describe('without debug mode', () => {
const delay = 300;

test('keeps the panel closed on Escape', async () => {
const onStateChange = jest.fn();
const getSources = jest.fn(() => {
return defer(() => {
return [
createSource({
getItems: () => [{ label: '1' }, { label: '2' }],
}),
];
}, delay);
const { timeout, delayedGetSources } = createDelayedGetSources({
sources: [100, 200],
});
const getSources = jest.fn(delayedGetSources);

const { inputElement } = createPlayground(createAutocomplete, {
onStateChange,
getSources,
});

userEvent.type(inputElement, 'a{esc}');
userEvent.type(inputElement, 'ab{esc}');

await defer(() => {}, delay);
await defer(() => {}, timeout);

expect(onStateChange).toHaveBeenLastCalledWith(
expect.objectContaining({
Expand All @@ -128,28 +100,24 @@ describe('concurrency', () => {
}),
})
);
expect(getSources).toHaveBeenCalledTimes(1);
expect(getSources).toHaveBeenCalledTimes(2);
});

test('keeps the panel closed on blur', async () => {
const onStateChange = jest.fn();
const getSources = jest.fn(() => {
return defer(() => {
return [
createSource({
getItems: () => [{ label: '1' }, { label: '2' }],
}),
];
}, delay);
const { timeout, delayedGetSources } = createDelayedGetSources({
sources: [100, 200],
});
const getSources = jest.fn(delayedGetSources);

const { inputElement } = createPlayground(createAutocomplete, {
onStateChange,
getSources,
});

userEvent.type(inputElement, 'a{enter}');

await defer(() => {}, delay);
await defer(() => {}, timeout);

expect(onStateChange).toHaveBeenLastCalledWith(
expect.objectContaining({
Expand All @@ -164,15 +132,11 @@ describe('concurrency', () => {

test('keeps the panel closed on touchstart blur', async () => {
const onStateChange = jest.fn();
const getSources = jest.fn(() => {
return defer(() => {
return [
createSource({
getItems: () => [{ label: '1' }, { label: '2' }],
}),
];
}, delay);
const { timeout, delayedGetSources } = createDelayedGetSources({
sources: [100, 200],
});
const getSources = jest.fn(delayedGetSources);

const {
getEnvironmentProps,
inputElement,
Expand All @@ -195,7 +159,7 @@ describe('concurrency', () => {
const customEvent = new CustomEvent('touchstart', { bubbles: true });
window.document.dispatchEvent(customEvent);

await defer(() => {}, delay);
await defer(() => {}, timeout);

expect(onStateChange).toHaveBeenLastCalledWith(
expect.objectContaining({
Expand Down Expand Up @@ -329,3 +293,38 @@ describe('concurrency', () => {
});
});
});

function createDelayedGetSources(delays: {
sources: number[];
items?: number[];
}) {
let deferSourcesCount = -1;
let deferItemsCount = -1;

const itemsDelays = delays.items || delays.sources.map(() => 0);

const timeout = Math.max(
...delays.sources.map((delay, index) => delay + itemsDelays[index])
);

function delayedGetSources({ query }) {
deferSourcesCount++;

return defer(() => {
return [
createSource({
getItems() {
deferItemsCount++;

return defer(
() => [{ label: query }],
itemsDelays[deferItemsCount]
);
},
}),
];
}, delays.sources[deferSourcesCount]);
}

return { timeout, delayedGetSources };
}
4 changes: 3 additions & 1 deletion packages/autocomplete-core/src/onInput.ts
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,9 @@ export function onInput<TItem extends BaseItem>({
setStatus('idle');

if (store.shouldSkipPendingUpdate) {
store.shouldSkipPendingUpdate = false;
if (!runConcurrentSafePromise.isRunning()) {
store.shouldSkipPendingUpdate = false;
}

return;
}
Expand Down

0 comments on commit ac2b33f

Please sign in to comment.