Skip to content
Merged
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
18 changes: 16 additions & 2 deletions src/RangeWithValue/components.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,15 @@ export const RangeLine = styled.div<{ left: string }>`
background: ${(props) => props.theme.borderColor};
`;

export const ScaleTick = styled.div<{ left: string }>`
position: absolute;
left: ${(props) => props.left};
top: 100%;
width: 1px;
height: 4px;
background: ${PALETTE.gray5};
`;

export const ValuePoint = styled.div<{
left: string;
width: number;
Expand Down Expand Up @@ -59,11 +68,16 @@ export const LabelWrapper = styled.div`
height: 18px;
`;

export const InvalidRange = styled.span`
color: ${(props) => props.theme.errorColor};
font-size: 12px;
`;

export const Label = styled.span<{ left: string }>`
position: absolute;
left: ${({ left }) => left};
top: 9px;
min-width: 28px;
text-align: center;
transform: translateX(-50%);
white-space: nowrap;
font-size: 12px;
`;
76 changes: 73 additions & 3 deletions src/RangeWithValue/index.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,20 +8,23 @@ export default {
component: RangeWithValue,
argTypes: {
expectedMin: {
control: { type: 'range', min: 0, max: 200, step: 1 },
control: { type: 'number', step: 0.001 },
},
expectedMax: {
control: { type: 'range', min: 0, max: 200, step: 1 },
control: { type: 'number', step: 0.001 },
},
actualValue: {
control: { type: 'range', min: 0, max: 200, step: 1 },
control: { type: 'number', step: 0.001 },
},
rangeType: {
control: { type: 'select', options: ['closed', 'open-ended'] },
},
showMean: {
control: { type: 'boolean' },
},
scale: {
control: { type: 'select', options: ['linear', 'logarithmic'] },
},
},
};

Expand All @@ -31,6 +34,7 @@ const Template: StoryFn<{
actualValue: number;
rangeType: 'closed' | 'open-ended';
showMean: boolean;
scale: 'linear' | 'logarithmic';
}> = function Template(args) {
return (
<div style={{ width: 300 }}>
Expand All @@ -47,3 +51,69 @@ Default.args = {
rangeType: 'closed',
showMean: false,
};

export const EqualBounds = Template.bind({});
EqualBounds.args = {
expectedMin: 0.029,
expectedMax: 0.029,
actualValue: 0.03,
rangeType: 'open-ended',
showMean: false,
};

export const EqualBoundsMet = Template.bind({});
EqualBoundsMet.args = {
expectedMin: 0,
expectedMax: 0,
actualValue: 0,
rangeType: 'open-ended',
showMean: false,
};

export const InvalidBounds = Template.bind({});
InvalidBounds.args = {
expectedMin: 0.029,
expectedMax: 0.024,
actualValue: 0.031,
rangeType: 'open-ended',
showMean: false,
};

export const LogarithmicScale = Template.bind({});
LogarithmicScale.args = {
expectedMin: 0.038,
expectedMax: 0.153,
actualValue: 0.1,
rangeType: 'closed',
showMean: true,
scale: 'logarithmic',
};

export const LogarithmicScaleSpanningDecades = Template.bind({});
LogarithmicScaleSpanningDecades.args = {
expectedMin: 0.01,
expectedMax: 10,
actualValue: 1.5,
rangeType: 'closed',
showMean: true,
scale: 'logarithmic',
};

export const LogarithmicScaleWithZeroBound = Template.bind({});
LogarithmicScaleWithZeroBound.args = {
expectedMin: 0,
expectedMax: 0.002,
actualValue: 0.001,
rangeType: 'open-ended',
showMean: true,
scale: 'logarithmic',
};

export const MissingMeasurement = Template.bind({});
MissingMeasurement.args = {
expectedMin: 0.04,
expectedMax: 0.15,
actualValue: NaN,
rangeType: 'closed',
showMean: false,
};
236 changes: 236 additions & 0 deletions src/RangeWithValue/index.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,236 @@
import { render, screen } from '@testing-library/react';
import React from 'react';

import { THEME } from '../theme';

import { RangeWithValue } from './index';

describe('RangeWithValue', () => {
it('labels a zero-width range once', () => {
render(
<RangeWithValue
expectedMin={0.029}
expectedMax={0.029}
actualValue={0.03}
rangeType="open-ended"
showMean
/>,
);

expect(screen.getAllByText('= 0.029')).toHaveLength(1);
});

it('separates the value from a zero-width range it lies outside of', () => {
render(
<RangeWithValue
expectedMin={0.029}
expectedMax={0.029}
actualValue={0.03}
rangeType="open-ended"
bufferPercentage={0}
/>,
);

expect(screen.getByText('= 0.029')).toHaveStyle({ left: '0%' });
expect(screen.getByText('0.03')).toHaveStyle({
left: 'calc(100% - 17.5px)',
});
});

it('labels a mean that needs more decimals than its bounds', () => {
render(
<RangeWithValue
expectedMin={0.04}
expectedMax={0.15}
actualValue={0.1}
rangeType="closed"
showMean
/>,
);

expect(screen.getByText('0.095')).toBeVisible();
});

it('labels the geometric mean of a multiplicatively derived range', () => {
render(
<RangeWithValue
expectedMin={0.038}
expectedMax={0.153}
actualValue={0.1}
rangeType="closed"
showMean
scale="logarithmic"
/>,
);

expect(screen.getByText('0.076')).toBeVisible();
expect(screen.queryByText('0.0955')).not.toBeInTheDocument();
expect(screen.queryByText('0.0762495901628')).not.toBeInTheDocument();
});

it('leaves the ticks of a logarithmic scale unlabelled', () => {
render(
<RangeWithValue
expectedMin={0.01}
expectedMax={10}
actualValue={1}
rangeType="closed"
scale="logarithmic"
/>,
);

expect(screen.getByText('0.01')).toBeVisible();
expect(screen.getByText('10')).toBeVisible();
expect(screen.queryByText('0.1')).not.toBeInTheDocument();
});

it('centers the mean of a logarithmic scale', () => {
render(
<RangeWithValue
expectedMin={1}
expectedMax={100}
actualValue={20}
rangeType="closed"
bufferPercentage={0}
showMean
scale="logarithmic"
/>,
);

expect(screen.getByText('10')).toHaveStyle({ left: '50%' });
});

it('places a value by its ratio to the bounds on a logarithmic scale', () => {
render(
<RangeWithValue
expectedMin={1}
expectedMax={100}
actualValue={4}
rangeType="closed"
bufferPercentage={0}
scale="logarithmic"
/>,
);

expect(screen.getByText('4')).toHaveStyle({
left: 'calc(30.10299956639812% - 10px)',
});
});

it('places a value by its distance to the bounds on a linear scale', () => {
render(
<RangeWithValue
expectedMin={1}
expectedMax={100}
actualValue={4}
rangeType="closed"
bufferPercentage={0}
/>,
);

expect(screen.getByText('4')).toHaveStyle({
left: 'calc(3.0303030303030303% - 10px)',
});
});

it('warns about a value within a tenth of the span of a linear bound', () => {
render(
<RangeWithValue
expectedMin={1}
expectedMax={100}
actualValue={5}
rangeType="closed"
/>,
);

expect(screen.getByText('5')).toHaveStyle({
background: THEME.warningColor,
});
});

it('warns about a value within a tenth of the ratio of a logarithmic bound', () => {
render(
<RangeWithValue
expectedMin={1}
expectedMax={100}
actualValue={5}
rangeType="closed"
scale="logarithmic"
/>,
);

expect(screen.getByText('5')).toHaveStyle({
background: THEME.successColor,
});
});

it('refuses a logarithmic scale for a lower bound of zero', () => {
render(
<RangeWithValue
expectedMin={0}
expectedMax={0.002}
actualValue={0.001}
rangeType="closed"
showMean
scale="logarithmic"
/>,
);

expect(
screen.getByText(
'Keine logarithmische Skala für Werte kleiner oder gleich null: 0',
{
exact: false,
},
),
).toBeVisible();
});

it('centers a range that collapses onto the value', () => {
render(
<RangeWithValue
expectedMin={0.029}
expectedMax={0.029}
actualValue={0.029}
rangeType="open-ended"
/>,
);

expect(screen.getByText('= 0.029')).toHaveStyle({ left: '50%' });
});

it('refuses to call a missing measurement in range', () => {
render(
<RangeWithValue
expectedMin={0.04}
expectedMax={0.15}
actualValue={NaN}
rangeType="closed"
/>,
);

expect(
screen.getByText('Keine gültigen Zahlenwerte: 0.04 / 0.15 / NaN', {
exact: false,
}),
).toBeVisible();
});

it('reports a maximum below the minimum instead of drawing a scale', () => {
render(
<RangeWithValue
expectedMin={0.029}
expectedMax={0.024}
actualValue={0.031}
rangeType="open-ended"
/>,
);

expect(
screen.getByText('Ungültige Grenzwerte: 0.029 ist größer als 0.024', {
exact: false,
}),
).toBeVisible();
expect(screen.queryByText('0.031')).not.toBeInTheDocument();
});
});
Loading
Loading