Skip to content

Commit

Permalink
feat: add Meter component
Browse files Browse the repository at this point in the history
  • Loading branch information
petermakowski committed Oct 11, 2023
1 parent 1aa0f7a commit 09f0b85
Show file tree
Hide file tree
Showing 10 changed files with 1,555 additions and 32 deletions.
1,169 changes: 1,140 additions & 29 deletions package-lock.json

Large diffs are not rendered by default.

10 changes: 9 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@
"types": "./dist/index.d.ts",
"import": "./dist/@canonical/maas-react-components.es.js",
"require": "./dist/@canonical/maas-react-components.umd.js"
},
"./dist/style.css": {
"import": "./dist/style.css",
"require": "./dist/style.css"
}
},
"files": [
Expand Down Expand Up @@ -45,6 +49,7 @@
"@testing-library/jest-dom": "^6.1.3",
"@testing-library/react": "^14.0.0",
"@testing-library/user-event": "^14.5.1",
"@types/react-dom": "18.2.7",
"@typescript-eslint/eslint-plugin": "^6.7.3",
"@typescript-eslint/parser": "^6.7.3",
"@vitejs/plugin-react": "^4.1.0",
Expand All @@ -67,11 +72,14 @@
"vitest": "^0.34.5"
},
"dependencies": {
"@canonical/react-components": "^0.47.0",
"classnames": "^2.3.2",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"vanilla-framework": "^4.3.0"
"vanilla-framework": "4.3.0"
},
"peerDependencies": {
"@canonical/react-components": "^0.47.0",
"react": "18.2.0",
"react-dom": "18.2.0"
}
Expand Down
54 changes: 54 additions & 0 deletions src/lib/elements/Meter/Meter.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
@import "vanilla-framework";

$meter-height: $sp-unit * 1.75;
$meter-height-small: $sp-unit * 1.5;

.p-meter {
margin-bottom: $sp-unit * 1.5;
padding-top: $sp-unit * 0.75;
}

.p-meter__bar {
border-radius: $meter-height;
height: $meter-height;
overflow: hidden;
position: relative;
width: 100%;
border: 1px solid $color-mid;
}

.p-meter__label {
display: flex;
justify-content: space-between;
margin-bottom: -#{$sp-unit * 0.25};
padding-top: $sp-unit * 0.5;
}

.p-meter__filled {
height: 100%;
position: absolute;
width: 0%;
}

.p-meter__separators {
height: 100%;
position: absolute;
width: 100%;
z-index: 1;
}

.p-meter--small {
margin-bottom: $sp-unit * 1.75;
padding-top: $sp-unit * 0.75;

.p-meter__bar {
border-radius: $meter-height-small;
height: $meter-height-small;
margin-bottom: $sp-unit * 0.75;
}

.p-meter__label {
margin-bottom: 0;
padding-top: 0;
}
}
142 changes: 142 additions & 0 deletions src/lib/elements/Meter/Meter.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
import { render, screen } from "@testing-library/react";
import { vi } from "vitest";

import { Meter, defaultSeparatorColor, testIds } from "./Meter";

const mockClientRect = ({
bottom = 0,
height = 0,
left = 0,
right = 0,
toJSON = () => undefined,
top = 0,
width = 0,
x = 0,
y = 0,
}) =>
vi.fn(() => {
return {
bottom,
height,
left,
right,
toJSON,
top,
width,
x,
y,
};
});

it("can be made small", async () => {
render(<Meter data={[]} small />);

expect(screen.getByTestId(testIds.container)).toHaveClass("p-meter--small");
});

it("can be given a label", () => {
render(<Meter data={[{ value: 1 }, { value: 3 }]} label="Meter label" />);

expect(screen.getByTestId(testIds.label).textContent).toBe("Meter label");
});

it("can be given a custom empty colour", () => {
render(<Meter data={[]} emptyColor="#ABC" />);

expect(screen.getByTestId(testIds.bar)).toHaveStyle({
backgroundColor: "#ABC",
});
});

it("can be given custom bar colours", () => {
render(
<Meter
data={[
{ color: "#AAA", value: 1 },
{ color: "#BBB", value: 2 },
{ color: "#CCC", value: 3 },
]}
/>,
);
const segments = screen.getAllByTestId(testIds.filled);

expect(segments[0]).toHaveStyle({ backgroundColor: "#AAA" });
expect(segments[1]).toHaveStyle({ backgroundColor: "#BBB" });
expect(segments[2]).toHaveStyle({ backgroundColor: "#CCC" });
});

it("changes colour if values exceed given maximum value", () => {
render(
<Meter data={[{ color: "#ABC", value: 100 }]} max={10} overColor="#DEF" />,
);

expect(screen.getByTestId(testIds.meteroverflow)).toHaveStyle({
backgroundColor: "#DEF",
});
});

it("correctly calculates datum widths", () => {
render(
<Meter
data={[
{ value: 10 }, // 10/100 = 10%
{ value: 20 }, // 20/100 = 20%
{ value: 30 }, // 30/100 = 30%
{ value: 40 }, // 40/100 = 40%
]}
/>,
);
const segments = screen.getAllByTestId(testIds.filled);

expect(segments[0]).toHaveStyle({ width: "10%" });
expect(segments[1]).toHaveStyle({ width: "20%" });
expect(segments[2]).toHaveStyle({ width: "30%" });
expect(segments[3]).toHaveStyle({ width: "40%" });
});

it("correctly calculates datum positions", () => {
render(
<Meter
data={[
{ value: 10 }, // 1st = 0%
{ value: 20 }, // 2nd = 1st width = 10%
{ value: 30 }, // 3rd = 1st + 2nd width = 30%
{ value: 40 }, // 4th = 1st + 2nd + 3rd width = 60%
]}
/>,
);
const segments = screen.getAllByTestId(testIds.filled);

expect(segments[0]).toHaveStyle({ left: "0%" });
expect(segments[1]).toHaveStyle({ left: "10%" });
expect(segments[2]).toHaveStyle({ left: "30%" });
expect(segments[3]).toHaveStyle({ left: "60%" });
});

it("can be made segmented", () => {
render(<Meter data={[{ value: 2 }]} max={10} segmented />);

expect(screen.getByTestId(testIds.segments)).toBeInTheDocument();
});

it("can set the segment separator color", () => {
render(
<Meter data={[{ value: 2 }]} max={10} segmented separatorColor="#abc123" />,
);

expect(screen.getByTestId(testIds.segments)).toHaveStyle({
background: "rgb(171, 193, 35);",
});
});

it("sets segment width to 1px if not enough space to show all segments", () => {
// Make width 128px so max number of segments is 64 (1px segment, 1px separator)
Element.prototype.getBoundingClientRect = mockClientRect({
width: 128,
});
render(<Meter data={[{ value: 10 }]} max={100} segmented />);

expect(screen.getByTestId(testIds.segments)).toHaveStyle({
background: `repeating-linear-gradient(to right, transparent 0, transparent 1px, ${defaultSeparatorColor} 1px, ${defaultSeparatorColor} 2px );`,
});
});

0 comments on commit 09f0b85

Please sign in to comment.