From 079921c5a28461a0b83b66fbd9b2b23f4a4d34f1 Mon Sep 17 00:00:00 2001 From: Maciej Rybaniec Date: Fri, 30 Oct 2020 13:17:55 +0100 Subject: [PATCH] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20autorun=20enhancement?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/js/app/components/App.tsx | 4 +- .../AutorunQuery/AutorunQuery.styles.ts | 22 +++++++ .../AutorunQuery/AutorunQuery.test.tsx | 25 +++++++- .../components/AutorunQuery/AutorunQuery.tsx | 56 ++++++++++++++--- .../BrowserPreview/BrowserPreview.tsx | 1 + lib/js/app/modules/app/reducer.test.ts | 4 +- lib/js/app/modules/app/reducer.ts | 2 +- lib/js/app/modules/savedQuery/actions.ts | 6 +- lib/js/app/modules/savedQuery/fixtures.ts | 21 +++++++ lib/js/app/modules/savedQuery/saga.test.ts | 62 +++++++++++++++++++ lib/js/app/modules/savedQuery/saga.ts | 6 +- lib/js/app/modules/savedQuery/types.ts | 1 + public/locales/en/translation.json | 3 +- 13 files changed, 194 insertions(+), 19 deletions(-) create mode 100644 lib/js/app/modules/savedQuery/fixtures.ts create mode 100644 lib/js/app/modules/savedQuery/saga.test.ts diff --git a/lib/js/app/components/App.tsx b/lib/js/app/components/App.tsx index c5149b020..8ce6641ba 100644 --- a/lib/js/app/components/App.tsx +++ b/lib/js/app/components/App.tsx @@ -19,6 +19,7 @@ import { getViewMode, setViewMode, getVisualization, + getQueryAutorun, switchToQueriesList, createNewQuery, explorerMounted, @@ -47,6 +48,7 @@ import { const mapStateToProps = (state: AppState) => ({ savedQuery: getSavedQuery(state), visualization: getVisualization(state), + autorunQuery: getQueryAutorun(state), view: getViewMode(state), query: getQuerySettings(state), }); @@ -134,7 +136,7 @@ class App extends Component { onRunQuery={() => this.props.runQuery(this.props.query)} onSelectQuery={(queryName) => { this.props.resetQueryResults(); - this.props.selectSavedQuery(queryName); + this.props.selectSavedQuery(queryName, this.props.autorunQuery); }} onEditQuery={(queryName) => { this.props.editQuery(queryName); diff --git a/lib/js/app/components/AutorunQuery/AutorunQuery.styles.ts b/lib/js/app/components/AutorunQuery/AutorunQuery.styles.ts index 29f3a2108..12c84615b 100644 --- a/lib/js/app/components/AutorunQuery/AutorunQuery.styles.ts +++ b/lib/js/app/components/AutorunQuery/AutorunQuery.styles.ts @@ -1,5 +1,7 @@ import styled from 'styled-components'; +import { motion } from 'framer-motion'; import { colors } from '@keen.io/colors'; +import { UI_LAYERS } from '@keen.io/ui-core'; export const Container = styled.div` display: flex; @@ -13,3 +15,23 @@ export const Label = styled.div` color: ${colors.black[100]}; margin-right: 10px; `; + +export const HintContainer = styled.div` + display: flex; + align-items: center; + margin-right: 5px; + position: relative; +`; + +export const TooltipMotion = styled(motion.div)` + position: absolute; + top: 100%; + z-index: ${UI_LAYERS.tooltip}; +`; + +export const TooltipContent = styled.div` + width: 120px; + color: ${colors.black[100]}; + font-size: 14px; + font-family: Lato Regular, sans-serif; +`; diff --git a/lib/js/app/components/AutorunQuery/AutorunQuery.test.tsx b/lib/js/app/components/AutorunQuery/AutorunQuery.test.tsx index af561b76f..85973edb9 100644 --- a/lib/js/app/components/AutorunQuery/AutorunQuery.test.tsx +++ b/lib/js/app/components/AutorunQuery/AutorunQuery.test.tsx @@ -6,7 +6,12 @@ import AutorunQuery from './AutorunQuery'; test('calls on "onToggle" event handler', () => { const mockFn = jest.fn(); const { getByTestId } = render( - + ); const element = getByTestId('toggle'); @@ -14,3 +19,21 @@ test('calls on "onToggle" event handler', () => { expect(mockFn).toHaveBeenCalledWith(true); }); + +test('shows tooltip with hint message', () => { + const mockFn = jest.fn(); + const tooltipMessage = 'tooltipMessage'; + const { getByTestId, getByText } = render( + + ); + + const element = getByTestId('hint-container'); + fireEvent.mouseEnter(element); + + expect(getByText(tooltipMessage)).toBeInTheDocument(); +}); diff --git a/lib/js/app/components/AutorunQuery/AutorunQuery.tsx b/lib/js/app/components/AutorunQuery/AutorunQuery.tsx index e607b9563..27e4b9c54 100644 --- a/lib/js/app/components/AutorunQuery/AutorunQuery.tsx +++ b/lib/js/app/components/AutorunQuery/AutorunQuery.tsx @@ -1,7 +1,18 @@ -import React, { FC } from 'react'; -import { Toggle } from '@keen.io/ui-core'; +import React, { FC, useState } from 'react'; +import { AnimatePresence } from 'framer-motion'; +import { Toggle, Tooltip } from '@keen.io/ui-core'; +import { Icon } from '@keen.io/icons'; +import { colors } from '@keen.io/colors'; -import { Container, Label } from './AutorunQuery.styles'; +import { + Container, + HintContainer, + Label, + TooltipContent, + TooltipMotion, +} from './AutorunQuery.styles'; + +import { TOOLTIP_MOTION } from '../../constants'; type Props = { /** Toggle autorun event handler */ @@ -10,13 +21,40 @@ type Props = { autorun: boolean; /** Settings label */ label: string; + /** Autorun tooltip hint message */ + tooltipMessage: string; }; -const AutorunQuery: FC = ({ autorun, label, onToggle }) => ( - - - - -); +const AutorunQuery: FC = ({ + autorun, + label, + tooltipMessage, + onToggle, +}) => { + const [showTooltip, setTooltipVisibility] = useState(false); + + return ( + + setTooltipVisibility(true)} + onMouseLeave={() => setTooltipVisibility(false)} + > + + + {showTooltip && ( + + + {tooltipMessage} + + + )} + + + + + + ); +}; export default AutorunQuery; diff --git a/lib/js/app/components/BrowserPreview/BrowserPreview.tsx b/lib/js/app/components/BrowserPreview/BrowserPreview.tsx index 18ef20c0f..f4034ae3d 100644 --- a/lib/js/app/components/BrowserPreview/BrowserPreview.tsx +++ b/lib/js/app/components/BrowserPreview/BrowserPreview.tsx @@ -60,6 +60,7 @@ const BrowserPreview: FC = ({ dispatch(setQueryAutorun(autorun))} /> diff --git a/lib/js/app/modules/app/reducer.test.ts b/lib/js/app/modules/app/reducer.test.ts index 3dd1b77bc..b3df7586d 100644 --- a/lib/js/app/modules/app/reducer.test.ts +++ b/lib/js/app/modules/app/reducer.test.ts @@ -20,10 +20,10 @@ import { } from './actions'; test('set query autorun settings', () => { - const action = setQueryAutorun(true); + const action = setQueryAutorun(false); const { autorunQuery } = appReducer(initialState, action); - expect(autorunQuery).toBeTruthy(); + expect(autorunQuery).toBeFalsy(); }); test('shows extraction settings modal', () => { diff --git a/lib/js/app/modules/app/reducer.ts b/lib/js/app/modules/app/reducer.ts index 3fae24e0f..560059f35 100644 --- a/lib/js/app/modules/app/reducer.ts +++ b/lib/js/app/modules/app/reducer.ts @@ -20,7 +20,7 @@ import { } from './constants'; export const initialState: ReducerState = { - autorunQuery: false, + autorunQuery: true, confirmModal: { visible: false, action: null, diff --git a/lib/js/app/modules/savedQuery/actions.ts b/lib/js/app/modules/savedQuery/actions.ts index 54db0507d..fbe8a7c42 100644 --- a/lib/js/app/modules/savedQuery/actions.ts +++ b/lib/js/app/modules/savedQuery/actions.ts @@ -13,10 +13,14 @@ export const updateSaveQuery = ( payload: properties, }); -export const selectSavedQuery = (name: string): SavedQueryActions => ({ +export const selectSavedQuery = ( + name: string, + autorunQuery?: boolean +): SavedQueryActions => ({ type: SELECT_SAVED_QUERY, payload: { name, + autorunQuery, }, }); diff --git a/lib/js/app/modules/savedQuery/fixtures.ts b/lib/js/app/modules/savedQuery/fixtures.ts new file mode 100644 index 000000000..e88ec5507 --- /dev/null +++ b/lib/js/app/modules/savedQuery/fixtures.ts @@ -0,0 +1,21 @@ +/* eslint-disable @typescript-eslint/camelcase */ + +export const savedQueries = [ + { + refreshRate: 0, + cached: false, + displayName: 'purchases', + name: 'purchases', + tags: [], + visualization: { + type: 'bar', + chartSettings: { + layout: 'vertical', + }, + widgetSettings: {}, + }, + query: { + analysis_type: 'count', + }, + }, +]; diff --git a/lib/js/app/modules/savedQuery/saga.test.ts b/lib/js/app/modules/savedQuery/saga.test.ts new file mode 100644 index 000000000..8abe34407 --- /dev/null +++ b/lib/js/app/modules/savedQuery/saga.test.ts @@ -0,0 +1,62 @@ +/* eslint-disable @typescript-eslint/camelcase */ +import sagaHelper from 'redux-saga-testing'; +import { select, put } from 'redux-saga/effects'; +import { Layout } from '@keen.io/ui-core'; + +import { selectSavedQuery, updateSaveQuery } from './actions'; +import { selectSavedQuery as selectSavedQueryFlow } from './saga'; + +import { runQuery, getSavedQueries, setQuerySettings } from '../queries'; +import { setVisualization } from '../app'; + +import { savedQueries } from './fixtures'; + +import { SelectSavedQueryAction } from './types'; + +describe('selectSavedQuery()', () => { + describe('Scenario 1: User selects query with enabled autorun', () => { + const action = selectSavedQuery('purchases', true); + const it = sagaHelper( + selectSavedQueryFlow(action as SelectSavedQueryAction) + ); + + it('get list of saved queries from state', (result) => { + expect(result).toEqual(select(getSavedQueries)); + return savedQueries; + }); + + it('setup visualization type', (result) => { + const chartSettings = { + layout: 'vertical' as Layout, + }; + + expect(result).toEqual(put(setVisualization('bar', chartSettings, {}))); + }); + + it('setup query settings', (result) => { + const query = { + analysis_type: 'count', + }; + + expect(result).toEqual(put(setQuerySettings(query))); + }); + + it('updates save query settings', (result) => { + const savedQuery = { + cached: false, + displayName: 'purchases', + name: 'purchases', + tags: [], + }; + expect(result).toMatchObject(put(updateSaveQuery(savedQuery))); + }); + + it('runs selected query', (result) => { + const query = { + analysis_type: 'count', + }; + + expect(result).toEqual(put(runQuery(query))); + }); + }); +}); diff --git a/lib/js/app/modules/savedQuery/saga.ts b/lib/js/app/modules/savedQuery/saga.ts index 2f45a2b57..fed8e3c51 100644 --- a/lib/js/app/modules/savedQuery/saga.ts +++ b/lib/js/app/modules/savedQuery/saga.ts @@ -4,7 +4,7 @@ import { takeLatest, select, put } from 'redux-saga/effects'; import { updateSaveQuery } from './actions'; -import { setVisualization, getQueryAutorun } from '../app'; +import { setVisualization } from '../app'; import { SavedQueryListItem } from '../queries'; import { setQuerySettings, @@ -21,7 +21,7 @@ import { SavedQueryAPIResponse } from '../../types'; import { serializeSavedQuery, convertMilisecondsToMinutes } from './utils'; -function* selectSavedQuery({ payload }: SelectSavedQueryAction) { +export function* selectSavedQuery({ payload }: SelectSavedQueryAction) { const savedQueries: SavedQueryListItem[] = yield select(getSavedQueries); try { @@ -50,7 +50,7 @@ function* selectSavedQuery({ payload }: SelectSavedQueryAction) { yield put(setQuerySettings(query)); yield put(updateSaveQuery(savedQuery)); - const autorunQuery = yield select(getQueryAutorun); + const { autorunQuery } = payload; if (autorunQuery) { yield put(runQuery(query)); } diff --git a/lib/js/app/modules/savedQuery/types.ts b/lib/js/app/modules/savedQuery/types.ts index 0e23ba71d..86148793e 100644 --- a/lib/js/app/modules/savedQuery/types.ts +++ b/lib/js/app/modules/savedQuery/types.ts @@ -29,6 +29,7 @@ export interface SelectSavedQueryAction { type: typeof SELECT_SAVED_QUERY; payload: { name: string; + autorunQuery?: boolean; }; } diff --git a/public/locales/en/translation.json b/public/locales/en/translation.json index c04895bc7..452c5ada1 100644 --- a/public/locales/en/translation.json +++ b/public/locales/en/translation.json @@ -99,7 +99,8 @@ }, "browser_preview": { "title": "Preview", - "autorun_query_label": "Auto-run selected query" + "autorun_query_label": "Auto-run selected query", + "autorun_query_tooltip": "Every run query count as one query for your usage" }, "query_summary": { "analysis": "Analysis:",