Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/AN-4135 pass query parameters to components #74

Merged
34 changes: 27 additions & 7 deletions bitmovin-analytics-datasource/src/components/FilterInput.tsx
Original file line number Diff line number Diff line change
@@ -1,43 +1,63 @@
import React from 'react';
import { SelectableValue } from '@grafana/data';
import type { SelectableValue } from '@grafana/data';
import { HorizontalGroup, IconButton, Input, Select, Tooltip } from '@grafana/ui';
import { isEmpty } from 'lodash';

import { QueryAttribute } from '../types/queryAttributes';
import { QueryAdAttribute } from '../types/queryAdAttributes';
import { QueryAttribute, SELECTABLE_QUERY_ATTRIBUTES } from '../types/queryAttributes';
import { QueryAdAttribute, SELECTABLE_QUERY_AD_ATTRIBUTES } from '../types/queryAdAttributes';
import { QueryFilterOperator, SELECTABLE_QUERY_FILTER_OPERATORS } from '../types/queryFilter';
import type { FilterRowData } from './QueryEditor';

const mapAttributeToSelectableValue = (
attribute: QueryAttribute | QueryAdAttribute,
isAdAnalytics: boolean
): SelectableValue<QueryAttribute | QueryAdAttribute> => {
if (isAdAnalytics) {
return SELECTABLE_QUERY_AD_ATTRIBUTES.filter((selectableValue) => selectableValue.value === attribute);
} else {
return SELECTABLE_QUERY_ATTRIBUTES.filter((selectableValue) => selectableValue.value === attribute);
}
};

const mapOperatorToSelectableOperator = (operator: QueryFilterOperator): SelectableValue<QueryFilterOperator> => {
return SELECTABLE_QUERY_FILTER_OPERATORS.filter((selectableOperator) => selectableOperator.value === operator);
};

type Props = {
readonly isAdAnalytics: boolean;
readonly filter: FilterRowData;
readonly selectableFilterAttributes: Array<SelectableValue<QueryAttribute | QueryAdAttribute>>;
readonly onAttributeChange: (newValue: SelectableValue<QueryAdAttribute | QueryAttribute>) => void;
readonly onOperatorChange: (newValue: SelectableValue<QueryFilterOperator>) => void;
readonly onValueChange: (newValue: string) => void;
readonly onDelete: () => void;
readonly addFilterDisabled: boolean;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this should be resp. of this component based on input prop filter

readonly onAddFilter: () => void;
readonly parsingValueError: string | undefined;
};

