Skip to content

Commit

Permalink
chore(dial): port over DialBase from SynchroCharts (#590)
Browse files Browse the repository at this point in the history
  • Loading branch information
diehbria authored Feb 16, 2023
1 parent a82178e commit 90a0790
Show file tree
Hide file tree
Showing 13 changed files with 525 additions and 7 deletions.
36 changes: 35 additions & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions packages/react-components/jest.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
* For a detailed explanation regarding each configuration property and type check, visit:
* https://jestjs.io/docs/configuration
*/

export default {
preset: 'ts-jest',
clearMocks: true,
Expand All @@ -18,6 +19,7 @@ export default {
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx'],
moduleNameMapper: {
'\\.(css|less)$': '<rootDir>/config/jest/styleMock.js',
'd3-shape': '<rootDir>/../../node_modules/d3-shape/dist/d3-shape.min.js',
},
testEnvironment: 'jsdom',
testPathIgnorePatterns: ['node_modules', 'dist', 'src/stencil-generated'],
Expand Down
3 changes: 2 additions & 1 deletion packages/react-components/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -106,10 +106,11 @@
},
"dependencies": {
"@awsui/components-react": "^3.0.0",
"@iot-app-kit/core": "*",
"@iot-app-kit/components": "*",
"@iot-app-kit/core": "*",
"@iot-app-kit/source-iottwinmaker": "*",
"color": "^4.2.3",
"d3-shape": "^3.2.0",
"dompurify": "2.3.4",
"parse-duration": "^1.0.2",
"uuid": "^8.3.2",
Expand Down
13 changes: 8 additions & 5 deletions packages/react-components/src/common/iconUtils.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
/* eslint-disable max-len */
import React from 'react';
import { StatusIconType } from './constants';

interface Icons {
// eslint-disable-next-line @typescript-eslint/ban-types
[statusIcon: string]: Function;
}
type Icons = {
[statusIcon: string]: (color?: string, size?: number) => JSX.Element;
};

const DEFAULT_SIZE_PX = 16;

Expand Down Expand Up @@ -35,6 +33,7 @@ export const icons: Icons = {
</svg>
);
},

acknowledged(color?: string, size: number = DEFAULT_SIZE_PX) {
return (
<svg width={`${size}px`} height={`${size}px`} viewBox='0 0 16 16' stroke={color ? `${color}` : '#3184c2'}>
Expand All @@ -55,6 +54,7 @@ export const icons: Icons = {
</svg>
);
},

disabled(color?: string, size: number = DEFAULT_SIZE_PX) {
return (
<svg width={`${size}px`} height={`${size}px`} viewBox='0 0 16 16' stroke={color ? `${color}` : '#687078'}>
Expand All @@ -65,6 +65,7 @@ export const icons: Icons = {
</svg>
);
},

latched(color?: string, size: number = DEFAULT_SIZE_PX) {
return (
<svg width={`${size}px`} height={`${size}px`} viewBox='0 0 16 16' fill={color ? `${color}` : '#f89256'}>
Expand All @@ -73,6 +74,7 @@ export const icons: Icons = {
</svg>
);
},

snoozed(color?: string, size: number = DEFAULT_SIZE_PX) {
return (
<svg width={`${size}px`} height={`${size}px`} viewBox='0 0 16 16' stroke={color ? `${color}` : '#879596'}>
Expand All @@ -83,6 +85,7 @@ export const icons: Icons = {
</svg>
);
},

error(color?: string, size: number = DEFAULT_SIZE_PX) {
return (
<svg width={`${size}px`} height={`${size}px`} viewBox='0 0 16 16' fill={color || '#FF0000'} data-test-tag='error'>
Expand Down
21 changes: 21 additions & 0 deletions packages/react-components/src/components/dial/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { DialSettings } from './types';

export enum ColorConfigurations {
BLUE = '#2E72B5',
NORMAL = '#3F7E23',
WARNING = '#F29D38',
CRITICAL = '#C03F25',
GRAY = '#D9D9D9',
PRIMARY_TEXT = '#16191f',
SECONDARY_TEXT = '#687078',
WHITE = '#fff',
}

export const DEFAULT_DIAL_SETTINGS: DialSettings = {
showName: true,
showUnit: true,
dialThickness: 34,
fontSize: 48,
unitFontSize: 24,
labelFontSize: 24,
};
49 changes: 49 additions & 0 deletions packages/react-components/src/components/dial/dial.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import React from 'react';
import { DialBase } from './dialBase';
import { TimeQuery, TimeSeriesData, TimeSeriesDataRequest, StyleSettingsMap, Viewport } from '@iot-app-kit/core';
import { useTimeSeriesData } from '../../hooks/useTimeSeriesData';
import { widgetPropertiesFromInputs } from '../../common/widgetPropertiesFromInputs';
import { Annotations } from '../../common/thresholdTypes';
import { DialSettings } from './types';
import { useViewport } from '../../hooks/useViewport';

export const Dial = ({
query,
viewport: passedInViewport,
annotations,
styles,
settings,
}: {
query: TimeQuery<TimeSeriesData[], TimeSeriesDataRequest>;
viewport?: Viewport;
annotations?: Annotations;
styles?: StyleSettingsMap;
settings?: DialSettings;
}) => {
const { dataStreams } = useTimeSeriesData({
viewport: passedInViewport,
query,
settings: { fetchMostRecentBeforeEnd: true },
styles,
});
const { viewport } = useViewport();

const utilizedViewport = passedInViewport || viewport; // explicitly passed in viewport overrides viewport group
const { propertyPoint, alarmPoint, alarmThreshold, propertyThreshold, alarmStream, propertyStream } =
widgetPropertiesFromInputs({ dataStreams, annotations, viewport: utilizedViewport });

const name = propertyStream?.name || alarmStream?.name;
const unit = propertyStream?.unit || alarmStream?.unit;
const color = alarmThreshold?.color || propertyThreshold?.color || propertyStream?.color;

return (
<DialBase
propertyPoint={propertyPoint}
alarmPoint={alarmPoint}
settings={settings}
name={name}
unit={unit}
color={color}
/>
);
};
127 changes: 127 additions & 0 deletions packages/react-components/src/components/dial/dialBase.spec.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
import React from 'react';
import { render, screen } from '@testing-library/react';
import { DialBase } from './dialBase';
import { DataPoint } from '../../common/dataTypes';

describe('name', () => {
it('renders name when showName is true', () => {
const someName = 'some-name';
render(<DialBase name={someName} settings={{ showName: true }} />);

expect(screen.queryByText(someName)).not.toBeNull();
});

it('does not render name when showName is false', () => {
const someName = 'some-name';
render(<DialBase name={someName} settings={{ showName: false }} />);

expect(screen.queryByText(someName)).toBeNull();
});
});

describe('unit', () => {
const point: DataPoint = { x: 1213, y: 123 };

it('renders unit when showUnit is true and provided a property point', () => {
const someUnit = 'some-Unit';
render(<DialBase unit={someUnit} propertyPoint={point} settings={{ showUnit: true }} />);

expect(screen.queryByText(someUnit)).not.toBeNull();
});

it('renders unit when showUnit is true and provided a alarm point', () => {
const someUnit = 'some-Unit';
render(<DialBase unit={someUnit} alarmPoint={point} settings={{ showUnit: true }} />);

expect(screen.queryByText(someUnit)).not.toBeNull();
});

it('does not render unit when showUnit is true and is not provided a data point', () => {
const someUnit = 'some-Unit';
render(<DialBase unit={someUnit} settings={{ showUnit: true }} />);

expect(screen.queryByText(someUnit)).toBeNull();
});

it('does not render unit when showUnit is false', () => {
const someUnit = 'some-Unit';
render(<DialBase unit={someUnit} settings={{ showUnit: false }} propertyPoint={point} />);

expect(screen.queryByText(someUnit)).toBeNull();
});
});

describe('property value', () => {
it('renders property points y value', () => {
const Y_VALUE = 123445;
render(<DialBase propertyPoint={{ x: new Date().getTime(), y: Y_VALUE }} settings={{ showName: false }} />);
expect(screen.queryByText(Y_VALUE)).not.toBeNull();
});

it('renders alarm points y value', () => {
const Y_VALUE = 123445;
render(<DialBase alarmPoint={{ x: new Date().getTime(), y: Y_VALUE }} settings={{ showName: false }} />);
expect(screen.queryByText(Y_VALUE)).not.toBeNull();
});

it('renders property points y value and alarm points y value when provided both an alarm and a property', () => {
const Y_VALUE_PROPERTY = 123445;
const Y_VALUE_ALARM = 'alarm_value';
render(
<DialBase
alarmPoint={{ x: new Date().getTime(), y: Y_VALUE_ALARM }}
propertyPoint={{ x: 234324, y: Y_VALUE_PROPERTY }}
settings={{ showName: false }}
/>
);

expect(screen.queryByText(Y_VALUE_PROPERTY)).not.toBeNull();
expect(screen.queryByText(Y_VALUE_ALARM)).not.toBeNull();
});
});

describe('error', () => {
it('renders error', () => {
const someError = 'some-error';
render(<DialBase error={someError} settings={{}} />);

expect(screen.queryByText(someError)).not.toBeNull();
});
});

describe('loading', () => {
it('renders loading spinner while isLoading is true', () => {
render(<DialBase isLoading settings={{}} />);

expect(screen.queryByTestId('loading')).not.toBeNull();
});

it('does not render loading spinner while isLoading is false', () => {
render(<DialBase isLoading={false} settings={{}} />);

expect(screen.queryByTestId('loading')).toBeNull();
});

it('renders error while loading', () => {
const someError = 'some-error';
render(<DialBase error={someError} isLoading settings={{}} />);

expect(screen.queryByText(someError)).not.toBeNull();
});

it('does not render data point while isLoading is true', () => {
const point = { x: 12341, y: 123213 };
render(<DialBase propertyPoint={point} isLoading settings={{}} />);

expect(screen.queryByText(point.y)).toBeNull();
});

it('does not render alarm or property data point while isLoading is true', () => {
const point = { x: 12341, y: 123213 };
const alarmPoint = { x: 12341, y: 'warning' };
render(<DialBase propertyPoint={point} alarmPoint={alarmPoint} isLoading settings={{}} />);

expect(screen.queryByText(point.y)).toBeNull();
expect(screen.queryByText(alarmPoint.y)).toBeNull();
});
});
Loading

0 comments on commit 90a0790

Please sign in to comment.