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: add skeleton error states #727

Open
wants to merge 7 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
16 changes: 2 additions & 14 deletions packages/core/CHANGELOG.md
@@ -1,32 +1,20 @@
# Change Log

All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
All notable changes to this project will be documented in this file. See
[Conventional Commits](https://conventionalcommits.org) for commit guidelines.

# [0.35.0](https://github.com/carbon-design-system/carbon-charts/compare/v0.34.11...v0.35.0) (2020-08-13)

**Note:** Version bump only for package @carbon/charts





## [0.34.11](https://github.com/carbon-design-system/carbon-charts/compare/v0.34.10...v0.34.11) (2020-08-12)

**Note:** Version bump only for package @carbon/charts





## [0.34.10](https://github.com/carbon-design-system/carbon-charts/compare/v0.34.9...v0.34.10) (2020-08-11)

**Note:** Version bump only for package @carbon/charts





# Change Log

All notable changes to this project will be documented in this file. See
Expand Down
19 changes: 19 additions & 0 deletions packages/core/demo/data/bar.ts
Expand Up @@ -494,6 +494,25 @@ export const simpleBarSkeletonOptions = {
}
};

// simple bar - error
export const simpleBarErrorData = [];
export const simpleBarErrorOptions = {
title: "Simple bar (error)",
axes: {
left: {},
bottom: {
scaleType: "labels"
}
},
data: {
loading: false,
error: {
title: "No data available",
subtitle: "Lorem ipsum dolor sit."
Copy link
Member

Choose a reason for hiding this comment

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

Maybe we should use a real subtitle here to make for a better demo? e.g. Unable to reach application servers at this time

}
}
};

// grouped bar - empty state
export const groupedBarEmptyStateData = [];
export const groupedBarEmptyStateOptions = {
Expand Down
19 changes: 19 additions & 0 deletions packages/core/demo/data/donut.ts
Expand Up @@ -54,3 +54,22 @@ export const donutSkeletonOptions = {
loading: true
}
};

// donut - error
export const donutErrorData = [];
export const donutErrorOptions = {
title: "Donut (error)",
resizable: true,
donut: {
center: {
label: "Browsers"
}
},
data: {
loading: false,
error: {
title: "No usage available",
subtitle: "Spending summary is not available for trial accounts"
}
}
};
26 changes: 25 additions & 1 deletion packages/core/demo/data/index.ts
Expand Up @@ -158,7 +158,7 @@ let allDemoGroups = [
{
options: barDemos.simpleBarCustomLegendOrderOptions,
data: barDemos.simpleBarData,
chartType: chartTypes.SimpleBarChart,
chartType: chartTypes.SimpleBarChart
},
{
options: barDemos.simpleBarCenteredLegendOptions,
Expand Down Expand Up @@ -199,6 +199,12 @@ let allDemoGroups = [
chartType: chartTypes.SimpleBarChart,
isDemoExample: false
},
{
options: barDemos.simpleBarErrorOptions,
data: barDemos.simpleBarErrorData,
chartType: chartTypes.SimpleBarChart,
isDemoExample: false
},
{
description:
"A grouped bar chart, also known as a clustered bar graph, multi-set bar chart, or grouped column chart, is a type of bar graph that is used to compare values across multiple categories.",
Expand Down Expand Up @@ -390,6 +396,12 @@ let allDemoGroups = [
data: donutDemos.donutSkeletonData,
chartType: chartTypes.DonutChart,
isDemoExample: false
},
{
options: donutDemos.donutErrorOptions,
data: donutDemos.donutErrorData,
chartType: chartTypes.DonutChart,
isDemoExample: false
}
]
},
Expand Down Expand Up @@ -472,6 +484,12 @@ let allDemoGroups = [
data: lineDemos.lineSkeletonData,
chartType: chartTypes.LineChart,
isDemoExample: false
},
{
options: lineDemos.lineErrorOptions,
data: lineDemos.lineErrorData,
chartType: chartTypes.LineChart,
isDemoExample: false
}
]
},
Expand Down Expand Up @@ -514,6 +532,12 @@ let allDemoGroups = [
data: pieDemos.pieSkeletonData,
chartType: chartTypes.PieChart,
isDemoExample: false
},
{
options: pieDemos.pieErrorOptions,
data: pieDemos.pieErrorData,
chartType: chartTypes.PieChart,
isDemoExample: false
}
]
},
Expand Down
26 changes: 26 additions & 0 deletions packages/core/demo/data/line.ts
Expand Up @@ -422,3 +422,29 @@ export const lineSkeletonOptions = {
loading: true
}
};

