Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/i18n-keysets/sql/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
"label_visualization-column-100p": "Normalized column chart",
"label_visualization-donut": "Donut chart",
"label_visualization-flat-table": "Table",
"label_visualization-funnel": "Funnel chart",
"label_visualization-line": "Line chart",
"label_visualization-metric": "Indicator",
"label_visualization-pie": "Pie chart",
Expand Down
5 changes: 3 additions & 2 deletions src/i18n-keysets/sql/ru.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,12 +47,13 @@
"label_visualization-bar-100p": "Нормированная линейчатая диаграмма",
"label_visualization-column": "Столбчатая диаграмма",
"label_visualization-column-100p": "Нормированная cтолбчатая диаграмма",
"label_visualization-donut": "Бубликовая диаграмма",
"label_visualization-donut": "Кольцевая диаграмма",
"label_visualization-flat-table": "Таблица",
"label_visualization-funnel": "Воронка",
"label_visualization-line": "Линейная диаграмма",
"label_visualization-metric": "Индикатор",
"label_visualization-pie": "Круговая диаграмма",
"label_visualization-scatter": "Точечная диаграмма",
"label_visualization-treemap": "Древовидная диаграмма",
"text_default-name": "Новый QL-чарт"
}
}
1 change: 1 addition & 0 deletions src/i18n-keysets/wizard/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -302,6 +302,7 @@
"label_visualization-combined-chart": "Combined chart",
"label_visualization-donut": "Donut chart",
"label_visualization-flat-table": "Table",
"label_visualization-funnel": "Funnel chart",
"label_visualization-geolayer": "Map",
"label_visualization-geopoint": "Points",
"label_visualization-geopoint-with-cluster": "Points with cluster",
Expand Down
3 changes: 2 additions & 1 deletion src/i18n-keysets/wizard/ru.json
Original file line number Diff line number Diff line change
Expand Up @@ -302,6 +302,7 @@
"label_visualization-combined-chart": "Комбинированная диаграмма",
"label_visualization-donut": "Кольцевая диаграмма",
"label_visualization-flat-table": "Таблица",
"label_visualization-funnel": "Воронка",
"label_visualization-geolayer": "Карта",
"label_visualization-geopoint": "Точки",
"label_visualization-geopoint-with-cluster": "Точки с кластеризацией",
Expand Down Expand Up @@ -388,4 +389,4 @@
"value_geotype-select-geopolygon": "Полигоны (Геoполигоны)",
"value_geotype-select-heatmap": "Теплокарта (Геоточки)",
"value_geotype-select-polyline": "Полилинии (Геоточки)"
}
}
10 changes: 10 additions & 0 deletions src/server/components/features/features-list/FunnelChart.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import {Feature} from '../../../../shared';
import {createFeatureConfig} from '../utils';

