<div id="app">
<mds-section
bold
:title="chartAriaLabel"
:aria-label="chartAriaLabel"
aria-describedby="aria-description"
>
<span
id="aria-description"
class="accessibly-hidden"
>
{{ chartAriaDescription }}
</span>
<div style="position: relative">
<mce-hover-flag
v-if="isFlagVisible"
:x="hoverFlagPosition.pageX"
:y="hoverFlagPosition.pageY"
role="region"
aria-live="polite"
>
<mce-legend
hide-top-border
hide-bottom-border
>
<mce-legend-group :title="selectedData.title">
<mce-legend-item
:color="chartColors[0]"
:label="chartData.name"
:value="selectedData.value"
key-type="bar"
></mce-legend-item>
</mce-legend-group>
</mce-legend>
</mce-hover-flag>
<mds-button-container class="data-navigation">
<mds-button
type="button"
variation="secondary"
size="small"
:disabled="focusDataIndex === -1 || focusDataIndex === 0 ? true : false"
@click="barPlotInteract('previous')"
>
Previous Data Point
</mds-button>
<mds-button
type="button"
variation="secondary"
size="small"
:disabled="focusDataIndex + 1 < plotData.length ? false : true"
@click="barPlotInteract('next')"
>
Next Data Point
</mds-button>
<mds-button
type="button"
variation="secondary"
size="small"
@click="resetData()"
>
Reset
</mds-button>
</mds-button-container>
</div>
<mce-layout
:plot-width="plotWidth"
:plot-height="plotHeight"
:axis-left-width="axisLeftWidth"
>
<template #axis-left>
<mce-y-axis
:domain="domainY"
:range="[plotHeight, 0]"
:labels="yAxisLabels"
:tick-quantity="tickQuantity"
></mce-y-axis>
</template>
<mce-grid
:range-x="[0, plotWidth]"
:range-y="[plotHeight, 0]"
:domain-y="domainY"
scale-type-x="band"
:tick-quantity-y="tickQuantity"
:outlines="{
top: true,
}"
></mce-grid>
<mce-bar-plot-vertical
:mouse="mouse"
:data="plotData"
:data-focused="focusedData"
:domain-y="domainY"
:domain-x="domainX"
:range-x="[0, plotWidth]"
:range-y="[plotHeight, 0]"
:color="[chartColors[0]]"
@mousemove-plot="barPlotInteract"
@mouseleave-plot="resetData"
></mce-bar-plot-vertical>
<mce-pointer v-model="mouse"></mce-pointer>
<template #legend-top>
<mce-legend horizontal>
<mce-legend-group>
<mce-legend-item
:color="chartColors[0]"
:label="chartData.name"
key-type="bar"
></mce-legend-item>
</mce-legend-group>
</mce-legend>
</template>
<template #axis-bottom>
<mce-x-axis
scale-type="band"
type="categorical"
:domain="xAxisLabels.values"
:labels="xAxisLabels"
:range="[0, plotWidth]"
></mce-x-axis>
</template>
</mce-layout>
<mds-table style="margin-top: 60px; max-width: 400px">
<caption>
{{
chartAriaLabel
}}
</caption>
<mds-thead>
<mds-th>Quintile</mds-th>
<mds-th>Fund Closure</mds-th>
</mds-thead>
<mds-tbody>
<mds-tr
v-for="(row, i) in chartData.data"
:key="i"
>
<mds-td> {{ row.title }} </mds-td>
<mds-td> {{ row.value }} </mds-td>
</mds-tr>
</mds-tbody>
</mds-table>
</mds-section>
</div>
import { min, range } from "https://cdn.skypack.dev/d3-array@3.2.0";
const { generic } = window.utilities.color;
const chartData = [
{
name: 'Closed Fund',
data: [
{ id: '1', title: '1 (best)', value: 434 },
{ id: '2', title: '2', value: 405 },
{ id: '3', title: '3', value: 543 },
{ id: '4', title: '4', value: 768 },
{ id: '5', title: '5 (worst)', value: 1383 },
],
},
];
const app = new Vue({
el: "#app",
data() {
return {
colors: generic,
hoverFlagPosition: {
pageX: 96,
pageY: 70,
},
mouse: null,
selectedData: {},
focusedData: null,
focusDataIndex: -1,
axisLeftWidth: 82,
chartData: chartData[0],
plotHeight: 450,
plotWidth: 600,
chartColors: generic,
tickQuantity: 8,
chartAriaLabel: 'Fund Closures by Performance Quintile (Full Life)',
chartAriaDescription: `
A clear relationship emerged between category-relative performance and fund closures. About 12% of all closed funds landed in the best-performing quintile of their respective category, while 39% came from the worst-performing quintile.
Further analysis revealed a similar relationship between funds that close and their category-relative performance over their final 12 months. This relationship may prove more useful for spotting funds that are not long for this world. Trailing performance over the prior 12 months could be combined with the other characteristics examined here in assessing how likely it is that a fund will shut down.
`,
};
},
computed: {
isFlagVisible() {
return this.focusDataIndex > -1 || this.mouse;
},
yAxisLabels() {
return { axis: 'Number of Closures' };
},
xAxisLabels() {
const values = this.chartData.data.map(d => {
return d.title;
});
return {
values,
axis: 'Morningstar Category Return Quintile',
};
},
plotData() {
return this.chartData.data;
},
domainY() {
const start = min(this.plotData, d => d.value) > 0 ? 0 : min(this.plotData, d => d.value);
const end = 1400;
return [start, end];
},
domainX() {
return range(this.chartData.data.length);
},
},
mounted() {
window.addEventListener('resize', this.onResize);
this.setSize();
},
destroyed() {
window.removeEventListener('resize', this.onResize);
},
methods: {
barPlotInteract(inputData) {
if (inputData.position) {
const { data } = inputData;
this.focusedData = [data._itemId];
this.selectedData = data;
this.hoverFlagPosition = this.mouse;
} else {
if (inputData === 'next') {
this.focusDataIndex = this.focusDataIndex + 1;
} else {
this.focusDataIndex = this.focusDataIndex - 1;
}
this.selectedData = this.plotData[this.focusDataIndex];
this.focusedData = [this.selectedData.id];
}
},
resetData() {
this.hoverFlagPosition = {
pageX: 96,
pageY: 70,
};
this.selectedData = {};
this.focusedData = null;
this.focusDataIndex = -1;
},
setSize() {
let windowWidth = window.innerWidth - 16;
this.plotWidth = windowWidth - this.axisLeftWidth;
},
onResize() {
this.setSize();
},
},
});
View Compiled