export function FilterInput(props: Props) {
return (
<HorizontalGroup spacing="xs">
<Select
value={mapAttributeToSelectableValue(props.filter.attribute, props.isAdAnalytics)}
onChange={(value) => props.onAttributeChange(value)}
options={props.selectableFilterAttributes}
width={30}
/>
<Select
value={mapOperatorToSelectableOperator(props.filter.operator)}
onChange={(value) => props.onOperatorChange(value)}
options={SELECTABLE_QUERY_FILTER_OPERATORS}
width={15}
/>
<Tooltip
content={props.parsingValueError ? props.parsingValueError : ''}
show={!!props.parsingValueError}
content={isEmpty(props.filter.parsingValueError) ? '' : props.filter.parsingValueError}
show={!isEmpty(props.filter.parsingValueError)}
theme="error"
>
<Input
invalid={!!props.parsingValueError}
value={props.filter.rawFilterValue}
invalid={!isEmpty(props.filter.parsingValueError)}
type="text"
onChange={(value) => props.onValueChange(value.currentTarget.value)}
width={30}
Expand Down
121 changes: 60 additions & 61 deletions bitmovin-analytics-datasource/src/components/FilterRow.tsx
Original file line number Diff line number Diff line change
@@ -1,136 +1,135 @@
import React, { useState } from 'react';
import { difference, isEmpty } from 'lodash';
import React from 'react';
import { differenceWith, isEmpty } from 'lodash';
import { SelectableValue } from '@grafana/data';
import { Box, HorizontalGroup, IconButton, InlineLabel, VerticalGroup } from '@grafana/ui';

import { QueryFilter, QueryFilterOperator, QueryFilterValue } from '../types/queryFilter';
import { QueryFilter, QueryFilterOperator } from '../types/queryFilter';
import { QueryAdAttribute, SELECTABLE_QUERY_AD_ATTRIBUTES } from '../types/queryAdAttributes';
import { QueryAttribute, SELECTABLE_QUERY_ATTRIBUTES } from '../types/queryAttributes';
import { FilterInput } from './FilterInput';
import { convertFilterValueToProperType } from '../utils/filterUtils';

type Filter = {
selectedAttribute: SelectableValue<QueryAdAttribute | QueryAttribute>;
selectedOperator: SelectableValue<QueryFilterOperator>;
rawFilterValue: string;
convertedFilterValue: QueryFilterValue;
parsingValueError: string;
};
import type { FilterRowData } from './QueryEditor';

const mapFilterAttributesToSelectableValue = (
filters: Filter[],
filters: FilterRowData[],
isAdAnalytics: boolean
): Array<SelectableValue<QueryAttribute | QueryAdAttribute>> => {
const selectedAttributes = filters.map((filter) => filter.selectedAttribute);
if (isAdAnalytics) {
return difference(SELECTABLE_QUERY_AD_ATTRIBUTES, selectedAttributes);
return differenceWith(
SELECTABLE_QUERY_AD_ATTRIBUTES,
filters,
(selectableValue, selectedValue) => selectableValue.value === selectedValue.attribute
);
} else {
return difference(SELECTABLE_QUERY_ATTRIBUTES, selectedAttributes);
return differenceWith(
SELECTABLE_QUERY_ATTRIBUTES,
filters,
(selectableValue, selectedValue) => selectableValue.value === selectedValue.attribute
);
}
};

const mapFiltersToQueryFilters = (filters: Filter[]): QueryFilter[] => {
const mapFilterRowsToQueryFilters = (filters: FilterRowData[]): QueryFilter[] => {
return filters.map((filter) => {
return {
name: filter.selectedAttribute.value!,
operator: filter.selectedOperator.value!,
name: filter.attribute,
operator: filter.operator,
value: filter.convertedFilterValue,
} as QueryFilter;
});
};

type Props = {
readonly isAdAnalytics: boolean;
readonly onChange: (newFilters: QueryFilter[]) => void;
readonly onQueryFilterChange: (newFilters: QueryFilter[]) => void;
readonly onFilterRowChange: (newFilters: FilterRowData[]) => void;
readonly filters: FilterRowData[];
};

export function FilterRow(props: Props) {
const [filters, setFilters] = useState<Filter[]>([]);

const addFilterInput = () => {
setFilters((prevState) => [
...prevState,
{
selectedAttribute: {},
selectedOperator: {},
rawFilterValue: '',
convertedFilterValue: '',
parsingValueError: '',
} as Filter,
]);
const newFilters = [...props.filters];
newFilters.push({
attribute: {},
operator: {},
rawFilterValue: '',
convertedFilterValue: '',
parsingValueError: '',
} as FilterRowData);
props.onFilterRowChange(newFilters);
};

const onAddFilter = (index: number) => {
const filter = filters[index];
const filter = props.filters[index];
try {
const convertedValue = convertFilterValueToProperType(
filter.rawFilterValue,
filter.selectedAttribute.value!,
filter.selectedAttribute.label!,
filter.selectedOperator.value!,
filter.attribute,
filter.attribute,
filter.operator,
props.isAdAnalytics
);

const newFilter = { ...filter, convertedFilterValue: convertedValue, parsingValueError: '' } as Filter;
const newFilter = { ...filter, convertedFilterValue: convertedValue, parsingValueError: '' } as FilterRowData;

const newFilters = [...filters];
const newFilters = [...props.filters];
newFilters.splice(index, 1, newFilter);

setFilters(newFilters);
props.onFilterRowChange(newFilters);

props.onChange(mapFiltersToQueryFilters(newFilters));
props.onQueryFilterChange(mapFilterRowsToQueryFilters(newFilters));
} catch (e: unknown) {
if (e instanceof Error) {
const errorMessage = e.message;
const newFilter = { ...filter, parsingValueError: errorMessage } as Filter;
const newFilter = { ...filter, parsingValueError: errorMessage } as FilterRowData;

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove not necessary castings like this as FilterRowData.

use instead const newFilter: FilterRowData = { ...filter, ......

const newFilters = [...filters];
const newFilters = [...props.filters];
newFilters.splice(index, 1, newFilter);

setFilters(newFilters);
props.onFilterRowChange(newFilters);
}
}
};

const deleteFilterInput = (index: number) => {
const newFilters = [...filters];
const newFilters = [...props.filters];
newFilters.splice(index, 1);

setFilters(newFilters);
props.onFilterRowChange(newFilters);

props.onChange(mapFiltersToQueryFilters(newFilters));
props.onQueryFilterChange(mapFilterRowsToQueryFilters(newFilters));
};

const onAttributesChange = (index: number, newAttribute: SelectableValue<QueryAttribute | QueryAdAttribute>) => {
const filter = filters[index];
const newFilter = { ...filter, selectedAttribute: newAttribute } as Filter;
const newFilters = [...filters];
const filter = props.filters[index];
const newFilter = { ...filter, attribute: newAttribute.value } as FilterRowData;
const newFilters = [...props.filters];
newFilters.splice(index, 1, newFilter);

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe lodash has something more human readable instead of .splice(index, 1, newFilter);

setFilters(newFilters);
props.onFilterRowChange(newFilters);
};

const onOperatorsChange = (index: number, newOperator: SelectableValue<QueryFilterOperator>) => {
const filter = filters[index];
const newFilter = { ...filter, selectedOperator: newOperator } as Filter;
const newFilters = [...filters];
const filter = props.filters[index];
const newFilter = { ...filter, operator: newOperator.value } as FilterRowData;
const newFilters = [...props.filters];
newFilters.splice(index, 1, newFilter);

setFilters(newFilters);
props.onFilterRowChange(newFilters);
};

const onValuesChange = (index: number, newValue: string) => {
const filter = filters[index];
const filter = props.filters[index];
const newFilter = { ...filter, rawFilterValue: newValue };
const newFilters = [...filters];
const newFilters = [...props.filters];
newFilters.splice(index, 1, newFilter);

setFilters(newFilters);
props.onFilterRowChange(newFilters);
};

return (
<VerticalGroup>
{filters.length !== 0 && (
{props.filters.length !== 0 && (
<HorizontalGroup spacing={'none'}>
<InlineLabel width={30} tooltip="">
Dimension
Expand All @@ -143,24 +142,24 @@ export function FilterRow(props: Props) {
</InlineLabel>
</HorizontalGroup>
)}
{filters.map((filter, index, filtersArray) => (
{props.filters.map((filter, index, filtersArray) => (
<FilterInput
key={index}
isAdAnalytics={props.isAdAnalytics}
filter={filter}
selectableFilterAttributes={mapFilterAttributesToSelectableValue(filtersArray, props.isAdAnalytics)}
onAttributeChange={(newValue: SelectableValue<QueryAdAttribute | QueryAttribute>) =>
onAttributesChange(index, newValue)
}
onOperatorChange={(newValue: SelectableValue<QueryFilterOperator>) => onOperatorsChange(index, newValue)}
onValueChange={(newValue: string) => onValuesChange(index, newValue)}
onDelete={() => deleteFilterInput(index)}
addFilterDisabled={isEmpty(filter.selectedAttribute) || isEmpty(filter.selectedOperator)}
addFilterDisabled={isEmpty(filter.attribute) || isEmpty(filter.operator)}
onAddFilter={() => onAddFilter(index)}
parsingValueError={isEmpty(filter.parsingValueError) ? undefined : filter.parsingValueError}
/>
))}

<Box paddingTop={filters.length === 0 ? 0.5 : 0}>
<Box paddingTop={props.filters.length === 0 ? 0.5 : 0}>
<IconButton name="plus-square" tooltip="Add Filter" onClick={() => addFilterInput()} size="xl" />
</Box>
</VerticalGroup>
Expand Down
4 changes: 2 additions & 2 deletions bitmovin-analytics-datasource/src/components/GroupByInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ type Props = {
readonly groupBy: SelectableValue<QueryAttribute | QueryAdAttribute>;
readonly selectableGroupBys: Array<SelectableValue<QueryAttribute | QueryAdAttribute>>;
readonly onDelete: () => void;
readonly onChange: (newValue: SelectableValue<QueryAdAttribute | QueryAttribute>) => void;
readonly onChange: (newValue: QueryAdAttribute | QueryAttribute) => void;
readonly isFirst: boolean;
readonly isLast: boolean;
readonly onReorderGroupBy: (direction: REORDER_DIRECTION) => void;
Expand All @@ -26,7 +26,7 @@ export function GroupByInput(props: Props) {
<HorizontalGroup>
<Select
value={isEmpty(props.groupBy) ? undefined : props.groupBy}
onChange={props.onChange}
onChange={(value) => props.onChange(value.value!)}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
onChange={(value) => props.onChange(value.value!)}
onChange={(selectableValue) => props.onChange(selectableValue.value!)}

and on another places same

options={props.selectableGroupBys}
width={30}
/>
Expand Down
Loading
Loading