Skip to content

Commit f5977be

Browse files
authored
Merge pull request #229 from springswell/fix/banner-favicon-a11y-responsive-211-214
Apple touch icon, Sparkline a11y label, responsive TvlChart (#212, #213, #214)
2 parents b30957c + 94e65c4 commit f5977be

4 files changed

Lines changed: 57 additions & 5 deletions

File tree

src/app/apple-icon.png

13.3 KB
Loading
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import { render, screen } from "@testing-library/react";
2+
import { describe, expect, it } from "vitest";
3+
import { Sparkline } from "./Sparkline";
4+
5+
describe("Sparkline", () => {
6+
it("exposes an accessible name via role=img and aria-label", () => {
7+
render(<Sparkline data={[1, 2, 3, 4]} />);
8+
expect(screen.getByRole("img", { name: /trend chart: increasing/i })).toBeTruthy();
9+
});
10+
11+
it("describes a decreasing trend", () => {
12+
render(<Sparkline data={[4, 3, 2, 1]} />);
13+
expect(screen.getByRole("img", { name: /trend chart: decreasing/i })).toBeTruthy();
14+
});
15+
16+
it("describes a flat trend", () => {
17+
render(<Sparkline data={[2, 2, 2, 2]} />);
18+
expect(screen.getByRole("img", { name: /trend chart: flat/i })).toBeTruthy();
19+
});
20+
21+
it("uses a custom label when provided", () => {
22+
render(<Sparkline data={[1, 2, 3]} label="TVL trend: increasing over 24 hours" />);
23+
expect(
24+
screen.getByRole("img", { name: "TVL trend: increasing over 24 hours" }),
25+
).toBeTruthy();
26+
});
27+
28+
it("includes a <title> element for tooltip/SR support", () => {
29+
const { container } = render(<Sparkline data={[1, 2, 3]} />);
30+
expect(container.querySelector("title")?.textContent).toBe("Trend chart: increasing");
31+
});
32+
});

src/components/Sparkline/Sparkline.tsx

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,20 @@ type SparklineProps = {
77
color?: string;
88
/** If true, fills the area under the line with a semi-transparent version of color. */
99
fill?: boolean;
10+
/**
11+
* Accessible label describing the trend (issue #213), e.g.
12+
* "TVL trend: increasing over 24 hours". Falls back to a label computed
13+
* from the first/last data points when omitted.
14+
*/
15+
label?: string;
1016
};
1117

18+
function describeTrend(data: number[]): string {
19+
const delta = data[data.length - 1] - data[0];
20+
const direction = delta > 0 ? "increasing" : delta < 0 ? "decreasing" : "flat";
21+
return `Trend chart: ${direction}`;
22+
}
23+
1224
/**
1325
* Minimal SVG sparkline — no dependencies.
1426
* Renders a polyline (and optional fill area) from an array of numbers.
@@ -19,9 +31,12 @@ export function Sparkline({
1931
height = 32,
2032
color = "#4AE292",
2133
fill = true,
34+
label,
2235
}: SparklineProps) {
2336
if (data.length < 2) return null;
2437

38+
const accessibleLabel = label ?? describeTrend(data);
39+
2540
const min = Math.min(...data);
2641
const max = Math.max(...data);
2742
const range = max - min || 1;
@@ -44,8 +59,10 @@ export function Sparkline({
4459
width={width}
4560
height={height}
4661
viewBox={`0 0 ${width} ${height}`}
47-
aria-hidden="true"
62+
role="img"
63+
aria-label={accessibleLabel}
4864
>
65+
<title>{accessibleLabel}</title>
4966
{fill && (
5067
<path
5168
d={fillPath}

src/components/TvlChart/TvlChart.tsx

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
"use client";
22

33
import { useEffect, useState } from "react";
4-
import { Box, Skeleton, Text } from "@chakra-ui/react";
4+
import { Box, Skeleton, Text, useBreakpointValue } from "@chakra-ui/react";
55
import {
66
AreaChart,
77
Area,
@@ -26,6 +26,9 @@ interface TvlChartProps {
2626
export default function TvlChart({ poolId }: TvlChartProps) {
2727
const [data, setData] = useState<TvlDataPoint[]>([]);
2828
const [loading, setLoading] = useState(true);
29+
// Issue #214: a fixed 180px is tall relative to viewport width on small
30+
// phones; shrink it below the sm breakpoint instead.
31+
const chartHeight = useBreakpointValue({ base: 140, sm: 180 }, { fallback: "sm" }) ?? 180;
2932

3033
useEffect(() => {
3134
let cancelled = false;
@@ -47,13 +50,13 @@ export default function TvlChart({ poolId }: TvlChartProps) {
4750
}, [poolId]);
4851

4952
if (loading) {
50-
return <Skeleton height="180px" borderRadius="2xl" startColor="app.border" endColor="app.surfaceHover" />;
53+
return <Skeleton height={`${chartHeight}px`} borderRadius="2xl" startColor="app.border" endColor="app.surfaceHover" />;
5154
}
5255

5356
if (data.length === 0) {
5457
return (
5558
<Box
56-
h="180px"
59+
h={`${chartHeight}px`}
5760
display="flex"
5861
alignItems="center"
5962
justifyContent="center"
@@ -69,7 +72,7 @@ export default function TvlChart({ poolId }: TvlChartProps) {
6972
}
7073

7174
return (
72-
<ResponsiveContainer width="100%" height={180}>
75+
<ResponsiveContainer width="100%" height={chartHeight}>
7376
<AreaChart data={data} margin={{ top: 8, right: 8, left: 0, bottom: 0 }}>
7477
<defs>
7578
<linearGradient id="tvlGradient" x1="0" y1="0" x2="0" y2="1">

0 commit comments

Comments
 (0)