Skip to content

Commit

Permalink
[App Search] Wired up the suggestions table to logic (#113322)
Browse files Browse the repository at this point in the history
  • Loading branch information
JasonStoltz authored Sep 29, 2021
1 parent a7874ff commit 39899f8
Show file tree
Hide file tree
Showing 8 changed files with 384 additions and 32 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

import {
LogicMounter,
mockFlashMessageHelpers,
mockHttpValues,
} from '../../../../__mocks__/kea_logic';
import '../../../__mocks__/engine_logic.mock';

import { nextTick } from '@kbn/test/jest';

import { DEFAULT_META } from '../../../../shared/constants';

import { SuggestionsLogic } from './suggestions_logic';

const DEFAULT_VALUES = {
dataLoading: true,
suggestions: [],
meta: {
...DEFAULT_META,
page: {
...DEFAULT_META.page,
size: 10,
},
},
};

const MOCK_RESPONSE = {
meta: {
page: {
current: 1,
size: 10,
total_results: 1,
total_pages: 1,
},
},
results: [
{
query: 'foo',
updated_at: '2021-07-08T14:35:50Z',
promoted: ['1', '2'],
},
],
};

describe('SuggestionsLogic', () => {
const { mount } = new LogicMounter(SuggestionsLogic);
const { flashAPIErrors } = mockFlashMessageHelpers;
const { http } = mockHttpValues;

beforeEach(() => {
jest.clearAllMocks();
});

it('has expected default values', () => {
mount();
expect(SuggestionsLogic.values).toEqual(DEFAULT_VALUES);
});

describe('actions', () => {
describe('onSuggestionsLoaded', () => {
it('should set suggestion, meta state, & dataLoading to false', () => {
mount();

SuggestionsLogic.actions.onSuggestionsLoaded(MOCK_RESPONSE);

expect(SuggestionsLogic.values).toEqual({
...DEFAULT_VALUES,
suggestions: MOCK_RESPONSE.results,
meta: MOCK_RESPONSE.meta,
dataLoading: false,
});
});
});

describe('onPaginate', () => {
it('should update meta', () => {
mount();

SuggestionsLogic.actions.onPaginate(2);

expect(SuggestionsLogic.values).toEqual({
...DEFAULT_VALUES,
meta: {
...DEFAULT_META,
page: {
...DEFAULT_META.page,
current: 2,
},
},
});
});
});
});

describe('listeners', () => {
describe('loadSuggestions', () => {
it('should set dataLoading state', () => {
mount({ dataLoading: false });

SuggestionsLogic.actions.loadSuggestions();

expect(SuggestionsLogic.values).toEqual({
...DEFAULT_VALUES,
dataLoading: true,
});
});

it('should make an API call and set suggestions & meta state', async () => {
http.post.mockReturnValueOnce(Promise.resolve(MOCK_RESPONSE));
mount();
jest.spyOn(SuggestionsLogic.actions, 'onSuggestionsLoaded');

SuggestionsLogic.actions.loadSuggestions();
await nextTick();

expect(http.post).toHaveBeenCalledWith(
'/internal/app_search/engines/some-engine/search_relevance_suggestions',
{
body: JSON.stringify({
page: {
current: 1,
size: 10,
},
filters: {
status: ['pending'],
type: 'curation',
},
}),
}
);

expect(SuggestionsLogic.actions.onSuggestionsLoaded).toHaveBeenCalledWith(MOCK_RESPONSE);
});

it('handles errors', async () => {
http.post.mockReturnValueOnce(Promise.reject('error'));
mount();

SuggestionsLogic.actions.loadSuggestions();
await nextTick();

expect(flashAPIErrors).toHaveBeenCalledWith('error');
});
});
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

import { kea, MakeLogicType } from 'kea';

import { Meta } from '../../../../../../common/types';
import { DEFAULT_META } from '../../../../shared/constants';
import { flashAPIErrors } from '../../../../shared/flash_messages';
import { HttpLogic } from '../../../../shared/http';
import { updateMetaPageIndex } from '../../../../shared/table_pagination';
import { EngineLogic } from '../../engine';
import { CurationSuggestion } from '../types';

interface SuggestionsAPIResponse {
results: CurationSuggestion[];
meta: Meta;
}

interface SuggestionsValues {
dataLoading: boolean;
suggestions: CurationSuggestion[];
meta: Meta;
}

interface SuggestionActions {
loadSuggestions(): void;
onPaginate(newPageIndex: number): { newPageIndex: number };
onSuggestionsLoaded(response: SuggestionsAPIResponse): SuggestionsAPIResponse;
}

export const SuggestionsLogic = kea<MakeLogicType<SuggestionsValues, SuggestionActions>>({
path: ['enterprise_search', 'app_search', 'curations', 'suggestions_logic'],
actions: () => ({
onPaginate: (newPageIndex) => ({ newPageIndex }),
onSuggestionsLoaded: ({ results, meta }) => ({ results, meta }),
loadSuggestions: true,
}),
reducers: () => ({
dataLoading: [
true,
{
loadSuggestions: () => true,
onSuggestionsLoaded: () => false,
},
],
suggestions: [
[],
{
onSuggestionsLoaded: (_, { results }) => results,
},
],
meta: [
{
...DEFAULT_META,
page: {
...DEFAULT_META.page,
size: 10,
},
},
{
onSuggestionsLoaded: (_, { meta }) => meta,
onPaginate: (state, { newPageIndex }) => updateMetaPageIndex(state, newPageIndex),
},
],
}),
listeners: ({ actions, values }) => ({
loadSuggestions: async () => {
const { meta } = values;
const { http } = HttpLogic.values;
const { engineName } = EngineLogic.values;

try {
const response = await http.post(
`/internal/app_search/engines/${engineName}/search_relevance_suggestions`,
{
body: JSON.stringify({
page: {
current: meta.page.current,
size: meta.page.size,
},
filters: {
status: ['pending'],
type: 'curation',
},
}),
}
);
actions.onSuggestionsLoaded(response);
} catch (e) {
flashAPIErrors(e);
}
},
}),
});
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
* 2.0.
*/

import { mockKibanaValues, setMockValues } from '../../../../__mocks__/kea_logic';
import '../../../../__mocks__/shallow_useeffect.mock';
import { mockKibanaValues, setMockActions, setMockValues } from '../../../../__mocks__/kea_logic';
import '../../../__mocks__/engine_logic.mock';

import React from 'react';
Expand All @@ -21,10 +22,31 @@ describe('SuggestionsTable', () => {

const values = {
engineName: 'some-engine',
dataLoading: false,
suggestions: [
{
query: 'foo',
updated_at: '2021-07-08T14:35:50Z',
promoted: ['1', '2'],
},
],
meta: {
page: {
current: 1,
size: 10,
total_results: 2,
},
},
};

const mockActions = {
loadSuggestions: jest.fn(),
onPaginate: jest.fn(),
};

beforeAll(() => {
setMockValues(values);
setMockActions(mockActions);
});

beforeEach(() => {
Expand Down Expand Up @@ -79,4 +101,17 @@ describe('SuggestionsTable', () => {
});
expect(navigateToUrl).toHaveBeenCalledWith('/engines/some-engine/curations/suggestions/foo');
});

it('fetches data on load', () => {
shallow(<SuggestionsTable />);

expect(mockActions.loadSuggestions).toHaveBeenCalled();
});

it('supports pagination', () => {
const wrapper = shallow(<SuggestionsTable />);
wrapper.find(EuiBasicTable).simulate('change', { page: { index: 0 } });

expect(mockActions.onPaginate).toHaveBeenCalledWith(1);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@
* 2.0.
*/

import React from 'react';
import React, { useEffect } from 'react';

import { useActions, useValues } from 'kea';

import { EuiBasicTable, EuiBasicTableColumn } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
Expand All @@ -22,6 +24,8 @@ import { generateEnginePath } from '../../engine';
import { CurationSuggestion } from '../types';
import { convertToDate } from '../utils';

import { SuggestionsLogic } from './suggestions_logic';

const getSuggestionRoute = (query: string) => {
return generateEnginePath(ENGINE_CURATION_SUGGESTION_PATH, { query });
};
Expand Down Expand Up @@ -74,32 +78,14 @@ const columns: Array<EuiBasicTableColumn<CurationSuggestion>> = [
];

export const SuggestionsTable: React.FC = () => {
// TODO wire up this data
const items: CurationSuggestion[] = [
{
query: 'foo',
updated_at: '2021-07-08T14:35:50Z',
promoted: ['1', '2'],
},
];
const meta = {
page: {
current: 1,
size: 10,
total_results: 100,
total_pages: 10,
},
};
const { loadSuggestions, onPaginate } = useActions(SuggestionsLogic);
const { meta, suggestions, dataLoading } = useValues(SuggestionsLogic);

useEffect(() => {
loadSuggestions();
}, [meta.page.current]);

const totalSuggestions = meta.page.total_results;
// TODO
// @ts-ignore
const onPaginate = (...params) => {
// eslint-disable-next-line no-console
console.log('paging...');
// eslint-disable-next-line no-console
console.log(params);
};
const isLoading = false;

return (
<DataPanel
Expand All @@ -126,10 +112,10 @@ export const SuggestionsTable: React.FC = () => {
>
<EuiBasicTable
columns={columns}
items={items}
items={suggestions}
responsive
hasActions
loading={isLoading}
loading={dataLoading}
pagination={{
...convertMetaToPagination(meta),
hidePerPageOptions: true,
Expand Down
Loading

0 comments on commit 39899f8

Please sign in to comment.