Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(web): day ahead price styling features v3 #7274

Merged
merged 23 commits into from
Oct 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
d7557bc
fix: check if the future price data is actually future price
silkeholmebonnen Sep 30, 2024
d5d8faa
fix: now in bold
silkeholmebonnen Sep 30, 2024
6b16c83
WIP
silkeholmebonnen Oct 3, 2024
b01f3a7
fix: highlight of current hour
silkeholmebonnen Oct 3, 2024
bd793c1
fix: currecy in front for all other than €
silkeholmebonnen Oct 3, 2024
4252417
fix: change order of if statements so that past hours are gray
silkeholmebonnen Oct 3, 2024
fc19b6b
Merge branch 'master' into silkebonnen/avo-553-v2-day-a-head-price-st…
silkeholmebonnen Oct 3, 2024
40f1d2e
fix: use Intl numberformat to format currency
silkeholmebonnen Oct 3, 2024
5913bda
merge master
silkeholmebonnen Oct 3, 2024
f511105
WIP
silkeholmebonnen Oct 3, 2024
8f0bf01
fix: use last index because first can be negative
silkeholmebonnen Oct 3, 2024
3a4fbf3
feat: 0 is 12 pix
silkeholmebonnen Oct 3, 2024
b70eb91
feat: green and red bars for 5th percentile
silkeholmebonnen Oct 3, 2024
8be1ba3
remove at before time
silkeholmebonnen Oct 7, 2024
c6d89ea
merge master
silkeholmebonnen Oct 9, 2024
d5155eb
extract reused numbers to constants
silkeholmebonnen Oct 10, 2024
f505ee3
add minWidth 17 to tailwind config
silkeholmebonnen Oct 10, 2024
fbb634e
fix: minwidth 18 instead of 17
silkeholmebonnen Oct 10, 2024
cfa1669
use bg-success-dark
silkeholmebonnen Oct 10, 2024
fe29183
fix: much simpler way of calculating the width
silkeholmebonnen Oct 16, 2024
e329814
Merge branch 'master' into silkebonnen/avo-559-v3-day-ahead-price
silkeholmebonnen Oct 16, 2024
7a37ba0
fix: use one calc
silkeholmebonnen Oct 16, 2024
e05242d
Merge branch 'silkebonnen/avo-559-v3-day-ahead-price' of https://gith…
silkeholmebonnen Oct 16, 2024
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
15 changes: 13 additions & 2 deletions web/src/features/charts/FuturePrice.stories.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Meta, StoryObj } from '@storybook/react';
import { halfHourPriceData, priceData } from 'stories/mockData';
import { halfHourPriceData, priceData, priceData2 } from 'stories/mockData';
import { mockDateDecorator } from 'storybook-mock-date-decorator';