export default createFeatureConfig({
name: Feature.FunnelChart,
state: {
development: true,
production: false,
},
});
6 changes: 6 additions & 0 deletions src/server/modes/charts/plugins/datalens/js/js.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import type {PivotData} from '../preparers/backend-pivot-table/types';
import {prepareGravityChartBarX, prepareHighchartsBarX} from '../preparers/bar-x';
import {prepareGravityChartsBarY, prepareHighchartsBarY} from '../preparers/bar-y';
import prepareFlatTableData from '../preparers/flat-table';
import {prepareFunnel} from '../preparers/funnel';
import prepareGeopointData from '../preparers/geopoint';
import prepareGeopointWithClusterData from '../preparers/geopoint-with-cluster';
import prepareGeopolygonData from '../preparers/geopolygon';
Expand Down Expand Up @@ -581,6 +582,11 @@ function prepareSingleResult({
rowsLimit = 800;
break;

case WizardVisualizationId.Funnel:
prepare = prepareFunnel;
rowsLimit = 800;
break;

case 'flatTable':
prepare = prepareFlatTableData;
rowsLimit = 100000;
Expand Down
158 changes: 158 additions & 0 deletions src/server/modes/charts/plugins/datalens/preparers/funnel/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
import type {ChartData, FunnelSeries} from '@gravity-ui/chartkit/gravity-charts';
import merge from 'lodash/merge';

import {
PlaceholderId,
getFakeTitleOrTitle,
getFormatOptions,
isMeasureValue,
isNumberField,
isPseudoField,
} from '../../../../../../../shared';
import {getBaseChartConfig} from '../../gravity-charts/utils';
import type {ColorValue} from '../../utils/color-helpers';
import {getColorsByMeasureField, getThresholdValues} from '../../utils/color-helpers';
import {
chartKitFormatNumberWrapper,
findIndexInOrder,
isGradientMode,
} from '../../utils/misc-helpers';
import {getFormattedValue} from '../helpers/get-formatted-value';
import {getLegendColorScale} from '../helpers/legend';
import type {PrepareFunctionArgs} from '../types';

export function prepareFunnel({
shared,
idToTitle,
idToDataType,
resultData,
placeholders,
labels,
colors,
colorsConfig,
}: PrepareFunctionArgs): Partial<ChartData> {
const {data, order} = resultData;
const measures = placeholders.find((p) => p.id === PlaceholderId.Measures)?.items ?? [];

const colorItem = colors?.[0];
const colorField = colorItem
? {...colorItem, data_type: idToDataType[colorItem.guid]}
: colorItem;
const colorIndex = colorField
? findIndexInOrder(order, colorField, idToTitle[colorField.guid])
: -1;

const labelItem = labels?.[0];
const labelField = labelItem
? {...labelItem, data_type: idToDataType[labelItem.guid]}
: labelItem;
const labelIndex = labelField
? findIndexInOrder(order, labelField, idToTitle[labelField.guid])
: -1;

const series: FunnelSeries = {
type: 'funnel',
name: '',
data: [],
dataLabels: {
enabled: Boolean(labelItem),
},
};

data.forEach((values) => {
measures.forEach((measureItem) => {
const actualTitle = idToTitle[measureItem.guid];
const i = findIndexInOrder(order, measureItem, actualTitle);
const _measureValue = values[i];
if (_measureValue === null) {
return;
}

const measureValue = Number(_measureValue);
const measureName = getFakeTitleOrTitle(measureItem);

let colorValue;
if (isMeasureValue(colorField)) {
colorValue = measureValue;
} else if (isNumberField(colorField)) {
colorValue = values[colorIndex];
}

let labelValue;
if (isPseudoField(labelField)) {
labelValue = isMeasureValue(labelField)
? chartKitFormatNumberWrapper(measureValue, {
lang: 'ru',
...getFormatOptions(measureItem),
})
: measureName;
} else {
labelValue = getFormattedValue(labelField, values[labelIndex]);
}

series.data.push({
value: Number(measureValue),
name: measureName,
label: labelValue as string | undefined,
custom: {
colorValue: colorValue === null ? null : Number(colorValue),
},
});
});
});

const isColoringByMeasure = isGradientMode({
colorField: colorField,
colorFieldDataType: colorField?.data_type,
colorsConfig,
});

let legend: ChartData['legend'] = {};

if (isColoringByMeasure) {
const points = series.data.map((d) => ({colorValue: d.custom.colorValue as unknown}));
const colorValues = points.map((p) => p.colorValue) as ColorValue[];

const gradientThresholdValues = getThresholdValues(colorsConfig, colorValues);
const gradientColors = getColorsByMeasureField({
values: colorValues,
colorsConfig,
gradientThresholdValues,
});

series.data.forEach((d) => {
const pointColorValue = Number(d.custom.colorValue);

if (gradientColors[pointColorValue]) {
// eslint-disable-next-line no-param-reassign
d.color = gradientColors[pointColorValue];
}
});

const colorScale = getLegendColorScale({
colorsConfig,
points,
});
legend = {
enabled: true,
type: 'continuous',
colorScale,
};
} else {
legend.enabled = series.data.length > 1;
}

return merge(getBaseChartConfig(shared), {
series: {
data: [series],
},
legend,
chart: {
zoom: {enabled: false},
margin: {
left: 20,
right: 20,
},
},
});
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@ import {getFormatOptions, isDateField, isNumberField} from '../../../../../../..
import {chartKitFormatNumberWrapper, formatDate} from '../../utils/misc-helpers';

export function getFormattedValue(field: ServerField, value: unknown) {
if (value === null) {
return null;
}

if (isDateField(field)) {
return formatDate({
valueType: field.data_type,
Expand Down
1 change: 1 addition & 0 deletions src/shared/constants/visualization.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ export enum WizardVisualizationId {
Geopolygon = 'geopolygon',
GeopointWithCluster = 'geopoint-with-cluster',
CombinedChart = 'combined-chart',
Funnel = 'funnel',
}

export enum QlVisualizationId {
Expand Down
1 change: 1 addition & 0 deletions src/shared/types/configs/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export type IconId =
| 'visPie'
| 'visPivot'
| 'visTreemap'
| 'visFunnel'
| 'collectionColored'
| 'collectionColoredDark'
| 'collectionColoredBig'
Expand Down
2 changes: 2 additions & 0 deletions src/shared/types/feature.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,8 @@ export enum Feature {
EnableDashColorPickersByTheme = 'EnableDashColorPickersByTheme',
/** Shows updated settings page */
EnableNewServiceSettings = 'EnableNewServiceSettings',
/** Funnel chart visualization */
FunnelChart = 'FunnelChart',
/** Replace static master token with dynamic one */
UsDynamicMasterToken = 'UsDynamicMasterToken',
/** Enable using dynamic master token in proxy */
Expand Down
3 changes: 2 additions & 1 deletion src/shared/types/wizard/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -238,7 +238,8 @@ export interface GraphShared extends CommonShared {
| WizardVisualizationId.Pie
| WizardVisualizationId.Donut
| WizardVisualizationId.Scatter
| WizardVisualizationId.Treemap;
| WizardVisualizationId.Treemap
| WizardVisualizationId.Funnel;
iconProps: VisualizationIconProps;
name: string;
hidden?: boolean;
Expand Down
4 changes: 4 additions & 0 deletions src/shared/utils/visualization-check.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,10 @@ export function isGravityChartsVisualization({
id: string;
features?: FeatureConfig;
}) {
if (id === WizardVisualizationId.Funnel) {
return true;
}

const isPieOrTreemap = [
WizardVisualizationId.Pie,
WizardVisualizationId.Donut,
Expand Down
10 changes: 10 additions & 0 deletions src/ui/assets/icons/vis-funnel.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 2 additions & 0 deletions src/ui/configs/common/icons.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import visColumn from 'assets/icons/vis-column.svg';
import visCombined from 'assets/icons/vis-combined.svg';
import visDonut from 'assets/icons/vis-donut.svg';
import visFlatTable from 'assets/icons/vis-flat-table.svg';
import visFunnel from 'assets/icons/vis-funnel.svg';
import visGeolayers from 'assets/icons/vis-geolayers.svg';
import visHeatmap from 'assets/icons/vis-heatmap.svg';
import visLines from 'assets/icons/vis-lines.svg';
Expand All @@ -47,6 +48,7 @@ export default {
visPie,
visPivot,
visTreemap,
visFunnel,
visHeatmap,
collectionColored,
collectionColoredDark,
Expand Down
32 changes: 32 additions & 0 deletions src/ui/constants/visualizations/funnel.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import {SquareHashtag} from '@gravity-ui/icons';
import type {Field, GraphShared} from 'shared';
import {WizardVisualizationId} from 'shared';
import {prepareFieldToMeasureTransformation} from 'units/wizard/utils/visualization';

import {ITEM_TYPES, PRIMITIVE_DATA_TYPES} from '../misc';

export const FUNNEL_VISUALIZATION: GraphShared['visualization'] = {
id: WizardVisualizationId.Funnel,
type: 'funnel',
name: 'label_visualization-funnel',
iconProps: {id: 'visFunnel', width: '24'},
allowColors: true,
checkAllowedDesignItems: ({item}) => ITEM_TYPES.MEASURES_AND_PSEUDO.has(item.type),
allowLabels: true,
checkAllowedLabels: (item: Field) => ITEM_TYPES.MEASURES_AND_PSEUDO.has(item.type),
allowFilters: true,
placeholders: [
{
allowedTypes: ITEM_TYPES.DIMENSIONS_AND_MEASURES,
allowedFinalTypes: ITEM_TYPES.MEASURES,
allowedDataTypes: PRIMITIVE_DATA_TYPES,
id: 'measures',
type: 'measures',
title: 'section_measures',
iconProps: {data: SquareHashtag},
items: [],
required: true,
transform: prepareFieldToMeasureTransformation,
},
],
};
1 change: 1 addition & 0 deletions src/ui/constants/visualizations/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ export * from './line';
export * from './metric';
export * from './combined-chart';
export * from './scatter';
export * from './funnel';

import {COMBINED_CHART_VISUALIZATION} from './combined-chart';
import {DONUT_VISUALIZATION} from './donut';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ class LabelsPlaceholder extends React.Component<Props> {
return false;
}

return (visualization as any).checkAllowedLabels(item);
return (visualization as any).checkAllowedLabels?.(item);
};

private onLabelsUpdate = (items: Field[]) => {
Expand Down
2 changes: 1 addition & 1 deletion src/ui/units/wizard/reducers/preview.ts
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,7 @@ function mutateAndValidateVisualization({
shapes = [],
}: MutateAndValidateVisualizationArgs) {
// We validate each item sequentially, while it is important that each previous item is executed
let everythingIsOk = visualization.placeholders.some((placeholder: Placeholder) => {
let everythingIsOk = visualization.placeholders?.some((placeholder: Placeholder) => {
return placeholder.items.length > 0;
});

Expand Down
Loading
Loading