Skip to content
Closed
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
6 changes: 5 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

## v3.1.0

A release that makes an interactive list valid HTML. An interactive item used to become a button itself, which left its `ul` with no list items and cost the list its announcement. The row control is now a real button or link inside the item, written with the new `x-h-list-item-button`. It also fixes a listbox and combobox bug where a list nested inside an option turned its own rows into options, and moves the listbox into a plugin of its own. It also repairs card and dialog padding around a slot behind an `x-if`, where the template Alpine leaves in place counted as the slot that never rendered, and lets a table header draw a top and bottom rule instead of a full outline. It ships the `mt-auto` utility class along with the half steps of the padding and gap scales. There are breaking changes to interactive lists and to registering the listbox by hand.
A release that makes an interactive list valid HTML. An interactive item used to become a button itself, which left its `ul` with no list items and cost the list its announcement. The row control is now a real button or link inside the item, written with the new `x-h-list-item-button`. It also fixes a listbox and combobox bug where a list nested inside an option turned its own rows into options, and moves the listbox into a plugin of its own. It also repairs card and dialog padding around a slot behind an `x-if`, where the template Alpine leaves in place counted as the slot that never rendered, and lets a table header draw a top and bottom rule instead of a full outline. A pie or doughnut slice label is a share of the total again, not the raw value with a percent sign after it. It ships the `mt-auto` utility class along with the half steps of the padding and gap scales. There are breaking changes to interactive lists and to registering the listbox by hand.

### List

Expand All @@ -27,6 +27,10 @@ A release that makes an interactive list valid HTML. An interactive item used to

- **New: the header border can be horizontal only.** `data-bordered="horizontal"` on `x-h-table-header` draws a rule above and below the head row and nothing down its sides, for a header separated from the rest of the table rather than boxed in. `data-bordered="true"` still draws the full outline.

### Chart

- **Fixed: a pie or doughnut slice label printed the raw value with a percent sign.** A ring of one 336-hour slice read `336%` instead of `100%`. The label is now the slice's share of the total, as the option always documented, with one decimal below 10%.

### New utility classes

- **`mt-auto`** pushes an element to the bottom of a flex column, the way `ml-auto` and `mr-auto` push along a row. The auto margins previously covered only the horizontal sides.
Expand Down
7 changes: 5 additions & 2 deletions src/components/chart.js
Original file line number Diff line number Diff line change
Expand Up @@ -541,15 +541,18 @@ function renderPie(root, cfg, ctx, doughnut = false) {
);
svg.appendChild(wedge);

// Percentage label for the slice (skip slivers too small to fit one).
// Percentage label for the slice - its SHARE of the total, never its raw value, so a 336-hour
// sum in a one-slice ring reads "100%" and not "336%" (slivers too small to fit one are skipped).
const pct = (s.value / total) * 100;
if (showLabels && pct >= 5) {
const angle = ((start + end) / 2) * 2 * Math.PI - Math.PI / 2;
const insideOffset = cutout > 0 ? ((cutout + 1) / 2) * radius : radius * 0.62;
const offset = outsideLabels ? radius * 1.16 : insideOffset;
const lx = cx + Math.cos(angle) * offset;
const ly = cy + Math.sin(angle) * offset;
svg.appendChild(outsideLabels ? dataText(`${s.value}%`, lx, ly) : onColorText(`${s.value}%`, lx, ly));
// One decimal below 10% keeps small-but-labelled slices distinguishable.
const text = `${pct < 10 ? pct.toFixed(1) : Math.round(pct)}%`;
svg.appendChild(outsideLabels ? dataText(text, lx, ly) : onColorText(text, lx, ly));
}
});

Expand Down
33 changes: 33 additions & 0 deletions tests/components/chart.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,39 @@ describe('chart directives', () => {
expect(Array.from(slot(el, 'chart-label')).map((l) => l.textContent)).toEqual(['30%', '70%']);
});

it('labels each slice with its share of the total, not its raw value', () => {
mount(
'h-chart-pie',
{
slices: [
{ label: 'Billable', value: 336 },
{ label: 'Non-billable', value: 64 },
],
},
el
);
expect(Array.from(slot(el, 'chart-label')).map((l) => l.textContent)).toEqual(['84%', '16%']);
});

it('labels a single slice 100%', () => {
mount('h-chart-pie', { slices: [{ label: 'Billable', value: 336 }] }, el);
expect(Array.from(slot(el, 'chart-label')).map((l) => l.textContent)).toEqual(['100%']);
});

it('keeps one decimal on a labelled slice under 10%', () => {
mount(
'h-chart-pie',
{
slices: [
{ label: 'A', value: 938 },
{ label: 'B', value: 62 },
],
},
el
);
expect(Array.from(slot(el, 'chart-label')).map((l) => l.textContent)).toEqual(['94%', '6.2%']);
});

it('omits slice labels when dataLabels is false', () => {
mount('h-chart-pie', { dataLabels: false, slices }, el);
expect(slot(el, 'chart-label').length).toBe(0);
Expand Down
Loading