// line - error
export const lineErrorData = [];
Copy link
Member

Choose a reason for hiding this comment

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

What happens if data is provided? would it still show the error?

export const lineErrorOptions = {
title: "Line (error)",
axes: {
bottom: {
title: "2019 Annual Sales Figures",
mapsTo: "date",
scaleType: "time"
},
left: {
mapsTo: "value",
title: "Conversion rate",
scaleType: "linear"
}
},
curve: "curveMonotoneX",
data: {
loading: false,
error: {
title: "No usage available",
subtitle: "Spending summary is not available for trial accounts"
}
}
};
14 changes: 14 additions & 0 deletions packages/core/demo/data/pie.ts
Expand Up @@ -41,3 +41,17 @@ export const pieSkeletonOptions = {
loading: true
}
};

// pie - error
export const pieErrorData = [];
export const pieErrorOptions = {
title: "Pie (error)",
resizable: true,
data: {
loading: false,
error: {
title: "No usage available",
subtitle: "Spending summary is not available for trial accounts"
}
}
};
6 changes: 4 additions & 2 deletions packages/core/src/components/essentials/legend.ts
Expand Up @@ -134,8 +134,10 @@ export class Legend extends Component {

sortDataGroups(dataGroups, legendOrder) {
// Sort data in user defined order
dataGroups.sort((dataA, dataB) =>
legendOrder.indexOf(dataA.name) - legendOrder.indexOf(dataB.name)
dataGroups.sort(
(dataA, dataB) =>
legendOrder.indexOf(dataA.name) -
legendOrder.indexOf(dataB.name)
);

// If user only defined partial ordering, ordered items are placed before unordered ones
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/components/graphs/scatter.ts
Expand Up @@ -298,7 +298,7 @@ export class Scatter extends Component {
const domainIdentifier = this.services.cartesianScales.getDomainIdentifier();

this.parent
.selectAll("circle")
.selectAll("circle.dot")
.on("mouseover", function (datum) {
const hoveredElement = select(this);

Expand Down
132 changes: 131 additions & 1 deletion packages/core/src/components/graphs/skeleton.ts
Expand Up @@ -28,12 +28,27 @@ export class Skeleton extends Component {
"data",
"loading"
);
const error = Tools.getProperty(
this.model.getOptions(),
"data",
"error"
);

// display a skeleton if there is no chart data or the loading flag is set to true
if (isDataLoading) {
this.renderSkeleton(isDataLoading);
} else {
}

// if we're done loading, and there's an error, render the skeleton (no shimmer) with an error message
if (!isDataLoading && error) {
this.renderSkeleton(false);
this.renderErrorMesssage(error);
}

// if neither are true, clean up
if (!isDataLoading && !error) {
this.removeSkeleton();
this.removeErrorMessage();
}
}
Copy link
Member

Choose a reason for hiding this comment

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

shouldn't the error be shown at all times regardless of whether the chart is loading or not? Errors probably won't always just be contained to the data, they could be about anything, and if you don't want an error to show you'd probably just remove it from options...?


Expand Down Expand Up @@ -301,4 +316,119 @@ export class Skeleton extends Component {
const container = this.parent.select(".chart-skeleton");
container.remove();
}

renderErrorMesssage(error) {
const skeleton = this.parent.select(".chart-skeleton");

const errorContainer = DOMUtils.appendOrSelect(
skeleton,
"svg.bx--cc--error-message"
)
.attr("width", "90%")
.attr("height", "25%")
.attr("x", "16") // usually 1rem
.attr("y", "45%");

// Error icon
const radius = 8;
const errorIcon = errorContainer
.selectAll("circle.bx--cc--error-message__icon")
.data([
{
cx: "0",
cy: "0",
r: radius
}
]);

errorIcon
.enter()
.append("circle")
.classed("bx--cc--error-message__icon", true)
.merge(errorIcon)
.attr("cx", (d) => d.cx)
.attr("cy", (d) => d.cy)
.attr("r", (d) => d.r)
.attr("transform", (d) => `translate(${d.r}, ${d.r})`);

DOMUtils.appendOrSelect(
errorContainer,
"circle.bx--cc--error-message__icon"
);

errorIcon.exit().remove();

// Exclamation point

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

const exclamationPoint = errorContainer.selectAll(
"path.bx--cc--error-message__exclamation-point"
);

exclamationPoint
.enter()
.append("path")
.classed("bx--cc--error-message__exclamation-point", true)
.merge(exclamationPoint)
.attr(
"d",
"M7.9375,11.125 C7.41973305,11.125 7,11.544733 7,12.0625 C7,12.580267 7.41973305,13 7.9375,13 C8.45526695,13 8.875,12.580267 8.875,12.0625 C8.875,11.544733 8.45526695,11.125 7.9375,11.125 M7.3125, 3 8.5625, 3 8.5625, 9.875 7.3125, 9.875, 7.3125, 3 Z"
);

DOMUtils.appendOrSelect(
errorContainer,
"path.bx--cc--error-message__exclamation-point"
);

exclamationPoint.exit().remove();

// Title
const errorTitle = errorContainer
.selectAll("text.bx--cc--error-message__title")
.data([error.title]);

const textHorizontalOffset = radius + 16;
const textVerticalOffset = 12.5; // this roughly centers our text with the icon

errorTitle
.enter()
.append("text")
.classed("bx--cc--error-message__title", true)
.merge(errorTitle)
.attr("x", textHorizontalOffset)
.attr("y", textVerticalOffset)
.html((d) => d);

DOMUtils.appendOrSelect(
errorContainer,
"text.bx--cc--error-message__title"
);

errorTitle.exit().remove();

// Subtitle
const errorSubtitle = errorContainer
.selectAll("text.bx--cc--error-message__subtitle")
.data([error.subtitle]);

errorSubtitle
.enter()
.append("text")
.classed("bx--cc--error-message__subtitle", true)
.merge(errorSubtitle)
.attr("x", textHorizontalOffset)
.attr("y", textVerticalOffset + 16)
.html((d) => d);

DOMUtils.appendOrSelect(
errorContainer,
"text.bx--cc--error-message__subtitle"
);

errorSubtitle.exit().remove();
}

removeErrorMessage() {
const container = this.parent.select(".bx--cc--error-message");
container.remove();
}
}
7 changes: 7 additions & 0 deletions packages/core/src/interfaces/charts.ts
Expand Up @@ -89,6 +89,13 @@ export interface BaseChartOptions {
* used to simulate data loading
*/
loading?: boolean;
/**
* a message to be rendered when data is unavailable for whatever reason
*/
error?: {
title: string;
subtitle: string;
};
/**
* options related to pre-selected data groups
* Remains empty if every legend item is active or dataset doesn't have the data groups.
Expand Down
4 changes: 3 additions & 1 deletion packages/core/src/model.ts
Expand Up @@ -95,7 +95,9 @@ export class ChartModel {

return allDataFromDomain.filter((datum) => {
return dataGroups.find(
(dataGroup) => dataGroup.name === datum[groupMapsTo] && dataGroup.status === ACTIVE
(dataGroup) =>
dataGroup.name === datum[groupMapsTo] &&
dataGroup.status === ACTIVE
);
});
}
Expand Down