import { FuturePrice } from './FuturePrice';
Expand All @@ -25,8 +25,10 @@ export const PositivePrices: Story = {
const negativePriceData = JSON.parse(JSON.stringify(priceData));

negativePriceData.priceData['2024-09-02 03:00:00+00:00'] = -0.2;
negativePriceData.priceData['2024-09-02 13:00:00+00:00'] = -0.9;
negativePriceData.priceData['2024-09-02 13:00:00+00:00'] = -1;
negativePriceData.priceData['2024-09-02 08:00:00+00:00'] = -0.3;
negativePriceData.priceData['2024-09-02 09:00:00+00:00'] = 0;
negativePriceData.priceData['2024-09-02 10:00:00+00:00'] = -0.0001;

export const NegativePrices: Story = {
args: {
Expand Down Expand Up @@ -60,3 +62,12 @@ export const HalfHourPrices: Story = {
date: new Date('2024-09-02 01:00:00+00:00'),
},
};

export const PricesGoingUp: Story = {
args: {
futurePrice: priceData2,
},
parameters: {
date: new Date('2024-09-01 12:00:00+00:00'),
},
};
130 changes: 71 additions & 59 deletions web/src/features/charts/FuturePrice.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import {
getGranularity,
negativeToPostivePercentage,
normalizeToGranularity,
priceIn5Percentile,
} from './futurePriceUtils';

export function FuturePrice({ futurePrice }: { futurePrice: FuturePriceData | null }) {
Expand All @@ -44,24 +45,23 @@ export function FuturePrice({ futurePrice }: { futurePrice: FuturePriceData | nu
() => calculatePriceBound(filteredPriceData, Math.min, granularity, true),
[filteredPriceData, granularity]
);
const hasNegativePrice = minPriceTotal < 0;
const negativePercentage = negativeToPostivePercentage(minPriceTotal, maxPriceTotal);

if (!futurePrice || !isFuturePrice(futurePrice)) {
return null;
}

const hasNegativePrice = minPriceTotal < 0;
const negativePercentage = negativeToPostivePercentage(minPriceTotal, maxPriceTotal);

return (
<div className="pt-2">
<Accordion
isCollapsed={isCollapsed}
title={t(`country-panel.price-chart.see`)}
expandedTitle={t(`country-panel.price-chart.hide`)}
className="text-success dark:text-emerald-500"
className="text-success dark:text-success-dark"
expandedIcon={ChevronsUpDownIcon}
collapsedIcon={ChevronsDownUpIcon}
iconClassName="text-success dark:text-emerald-500"
iconClassName="text-success dark:text-success-dark"
iconSize={20}
setState={setIsCollapsed}
onOpen={() => trackEvent(TrackEvent.FUTURE_PRICE_EXPANDED)}
Expand All @@ -81,31 +81,33 @@ export function FuturePrice({ futurePrice }: { futurePrice: FuturePriceData | nu
: ''
}
>
{dateIsFirstHourOfTomorrow(new Date(date)) && (
<div className="flex flex-col py-1" data-test-id="tomorrow-label">
<HorizontalDivider />
<TommorowLabel date={date} t={t} i18n={i18n} />
</div>
)}
<div className="flex flex-row justify-items-end gap-2 px-1">
<TimeDisplay date={date} granularity={granularity} />
<PriceDisplay
price={price}
currency={futurePrice.currency}
i18n={i18n}
/>
<div className="flex h-full w-full flex-row self-center">
{hasNegativePrice && (
<div
className="flex flex-row justify-end"
style={{
width: `${negativePercentage}%`,
}}
data-test-id="negative-price"
>
{price < 0 && (
<div>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this div is needed?

{dateIsFirstHourOfTomorrow(new Date(date)) && (
<div className="flex flex-col py-1" data-test-id="tomorrow-label">
<HorizontalDivider />
<TommorowLabel date={date} t={t} i18n={i18n} />
</div>
)}
<div className="flex flex-row justify-items-end gap-2 px-1">
<TimeDisplay date={date} granularity={granularity} />
<PriceDisplay
price={price}
currency={futurePrice.currency}
i18n={i18n}
/>
<div className="flex h-full w-full flex-row self-center">
{hasNegativePrice && price < 0 && (
<div
className="flex flex-row justify-end"
style={{
width: `calc(100% * ${
negativePercentage / 100
} + 12px - 12px * ${negativePercentage / 100})`,
}}
data-test-id="negative-price"
>
<PriceBar
price={price * -1}
price={price}
maxPrice={-1 * minPriceTotal}
color={getColor(
price,
Expand All @@ -115,27 +117,32 @@ export function FuturePrice({ futurePrice }: { futurePrice: FuturePriceData | nu
granularity
)}
/>
)}
</div>
)}
<div
style={{
width: `${100 - negativePercentage}%`,
}}
data-test-id="positive-price"
>
{price > 0 && (
<PriceBar
price={price}
maxPrice={maxPriceTotal}
color={getColor(
price,
maxPriceFuture,
minPriceFuture,
date,
granularity
</div>
)}
{price >= 0 && (
<div
data-test-id="positive-price"
style={{
width: `calc(100% * ${
(100 - negativePercentage) / 100
} + 12px - 12px * ${(100 - negativePercentage) / 100})`,
}}
className="ml-auto"
>
{price >= 0 && (
<PriceBar
price={price}
maxPrice={maxPriceTotal}
color={getColor(
price,
maxPriceFuture,
minPriceFuture,
date,
granularity
)}
/>
)}
/>
</div>
)}
</div>
</div>
Expand Down Expand Up @@ -172,10 +179,13 @@ export function PriceBar({
maxPrice: number;
color: string;
}) {
const nonNegativePrice = Math.abs(price);
return (
<div
className={`h-3 rounded-full ${color}`}
style={{ width: `${(price / maxPrice) * 100}%` }}
style={{
width: `calc(calc(${nonNegativePrice / maxPrice} * (100% - 12px)) + 12px)`,
}}
data-test-id="price-bar"
/>
);
Expand Down Expand Up @@ -214,7 +224,7 @@ function TimeDisplay({ date, granularity }: { date: string; granularity: number

if (isNow(date, granularity)) {
return (
<p className="min-w-[82px] text-sm font-semibold" data-test-id="now-label">
<p className={`min-w-18 pl-1 text-sm font-semibold`} data-test-id="now-label">
{t(`country-panel.price-chart.now`)}
</p>
);
Expand All @@ -225,11 +235,7 @@ function TimeDisplay({ date, granularity }: { date: string; granularity: number
minute: '2-digit',
}).format(datetime);

return (
<p className="min-w-[82px] text-nowrap text-sm tabular-nums">
{`${t(`country-panel.price-chart.at`)} ${formattedDate}`}
</p>
);
return <p className={`min-w-18 text-nowrap text-sm tabular-nums`}>{formattedDate}</p>;
}

function PriceDisclaimer() {
Expand Down Expand Up @@ -292,10 +298,16 @@ const getColor = (
normalizeToGranularity(new Date(), granularity)
) {
return 'bg-price-light dark:bg-price-dark opacity-50';
} else if (price === maxPrice) {
} else if (
priceIn5Percentile(price, maxPrice, minPrice, true) &&
maxPrice != minPrice
) {
return 'bg-danger dark:bg-red-400';
} else if (price === minPrice) {
return 'bg-success dark:bg-emerald-500';
} else if (
priceIn5Percentile(price, maxPrice, minPrice, false) &&
maxPrice != minPrice
) {
return 'bg-success dark:bg-success-dark';
} else {
return 'bg-price-light dark:bg-price-dark';
}
Expand Down
2 changes: 1 addition & 1 deletion web/src/features/charts/RoundedCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ export const RoundedCard = forwardRef<
return (
<section
className={twMerge(
'my-2 rounded-2xl border border-neutral-200 px-4 pb-2 dark:border-gray-700',
'my-2 overflow-hidden rounded-2xl border border-neutral-200 px-4 pb-2 dark:border-gray-700',
className
)}
ref={reference}
Expand Down
37 changes: 37 additions & 0 deletions web/src/features/charts/futurePriceUtils.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
getGranularity,
negativeToPostivePercentage,
normalizeToGranularity,
priceIn5Percentile,
} from './futurePriceUtils';

describe('FuturePrice Utility Functions', () => {
Expand Down Expand Up @@ -85,4 +86,40 @@ describe('FuturePrice Utility Functions', () => {
const percentage = negativeToPostivePercentage(minPrice, maxPrice);
expect(percentage).to.equal(0);
});

test('priceIn5Percentile returns true if price is in top 5 percentile', () => {
const price = 95;
const maxPrice = 100;
const minPrice = 0;
const inTop = true;
const result = priceIn5Percentile(price, maxPrice, minPrice, inTop);
expect(result).to.equal(true);
});

test('priceIn5Percentile returns false if price is not in top 5 percentile', () => {
const price = 80;
const maxPrice = 100;
const minPrice = 50;
const inTop = true;
const result = priceIn5Percentile(price, maxPrice, minPrice, inTop);
expect(result).to.equal(false);
});

test('priceIn5Percentile returns true if price is in bottom 5 percentile', () => {
const price = 52;
const maxPrice = 100;
const minPrice = 50;
const inTop = false;
const result = priceIn5Percentile(price, maxPrice, minPrice, inTop);
expect(result).to.equal(true);
});

test('priceIn5Percentile returns false if price is not in bottom 5 percentile', () => {
const price = 60;
const maxPrice = 100;
const minPrice = 50;
const inTop = false;
const result = priceIn5Percentile(price, maxPrice, minPrice, inTop);
expect(result).to.equal(false);
});
});
15 changes: 15 additions & 0 deletions web/src/features/charts/futurePriceUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,3 +72,18 @@ export const negativeToPostivePercentage = (

return Math.round(Math.abs((minPrice / (maxPrice + Math.abs(minPrice))) * 100));
};

export const priceIn5Percentile = (
price: number,
maxPrice: number,
minPrice: number,
inTop: boolean
): boolean => {
const fivePercent = 0.05;
const priceRange = maxPrice - minPrice;

if (inTop) {
return price >= maxPrice - priceRange * fivePercent;
}
return price <= minPrice + priceRange * fivePercent;
};
50 changes: 45 additions & 5 deletions web/src/stories/mockData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -125,13 +125,53 @@ export const zoneStateMock: StateZoneData = {
e: 0,
};

export const priceData2 = {
entryCount: 24,
priceData: {
'2024-09-01 09:00:00+00:00': 0,
'2024-09-01 10:00:00+00:00': 1,
'2024-09-01 11:00:00+00:00': 2,
'2024-09-01 12:00:00+00:00': 3,
'2024-09-01 13:00:00+00:00': 4,
'2024-09-01 14:00:00+00:00': 5,
'2024-09-01 15:00:00+00:00': 6,
'2024-09-01 16:00:00+00:00': 7,
'2024-09-01 17:00:00+00:00': 8,
'2024-09-01 18:00:00+00:00': 9,
'2024-09-01 19:00:00+00:00': 10,
'2024-09-01 20:00:00+00:00': 11,
'2024-09-01 21:00:00+00:00': 12,
'2024-09-01 22:00:00+00:00': 13,
'2024-09-01 23:00:00+00:00': 14,
'2024-09-02 00:00:00+00:00': 15,
'2024-09-02 01:00:00+00:00': 16,
'2024-09-02 02:00:00+00:00': 17,
'2024-09-02 03:00:00+00:00': 18,
'2024-09-02 04:00:00+00:00': 19,
'2024-09-02 05:00:00+00:00': 20,
'2024-09-02 06:00:00+00:00': 21,
'2024-09-02 07:00:00+00:00': 22,
'2024-09-02 08:00:00+00:00': 23,
'2024-09-02 09:00:00+00:00': 24,
'2024-09-02 10:00:00+00:00': 25,
'2024-09-02 11:00:00+00:00': 26,
'2024-09-02 12:00:00+00:00': 27,
'2024-09-02 13:00:00+00:00': 28,
'2024-09-02 14:00:00+00:00': 29,
'2024-09-02 15:00:00+00:00': 30,
},
currency: 'EUR',
source: 'nordpool.com',
zoneKey: 'DE',
};

export const priceData = {
entryCount: 24,
priceData: {
'2024-09-01 22:00:00+00:00': 25,
'2024-09-01 23:00:00+00:00': 15,
'2024-09-02 00:00:00+00:00': 12,
'2024-09-02 01:00:00+00:00': 8,
'2024-09-02 01:00:00+00:00': 40,
'2024-09-02 02:00:00+00:00': 21,
'2024-09-02 03:00:00+00:00': 16,
'2024-09-02 04:00:00+00:00': 19,
Expand All @@ -145,7 +185,7 @@ export const priceData = {
'2024-09-02 12:00:00+00:00': 26,
'2024-09-02 13:00:00+00:00': 14,
'2024-09-02 14:00:00+00:00': 23,
'2024-09-02 15:00:00+00:00': 30,
'2024-09-02 15:00:00+00:00': 29,
'2024-09-02 16:00:00+00:00': 17,
'2024-09-02 17:00:00+00:00': 11,
'2024-09-02 18:00:00+00:00': 22,
Expand All @@ -168,8 +208,8 @@ export const halfHourPriceData = {
'2024-09-01 22:00:00+00:00': 25,
'2024-09-01 22:30:00+00:00': 15,
'2024-09-01 23:00:00+00:00': 12,
'2024-09-01 23:30:00+00:00': 28,
'2024-09-02 00:00:00+00:00': 21,
'2024-09-01 23:30:00+00:00': -20,
'2024-09-02 00:00:00+00:00': 0,
'2024-09-02 00:30:00+00:00': -5,
'2024-09-02 01:00:00+00:00': 19,
'2024-09-02 01:30:00+00:00': 24,
Expand Down Expand Up @@ -227,7 +267,7 @@ export const halfHourPriceData = {
'2024-09-03 03:30:00+00:00': 24,
'2024-09-03 04:00:00+00:00': 27,
'2024-09-03 04:30:00+00:00': 22,
'2024-09-03 05:00:00+00:00': -10,
'2024-09-03 05:00:00+00:00': -40,
'2024-09-03 05:30:00+00:00': 29,
'2024-09-03 06:00:00+00:00': 18,
'2024-09-03 06:30:00+00:00': 20,
Expand Down
Loading
Loading