diff --git a/changelog.md b/changelog.md index 6b528d8..597bf4d 100644 --- a/changelog.md +++ b/changelog.md @@ -1,3 +1,9 @@ +## 1.9.1 + +* Fix x-axis title position, when axis labels rotated to 45 degrees +* Fix color selection, when visual doesn't have series +* Displaying category with null value + ## 1.9.0 * Add sorting of legend items by value diff --git a/package.json b/package.json index 02b09ba..fb1f8a9 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "powerbi-visuals-mekkochart", - "version": "1.9.0", + "version": "1.9.1", "description": "A Mekko chart is a mix of a 100% stacked column chart and a 100% stacked bar chart combined into one view. Similar to a treemap, the dimensional values are represented by length and width of each rectangle. The width of a column is proportional to the total value of the column.", "repository": { "type": "git", diff --git a/pbiviz.json b/pbiviz.json index 11d0ce2..c89a3b3 100644 --- a/pbiviz.json +++ b/pbiviz.json @@ -1,10 +1,10 @@ { "visual": { "name": "MekkoChart", - "displayName": "Mekko Chart 1.9.0", + "displayName": "Mekko Chart 1.9.1", "guid": "MekkoChart1449744733038", "visualClassName": "MekkoChart", - "version": "1.9.0", + "version": "1.9.1", "description": "A Mekko chart is a mix of a 100% stacked column chart and a 100% stacked bar chart combined into one view. Similar to a treemap, the dimensional values are represented by length and width of each rectangle. The width of a column is proportional to the total value of the column.", "supportUrl": "http://community.powerbi.com", "gitHubUrl": "https://github.com/Microsoft/powerbi-visuals-mekkochart" diff --git a/src/columnChart/baseColumnChart.ts b/src/columnChart/baseColumnChart.ts index 3ae99d9..5d12b25 100644 --- a/src/columnChart/baseColumnChart.ts +++ b/src/columnChart/baseColumnChart.ts @@ -817,7 +817,8 @@ module powerbi.extensibility.visual.columnChart { let color: string = BaseColumnChart.getDataPointColor( legendItem, - categoryIndex + categoryIndex, + dataPointObjects ); const seriesData: tooltip.TooltipSeriesDataItem[] = []; @@ -1001,7 +1002,7 @@ module powerbi.extensibility.visual.columnChart { if (series.data[0] !== undefined && series.data[0].valueAbsolute > categoryProperties[series.data[0].categoryIndex].valueAbsolute) { categoryProperties[series.data[0].categoryIndex].valueAbsolute = series.data[0].valueAbsolute; categoryProperties[series.data[0].categoryIndex].color = series.data[0].color; - categoryProperties[series.data[0].categoryIndex].name = series.data[0].categoryValue.toString(); + categoryProperties[series.data[0].categoryIndex].name = (series.data[0].categoryValue || "").toString(); categoryProperties[series.data[0].categoryIndex].series = series; categoryProperties[series.data[0].categoryIndex].identity = series.identity; } diff --git a/src/converterStrategy/baseConverterStrategy.ts b/src/converterStrategy/baseConverterStrategy.ts index 273c054..f56fbba 100644 --- a/src/converterStrategy/baseConverterStrategy.ts +++ b/src/converterStrategy/baseConverterStrategy.ts @@ -84,70 +84,75 @@ module powerbi.extensibility.visual.converterStrategy { let categoryGradientStartBaseColorIdentities: BaseColorIdentity[] = []; let categoryGradientEndBaseColorIdentities: BaseColorIdentity[] = []; let categoryItemsCount: Array = []; - this.dataView.categories[0].values.forEach( (category: PrimitiveValue, index: number) => { - // gradiend start color - let mappedItems: IFilteredValueGroups[] = valueGroups.map( group => { - return { - gr: group, - categoryValue: group.values[0].values[index], - categoryIndex: index, - category: category, - identity: group.identity - }; - }).filter(v => v.categoryValue !== null); - - categoryItemsCount[index] = mappedItems; - if (colorGradient) { - categoryItemsCount[index] = _.sortBy(categoryItemsCount[index], BaseConverterStrategy.SortField); - } + if (colorGradient) { + this.dataView.categories[0].values.forEach( (category: PrimitiveValue, index: number) => { + // gradiend start color + let mappedItems: IFilteredValueGroups[] = []; + valueGroups.forEach( group => { + if (group.values[0].values[index] !== null) { + mappedItems.push({ + gr: group, + categoryValue: group.values[0].values[index], + categoryIndex: index, + category: category || "", + identity: group.identity + }); + } + }); + categoryItemsCount[index] = mappedItems; - let baseStartColorIdentity: IFilteredValueGroups = _.maxBy(mappedItems, BaseConverterStrategy.SortField); - if (baseStartColorIdentity === undefined) { - return; - } + if (colorGradient) { + categoryItemsCount[index] = _.sortBy(categoryItemsCount[index], BaseConverterStrategy.SortField); + } - let colorStart: string = defaultLabelLegendColor; + let baseStartColorIdentity: IFilteredValueGroups = _.maxBy(mappedItems, BaseConverterStrategy.SortField); + if (baseStartColorIdentity === undefined) { + return; + } - if (baseStartColorIdentity.gr.objects !== undefined && ((baseStartColorIdentity.gr.objects).dataPoint.fill).solid !== undefined) { - colorStart = ((baseStartColorIdentity.gr.objects).dataPoint.fill).solid.color; - } + let colorStart: string = defaultLabelLegendColor; - categoryGradientStartBaseColorIdentities[index] = { - category: baseStartColorIdentity.category.toString(), - color: colorStart, - identity: baseStartColorIdentity.gr.identity, - group: baseStartColorIdentity.gr - }; + if (baseStartColorIdentity.gr.objects !== undefined && ((baseStartColorIdentity.gr.objects).dataPoint.fill).solid !== undefined) { + colorStart = ((baseStartColorIdentity.gr.objects).dataPoint.fill).solid.color; + } - // gradiend end color - let baseEndColorIdentity: IFilteredValueGroups = _.minBy(valueGroups.map( group => { - return { - gr: group, - categoryValue: group.values[0].values[index], - categoryIndex: index, - category: category, - identity: group.identity + categoryGradientStartBaseColorIdentities[index] = { + category: (baseStartColorIdentity.category || "").toString(), + color: colorStart, + identity: baseStartColorIdentity.gr.identity, + group: baseStartColorIdentity.gr }; - }), BaseConverterStrategy.SortField); - if (baseEndColorIdentity === undefined) { - return; - } + // gradiend end color + let baseEndColorIdentity: IFilteredValueGroups = _.minBy(valueGroups.map( group => { + return { + gr: group, + categoryValue: group.values[0].values[index], + categoryIndex: index, + category: category, + identity: group.identity + }; + }), BaseConverterStrategy.SortField); - let colorEnd: string = defaultLabelLegendColor; + if (baseEndColorIdentity === undefined) { + return; + } - if (baseEndColorIdentity.gr.objects !== undefined && ((baseEndColorIdentity.gr.objects).dataPoint.fill).solid !== undefined) { - colorEnd = ((baseEndColorIdentity.gr.objects).dataPoint.fill).solid.color; - } + let colorEnd: string = defaultLabelLegendColor; - categoryGradientEndBaseColorIdentities[index] = { - category: baseEndColorIdentity.category.toString(), - color: colorEnd, - identity: baseEndColorIdentity.gr.identity, - group: baseEndColorIdentity.gr - }; - }); + if (baseEndColorIdentity.gr.objects !== undefined && ((baseEndColorIdentity.gr.objects).dataPoint.fill).solid !== undefined) { + colorEnd = ((baseEndColorIdentity.gr.objects).dataPoint.fill).solid.color; + } + + categoryGradientEndBaseColorIdentities[index] = { + category: (baseEndColorIdentity.category || "").toString(), + color: colorEnd, + identity: baseEndColorIdentity.gr.identity, + group: baseEndColorIdentity.gr + }; + }); + } if (this.dataView && this.dataView.values) { const allValues: DataViewValueColumns = this.dataView.values; @@ -196,7 +201,7 @@ module powerbi.extensibility.visual.converterStrategy { let categoryIndex: number = _.findIndex(series.values, value => value); let positionIndex: number = _.findIndex(categoryItemsCount[categoryIndex], ser => ser.identity === series.identity ); - category = categoryMaxValues[categoryIndex].category.toString(); + category = (categoryMaxValues[categoryIndex].category || "").toString(); let gradientBaseColorStart: string = colorHelper.getColorForSeriesValue(categoryGradientStartBaseColorIdentities[categoryIndex].group.objects, category); let gradientBaseColorEnd: string = colorHelper.getColorForSeriesValue(categoryGradientEndBaseColorIdentities[categoryIndex].group.objects, category); diff --git a/src/visual.ts b/src/visual.ts index d3121a1..4aa103d 100644 --- a/src/visual.ts +++ b/src/visual.ts @@ -410,6 +410,37 @@ module powerbi.extensibility.visual { true); } + private calculateXAxisAdditionalHeight(): number { + let categories: PrimitiveValue[] = (this.layers[0]).getData().categories; + let sortedByLength: PrimitiveValue[] = _.sortBy(categories, "length"); + let longestCategory: PrimitiveValue = sortedByLength[categories.length - 1] || ""; + let shortestCategory: PrimitiveValue = sortedByLength[0] || ""; + + if (longestCategory instanceof Date) { + let metadataColumn: DataViewMetadataColumn = (this.layers[0]).getData().valuesMetadata[0]; + let formatString: string = valueFormatter.getFormatStringByColumn(metadataColumn); + + let formatter = valueFormatter.create({ + format: formatString, + value: shortestCategory, + value2: longestCategory, + columnType: { + dateTime: true + } + }); + + longestCategory = formatter.format(longestCategory); + } + + const xAxisTextProperties: TextProperties = MekkoChart.getTextProperties(this.categoryAxisProperties + && PixelConverter.fromPointToPixel( + parseFloat(this.categoryAxisProperties["fontSize"])) || undefined); + + let longestCategoryWidth = textMeasurementService.measureSvgTextWidth(xAxisTextProperties, longestCategory.toString()); + let requiredHeight = longestCategoryWidth * Math.tan(MekkoChart.CategoryTextRotataionDegree * Math.PI / 180); + return requiredHeight; + } + private renderAxesLabels(options: MekkoAxisRenderingOptions, xFontSize: number): void { this.axisGraphicsContext .selectAll(MekkoChart.XAxisLabelSelector.selectorName) @@ -430,10 +461,17 @@ module powerbi.extensibility.visual { const xAxisYPosition: number = d3.transform(this.xAxisGraphicsContext.attr("transform")).translate[1] - fontSize + xFontSize + MekkoChart.XAxisYPositionOffset; + const rotataionEnabled = (this.layers[0]).getXAxisLabelsSettings().enableRotataion; + + let shiftTitle: number = 0; + if (rotataionEnabled) { + shiftTitle = this.calculateXAxisAdditionalHeight(); + } + const xAxisLabel: Selection = this.axisGraphicsContext.append("text") .attr({ x: width / MekkoChart.WidthDelimiter, - y: xAxisYPosition + y: xAxisYPosition + shiftTitle }) .style({ "fill": options.xLabelColor @@ -1872,30 +1910,7 @@ module powerbi.extensibility.visual { const rotataionEnabled = (this.layers[0]).getXAxisLabelsSettings().enableRotataion; if (rotataionEnabled) { - let categories: any[] = (this.layers[0]).getData().categories; - let sortedByLength: any[] = _.sortBy(categories, "length"); - let longestCategory: any = sortedByLength[categories.length - 1]; - let shortestCategory: any = sortedByLength[0]; - - if (longestCategory instanceof Date) { - let metadataColumn: DataViewMetadataColumn = (this.layers[0]).getData().valuesMetadata[0]; - let formatString: string = valueFormatter.getFormatStringByColumn(metadataColumn); - - let formatter = valueFormatter.create({ - format: formatString, - value: shortestCategory, - value2: longestCategory, - columnType: { - dateTime: true - } - }); - - longestCategory = formatter.format(longestCategory); - } - - let longestCategoryWidth = textMeasurementService.measureSvgTextWidth(xAxisTextProperties, longestCategory); - let requiredHeight = longestCategoryWidth * Math.tan(MekkoChart.CategoryTextRotataionDegree * Math.PI / 180); - xMax += requiredHeight; + xMax += this.calculateXAxisAdditionalHeight(); } if (this.hideAxisLabels(this.legendMargins)) {