diff --git a/lib/js/app/modules/queries/actions.ts b/lib/js/app/modules/queries/actions.ts index 9c8c0890c..38efc0456 100644 --- a/lib/js/app/modules/queries/actions.ts +++ b/lib/js/app/modules/queries/actions.ts @@ -33,7 +33,10 @@ export const setQueryCacheLimitError = (error: Error): QueriesActions => ({ payload: { error }, }); -export const saveQuery = (name: string, body: Record): QueriesActions => ({ +export const saveQuery = ( + name: string, + body: Record +): QueriesActions => ({ type: SAVE_QUERY, payload: { name, body }, }); @@ -73,7 +76,9 @@ export const getSavedQueries = (): QueriesActions => ({ type: GET_SAVED_QUERIES, }); -export const getSavedQueriesSuccess = (queries: Record[]): QueriesActions => ({ +export const getSavedQueriesSuccess = ( + queries: Record[] +): QueriesActions => ({ type: GET_SAVED_QUERIES_SUCCESS, payload: { queries }, }); @@ -88,7 +93,9 @@ export const runQuery = (body: Record): QueriesActions => ({ payload: { body }, }); -export const runQuerySuccess = (results: Record): QueriesActions => ({ +export const runQuerySuccess = ( + results: Record +): QueriesActions => ({ type: RUN_QUERY_SUCCESS, payload: { results }, }); diff --git a/lib/js/app/queryCreator/components/AbsoluteTime/__snapshots__/AbsoluteTime.test.tsx.snap b/lib/js/app/queryCreator/components/AbsoluteTime/__snapshots__/AbsoluteTime.test.tsx.snap index 6949e4837..94542d92d 100644 --- a/lib/js/app/queryCreator/components/AbsoluteTime/__snapshots__/AbsoluteTime.test.tsx.snap +++ b/lib/js/app/queryCreator/components/AbsoluteTime/__snapshots__/AbsoluteTime.test.tsx.snap @@ -46,6 +46,27 @@ exports[`renders date picker for timeframe end 1`] = ` box-shadow: 0 0 3px 1px rgba(119,163,187,0.5); } +.c0 .DateInput_input:disabled { + border: 1px solid rgba(39,86,109,0.5); + background: #FFFFFF; +} + +.c0 .DateInput_input:disabled::-webkit-input-placeholder { + color: rgba(39,52,55,0.4); +} + +.c0 .DateInput_input:disabled::-moz-placeholder { + color: rgba(39,52,55,0.4); +} + +.c0 .DateInput_input:disabled:-ms-input-placeholder { + color: rgba(39,52,55,0.4); +} + +.c0 .DateInput_input:disabled::placeholder { + color: rgba(39,52,55,0.4); +} + .c0 .DateInput_fang { display: none; } @@ -141,6 +162,27 @@ exports[`renders date picker for timeframe start 1`] = ` box-shadow: 0 0 3px 1px rgba(119,163,187,0.5); } +.c0 .DateInput_input:disabled { + border: 1px solid rgba(39,86,109,0.5); + background: #FFFFFF; +} + +.c0 .DateInput_input:disabled::-webkit-input-placeholder { + color: rgba(39,52,55,0.4); +} + +.c0 .DateInput_input:disabled::-moz-placeholder { + color: rgba(39,52,55,0.4); +} + +.c0 .DateInput_input:disabled:-ms-input-placeholder { + color: rgba(39,52,55,0.4); +} + +.c0 .DateInput_input:disabled::placeholder { + color: rgba(39,52,55,0.4); +} + .c0 .DateInput_fang { display: none; } diff --git a/lib/js/app/queryCreator/components/GroupBy/GroupBy.test.tsx b/lib/js/app/queryCreator/components/GroupBy/GroupBy.test.tsx index 482d356f5..b196963ac 100644 --- a/lib/js/app/queryCreator/components/GroupBy/GroupBy.test.tsx +++ b/lib/js/app/queryCreator/components/GroupBy/GroupBy.test.tsx @@ -1,14 +1,10 @@ import React from 'react'; import { Provider } from 'react-redux'; -import { - render as rtlRender, - fireEvent, -} from '@testing-library/react'; +import { render as rtlRender, fireEvent } from '@testing-library/react'; import configureStore from 'redux-mock-store'; import GroupBy from './GroupBy'; - const render = (storeState: any = {}, overProps: any = {}) => { const mockStore = configureStore([]); const store = mockStore({ ...storeState }); diff --git a/lib/js/app/queryCreator/components/Input/Input.styles.ts b/lib/js/app/queryCreator/components/Input/Input.styles.ts index e139de1b4..1748ec546 100644 --- a/lib/js/app/queryCreator/components/Input/Input.styles.ts +++ b/lib/js/app/queryCreator/components/Input/Input.styles.ts @@ -19,6 +19,15 @@ export const inputMixin = () => css` &:focus { box-shadow: 0 0 3px 1px rgba(119, 163, 187, 0.5); } + + &:disabled { + border: 1px solid ${transparentize(0.5, colors.blue[500])}; + background: ${colors.white[500]}; + } + + &:disabled::placeholder { + color: ${transparentize(0.6, colors.black[400])}; + } `; export const Input = styled.input` diff --git a/lib/js/app/queryCreator/components/Limit/Limit.test.tsx b/lib/js/app/queryCreator/components/Limit/Limit.test.tsx index ce15a5b23..c4f78290e 100644 --- a/lib/js/app/queryCreator/components/Limit/Limit.test.tsx +++ b/lib/js/app/queryCreator/components/Limit/Limit.test.tsx @@ -21,12 +21,41 @@ const render = (storeState: any = {}) => { }; }; +test('allows user to set limit', () => { + const { + wrapper: { getByTestId }, + store, + } = render({ + query: { + groupBy: ['category'], + orderBy: [{ propertyName: 'result', direction: 'DESC' }], + limit: undefined, + }, + }); + const input = getByTestId('limit'); + + fireEvent.change(input, { target: { value: 80 } }); + + expect(store.getActions()).toMatchInlineSnapshot(` + Array [ + Object { + "payload": Object { + "limit": 80, + }, + "type": "@query-creator/SET_LIMIT", + }, + ] + `); +}); + test('allows user to set limit', () => { const { wrapper: { container }, store, } = render({ query: { + groupBy: ['category'], + orderBy: [{ propertyName: 'result', direction: 'DESC' }], limit: undefined, }, }); @@ -46,16 +75,48 @@ test('allows user to set limit', () => { `); }); +test('do not allows user to set limit without order settings', () => { + const { + wrapper: { getByTestId }, + } = render({ + query: { + groupBy: ['category'], + orderBy: undefined, + limit: undefined, + }, + }); + const input = getByTestId('limit'); + + expect(input).toBeDisabled(); +}); + +test('do not allows user to set limit without group by settings', () => { + const { + wrapper: { getByTestId }, + } = render({ + query: { + groupBy: undefined, + orderBy: [{ propertyName: 'result', direction: 'DESC' }], + limit: undefined, + }, + }); + const input = getByTestId('limit'); + + expect(input).toBeDisabled(); +}); + test('allows user to remove limit', () => { const { - wrapper: { container }, + wrapper: { getByTestId }, store, } = render({ query: { limit: 100, + groupBy: ['category'], + orderBy: [{ propertyName: 'result', direction: 'DESC' }], }, }); - const input = container.querySelector('input[type="number"]'); + const input = getByTestId('limit'); fireEvent.change(input, { target: { value: null } }); diff --git a/lib/js/app/queryCreator/components/Limit/Limit.tsx b/lib/js/app/queryCreator/components/Limit/Limit.tsx index af1413ad7..774c572f0 100644 --- a/lib/js/app/queryCreator/components/Limit/Limit.tsx +++ b/lib/js/app/queryCreator/components/Limit/Limit.tsx @@ -1,8 +1,15 @@ import React, { FC, useCallback, useEffect } from 'react'; import { useDispatch, useSelector } from 'react-redux'; -import { Label, Input } from '@keen.io/ui-core'; -import { setLimit, getLimit } from '../../modules/query'; +import Title from '../Title'; +import Input from '../Input'; + +import { + setLimit, + getLimit, + getGroupBy, + getOrderBy, +} from '../../modules/query'; import text from './text.json'; @@ -11,6 +18,9 @@ type Props = {}; const Limit: FC = () => { const dispatch = useDispatch(); const limit = useSelector(getLimit); + const groupBy = useSelector(getGroupBy); + const orderBy = useSelector(getOrderBy); + const isDisabled = !groupBy || !orderBy; const changeHandler = useCallback((eventValue) => { if (eventValue) { @@ -25,14 +35,22 @@ const Limit: FC = () => { return () => dispatch(setLimit(undefined)); }, []); + useEffect(() => { + if (isDisabled) { + dispatch(setLimit(undefined)); + } + }, [isDisabled]); + return ( <> - + {text.label} changeHandler(e.target.value)} /> diff --git a/lib/js/app/queryCreator/components/Limit/text.json b/lib/js/app/queryCreator/components/Limit/text.json index e01a34963..00bdb1d93 100644 --- a/lib/js/app/queryCreator/components/Limit/text.json +++ b/lib/js/app/queryCreator/components/Limit/text.json @@ -1,3 +1,4 @@ { - "label": "Limit" + "label": "Limit results to", + "placeholder": "Limit to" } diff --git a/lib/js/app/queryCreator/components/OrderBy/OrderBy.test.tsx b/lib/js/app/queryCreator/components/OrderBy/OrderBy.test.tsx index 98273281e..a041cdae2 100644 --- a/lib/js/app/queryCreator/components/OrderBy/OrderBy.test.tsx +++ b/lib/js/app/queryCreator/components/OrderBy/OrderBy.test.tsx @@ -107,7 +107,7 @@ test('allows user to remove order by settings', () => { Array [ Object { "payload": Object { - "orderBy": Array [], + "orderBy": undefined, }, "type": "@query-creator/SET_ORDER_BY", }, diff --git a/lib/js/app/queryCreator/components/OrderBy/OrderBy.tsx b/lib/js/app/queryCreator/components/OrderBy/OrderBy.tsx index 42fe07053..e34337697 100644 --- a/lib/js/app/queryCreator/components/OrderBy/OrderBy.tsx +++ b/lib/js/app/queryCreator/components/OrderBy/OrderBy.tsx @@ -62,7 +62,8 @@ const OrderBy: FC = () => { const removeOrderBy = useCallback( (index: number) => { - const orderBySettings = orderBy.filter((_order, idx) => index !== idx); + let orderBySettings = orderBy.filter((_order, idx) => index !== idx); + if (orderBySettings.length === 0) orderBySettings = undefined; dispatch(setOrderBy(orderBySettings)); }, [orderBy] @@ -84,58 +85,62 @@ const OrderBy: FC = () => { - {orderBy.map(({ propertyName, direction }, idx) => ( -
-
removeOrderBy(idx)} - > - + {orderBy && + orderBy.map(({ propertyName, direction }, idx) => ( +
+
removeOrderBy(idx)} + > + +
+ + { + const orderSettings = { propertyName, direction: value }; + updateOrderBy(orderSettings as OrderBySettings, idx); + }} + placeholder={text.directionPlaceholder} + value={ + direction + ? { label: DIRECTION_LABELS[direction], value: direction } + : null + } + />
- - { - const orderSettings = { propertyName, direction: value }; - updateOrderBy(orderSettings as OrderBySettings, idx); - }} - placeholder={text.directionPlaceholder} - value={ - direction - ? { label: DIRECTION_LABELS[direction], value: direction } - : null - } - /> -
- ))} + ))}
) : ( text.specifyGroupBy diff --git a/lib/js/app/queryCreator/components/OrderBy/utils/serializeOrderBy.test.ts b/lib/js/app/queryCreator/components/OrderBy/utils/serializeOrderBy.test.ts index 11e761a5f..0e4291a96 100644 --- a/lib/js/app/queryCreator/components/OrderBy/utils/serializeOrderBy.test.ts +++ b/lib/js/app/queryCreator/components/OrderBy/utils/serializeOrderBy.test.ts @@ -26,7 +26,7 @@ test('serialize empty orderBy settings', () => { const orderBy = undefined; const result = serializeOrderBy(orderBy); - expect(result).toEqual([]); + expect(result).toEqual(undefined); }); test('serialize collection of order settings', () => { diff --git a/lib/js/app/queryCreator/components/OrderBy/utils/serializeOrderBy.ts b/lib/js/app/queryCreator/components/OrderBy/utils/serializeOrderBy.ts index ab9bf88f0..ffbdce41e 100644 --- a/lib/js/app/queryCreator/components/OrderBy/utils/serializeOrderBy.ts +++ b/lib/js/app/queryCreator/components/OrderBy/utils/serializeOrderBy.ts @@ -15,5 +15,5 @@ export const serializeOrderBy = ( if (Array.isArray(orderBy)) return orderBy; if (typeof orderBy === 'object' && orderBy !== null) return [orderBy]; - return []; + return undefined; }; diff --git a/lib/js/app/queryCreator/utils/serializeQuery.tsx b/lib/js/app/queryCreator/utils/serializeQuery.tsx index 1ffa0b9ee..e5b4cd2fc 100644 --- a/lib/js/app/queryCreator/utils/serializeQuery.tsx +++ b/lib/js/app/queryCreator/utils/serializeQuery.tsx @@ -1,5 +1,6 @@ import camelCase from 'camelcase-keys'; import { ReducerState as QueryState } from '../modules/query'; -export const serializeQuery = (query: Record): Partial => - camelCase(query, { deep: true }); +export const serializeQuery = ( + query: Record +): Partial => camelCase(query, { deep: true });