Skip to content

Commit

Permalink
Merge branch 'main' into release/v8.0.0-2
Browse files Browse the repository at this point in the history
  • Loading branch information
mattcorner committed May 28, 2024
2 parents e29b359 + bbaeeca commit c6855d2
Show file tree
Hide file tree
Showing 325 changed files with 17,229 additions and 254 deletions.
6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@ipguk/react-ui",
"version": "7.6.3",
"version": "7.8.0",
"description": "React UI component library for IPG web applications",
"author": "IPG-Automotive-UK",
"license": "MIT",
Expand All @@ -10,9 +10,9 @@
"node": ">=10"
},
"scripts": {
"build": "pnpm run clean && tsc --project ./tsconfig.production.json && pnpm run copy-files",
"build": "pnpm run clean && pnpm run copy-files && tsc --project ./tsconfig.production.json",
"clean": "rimraf dist",
"copy-files": "copyfiles -e '**/*.test.**' -e '**/*.spec.**' -e '**/*.stories.**' -u 1 src/**/*.html src/**/*.css src/**/*.ts src/**/*.tsx dist/",
"copy-files": "copyfiles -e '**/*.test.**' -e '**/*.spec.**' -e '**/*.stories.**' -u 1 src/**/*.html src/**/*.css src/**/*.ts src/**/*.tsx src/**/*/*.svg dist/",
"prepublishOnly": "pnpm run build",
"start": "microbundle-crl watch --no-compress --format modern,cjs --jsxFragment React.Fragment",
"storybook": "storybook dev -p 6006",
Expand Down
124 changes: 124 additions & 0 deletions src/BulletGauge/BulletGauge.spec.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
import { expect, test } from "@playwright/test";

import { getIframeContext } from "../../test/utils";

test("should render the title", async ({ page }) => {
// Navigate to the page
await page.goto(
"http://localhost:6006/?path=/story/plots-bulletgauge--default"
);

// Get the iframe context
const iframe = await getIframeContext(page);

// Wait for elements are loaded
await iframe.waitForTimeout(5000);

// Get the text content of the element with the text 'Data Maturity'
const titleText = await iframe.textContent("text=Data Maturity");

// Check if the title is rendered
expect(titleText).toBeTruthy();
});

test("should render the value", async ({ page }) => {
await page.goto(
"http://localhost:6006/?path=/story/plots-bulletgauge--default"
);

// Get the iframe context
const iframe = await getIframeContext(page);

// Wait for elements are loaded
await iframe.waitForTimeout(5000);

// Get the text content of the element with the class 'numbers'
const numbersText = await iframe
.locator(
"div.plot-container.plotly svg.main-svg g.indicatorlayer g.trace g.numbers text"
)
.textContent();

// Check if the value is rendered
expect(numbersText).toContain("30");
});

test("should render the suffix", async ({ page }) => {
// Navigate to the page
await page.goto(
"http://localhost:6006/?path=/story/plots-bulletgauge--default"
);

// Get the iframe context
const iframe = await getIframeContext(page);

// Wait for elements are loaded
await iframe.waitForTimeout(5000);

// Get the text content of the element with the text '%'
const suffixText = await iframe.textContent("text=%");

// Check if the suffix is rendered
expect(suffixText).toBeTruthy();
});

test("should cap the displayed value at 100 even if a higher value is supplied", async ({
page
}) => {
// Navigate to the page with a BulletGauge value of 125
await page.goto(
"http://localhost:6006/?path=/story/plots-bulletgauge--default&args=value:125"
);

// Get the iframe context
const iframe = await getIframeContext(page);

// Wait for elements to load
await iframe.waitForTimeout(5000);

// Extract the text content of the 'numbers' element
const numbersText = await iframe
.locator(
"div.plot-container.plotly svg.main-svg g.indicatorlayer g.trace g.numbers text"
)
.textContent();

// Verify that the displayed value is capped at 100
expect(numbersText).toContain("100");
});

// Define the test cases with the value and expected color
const testCases = [
{ expectedColor: "rgb(211, 47, 47)", value: 29 }, // red
{ expectedColor: "rgb(237, 108, 2)", value: 30 }, // orange
{ expectedColor: "rgb(237, 108, 2)", value: 70 }, // orange
{ expectedColor: "rgb(46, 125, 50)", value: 71 } // green
];

// iterate over test cases
for (const testCase of testCases) {
test(`should render the gauge with correct colour for value ${testCase.value}`, async ({
page
}) => {
// Navigate to the specific story or dynamically set the value if possible
await page.goto(
`http://localhost:6006/?path=/story/plots-bulletgauge--default&args=value:${testCase.value}`
);

// Get the iframe context
const iframe = await getIframeContext(page);

// Wait for elements to load
await iframe.waitForTimeout(5000);

// Use a specific path to locate the 'value-bullet' rect in the SVG
const rectStyle = await iframe
.locator(
"div.plot-container.plotly svg.main-svg g.indicatorlayer g.trace g.bullet g.value-bullet > rect"
)
.getAttribute("style");

// Check if the fill color is as expected
expect(rectStyle).toContain(`fill: ${testCase.expectedColor};`);
});
}
58 changes: 58 additions & 0 deletions src/BulletGauge/BulletGauge.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import { Meta, StoryFn } from "@storybook/react";

import BulletGauge from "./BulletGauge";
import { BulletGaugeProps } from "./BulletGauge.types";
import React from "react";
import { useArgs } from "@storybook/preview-api";

/**
* Story metadata
*/
const meta: Meta<typeof BulletGauge> = {
component: BulletGauge,
title: "Plots/BulletGauge"
};
export default meta;

// Story Template
const Template: StoryFn<BulletGaugeProps> = args => {
// useArgs is a hook that returns the current state of the args object
const [{ value }, updateArgs] = useArgs<BulletGaugeProps>();

// update the args object with the new markers value
React.useEffect(() => {
updateArgs({ value });
}, [value, updateArgs]);

return <BulletGauge {...args} />;
};

// Default
export const Default = {
args: {
suffix: "%",
title: "Data Maturity",
value: 30
},
render: Template
};

// High
export const High = {
args: {
suffix: "%",
title: "Progress",
value: 99
},
render: Template
};

// Low
export const Low = {
args: {
suffix: "%",
title: "Progress",
value: 5
},
render: Template
};
84 changes: 84 additions & 0 deletions src/BulletGauge/BulletGauge.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import { BulletGaugeProps } from "./BulletGauge.types";
import Plotly from "react-plotly.js";
import React from "react";
import { useTheme } from "@mui/material";

/**
* This component displays a bullet gauge progress indicator.
*/
const BulletGauge = ({ title, value, suffix }: BulletGaugeProps) => {
// theme hook
const theme = useTheme();

// Limit value to 100
const limitedValue = Math.min(value, 100);

return (
<Plotly
data={[
{
domain: { x: [0, 1], y: [0, 1] },
gauge: {
axis: {
range: [null, 100],
tickfont: {
color: theme.palette.mode === "light" ? "black" : "white",
size: 12
}
},
bar: {
color:
limitedValue < 30
? theme.palette.error.main
: value > 70
? theme.palette.success.main
: theme.palette.warning.main
},
shape: "bullet"
},
mode: "gauge+number",
number: {
font: {
color: theme.palette.mode === "light" ? "black" : "white",
size: 20
},
suffix: suffix || ""
},
type: "indicator",
value: limitedValue
}
]}
layout={{
font: {
size: 16
},
height: 80,
margin: { b: 25, l: 8, r: 0, t: 25 },
paper_bgcolor: "rgba(0,0,0,0)",
plot_bgcolor: "rgba(0,0,0,0)",
title: {
font: {
color:
theme.palette.mode === "light" ? "rgba(0, 0, 0, 0.54)" : "white",
size: 12
},
pad: {
l: 8,
t: 3
},
text: title,
x: 0,
xanchor: "left",
y: 1,
yanchor: "top"
},
width: 300
}}
config={{
displayModeBar: false
}}
/>
);
};

export default BulletGauge;
20 changes: 20 additions & 0 deletions src/BulletGauge/BulletGauge.types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/**
* This type defines the props for the BulletGauge component.
*/
export type BulletGaugeProps = {
/**
* The value to be displayed on the gauge. The maximum value is 100. The color of the gauge changes based on this value:
* - Less than 30: Red
* - Between 30 and 70: Orange
* - Greater than 70: Green
*/
value: number;
/**
* Title of the plot
*/
title?: string;
/**
* The suffix to be displayed after the value.
*/
suffix?: string;
};
19 changes: 19 additions & 0 deletions src/BulletGauge/BulletGaugeClientOnly.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import React, { Suspense, lazy } from "react";

import { BulletGaugeProps } from "./BulletGauge.types";
import ClientOnly from "../ClientOnly";

const LazyImportedBulletGauge = lazy(() => import("./BulletGauge"));

/**
* BulletGauge component wrapped in ClientOnly for use with server side rendering.
*/
export default function BulletGaugeClientOnly(props: BulletGaugeProps) {
return (
<ClientOnly>
<Suspense fallback={<span>Loading...</span>}>
<LazyImportedBulletGauge {...props} />
</Suspense>
</ClientOnly>
);
}
9 changes: 9 additions & 0 deletions src/BulletGauge/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
/**
* Export the default export from the BulletGaugeClientOnly module.
*/
export { default } from "./BulletGaugeClientOnly";

/**
* Export the BulletGaugeProps type from the BulletGauge.types module.
*/
export type { BulletGaugeProps } from "./BulletGauge.types";
Loading

0 comments on commit c6855d2

Please sign in to comment.