chore: Storybook migration (#19780)
This commit is contained in:
parent
97b4b9aeda
commit
ec9af3782f
49 changed files with 3384 additions and 2933 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
|
@ -43,3 +43,5 @@ asset-stats.html
|
|||
/playwright/.cache/
|
||||
.gitpod.yml
|
||||
/evcc.db
|
||||
|
||||
*storybook.log
|
||||
|
|
|
|||
14
.storybook/main.js
Normal file
14
.storybook/main.js
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
/** @type { import('@storybook/vue3-vite').StorybookConfig } */
|
||||
const config = {
|
||||
stories: ["../assets/js/**/*.stories.@(js|ts)"],
|
||||
addons: [
|
||||
"@storybook/addon-essentials",
|
||||
"@chromatic-com/storybook",
|
||||
"@storybook/addon-interactions",
|
||||
],
|
||||
framework: {
|
||||
name: "@storybook/vue3-vite",
|
||||
options: {},
|
||||
},
|
||||
};
|
||||
export default config;
|
||||
42
.storybook/preview.js
Normal file
42
.storybook/preview.js
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
import { setup } from "@storybook/vue3";
|
||||
import "bootstrap/dist/css/bootstrap.min.css";
|
||||
import smoothscroll from "smoothscroll-polyfill";
|
||||
import setupI18n from "../assets/js/i18n";
|
||||
import "../assets/css/app.css";
|
||||
import { watchThemeChanges } from "../assets/js/theme";
|
||||
|
||||
smoothscroll.polyfill();
|
||||
watchThemeChanges();
|
||||
|
||||
// Setup global parameters
|
||||
/** @type { import('@storybook/vue3').Preview } */
|
||||
const preview = {
|
||||
parameters: {
|
||||
controls: {
|
||||
matchers: {
|
||||
color: /(background|color)$/i,
|
||||
date: /Date$/,
|
||||
},
|
||||
},
|
||||
backgrounds: {
|
||||
default: "default",
|
||||
values: [
|
||||
{
|
||||
name: "default",
|
||||
value: "var(--evcc-background)",
|
||||
},
|
||||
{
|
||||
name: "box",
|
||||
value: "var(--evcc-box)",
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
setup((app) => {
|
||||
app.config.globalProperties.$hiddenFeatures = () => true;
|
||||
app.use(setupI18n());
|
||||
});
|
||||
|
||||
export default preview;
|
||||
|
|
@ -89,6 +89,14 @@ npm install
|
|||
npm run dev
|
||||
```
|
||||
|
||||
#### Storybook
|
||||
|
||||
We're using storybook to develop and visualize UI components in different states. Running the command below will open your browser at http://127.0.0.1:6006/.
|
||||
|
||||
```sh
|
||||
npm run storybook
|
||||
```
|
||||
|
||||
#### Integration testing
|
||||
|
||||
We use Playwright for end-to-end integration tests. They start a local evcc instance with different configuration yamls and prefilled databases. To run them, you have to do a local build first.
|
||||
|
|
|
|||
|
|
@ -1,14 +0,0 @@
|
|||
import { defineSetupVue3 } from "@histoire/plugin-vue";
|
||||
import "bootstrap/dist/css/bootstrap.min.css";
|
||||
import smoothscroll from "smoothscroll-polyfill";
|
||||
import setupI18n from "./js/i18n";
|
||||
import "./css/app.css";
|
||||
import { watchThemeChanges } from "./js/theme";
|
||||
|
||||
smoothscroll.polyfill();
|
||||
watchThemeChanges();
|
||||
|
||||
export const setupVue3 = defineSetupVue3(({ app }) => {
|
||||
app.config.globalProperties.$hiddenFeatures = () => true;
|
||||
app.use(setupI18n());
|
||||
});
|
||||
55
assets/js/components/ChargingPlans/ChargingPlan.stories.js
Normal file
55
assets/js/components/ChargingPlans/ChargingPlan.stories.js
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
import ChargingPlan from "./ChargingPlan.vue";
|
||||
|
||||
const hoursFromNow = (hours) => {
|
||||
const now = new Date();
|
||||
now.setHours(now.getHours() + hours);
|
||||
return now.toISOString();
|
||||
};
|
||||
|
||||
export default {
|
||||
title: "ChargingPlans/ChargingPlan",
|
||||
component: ChargingPlan,
|
||||
parameters: {
|
||||
layout: "centered",
|
||||
},
|
||||
argTypes: {
|
||||
id: { control: "text" },
|
||||
effectivePlanTime: { control: "text" },
|
||||
effectivePlanSoc: { control: "number" },
|
||||
planEnergy: { control: "number" },
|
||||
socBasedCharging: { control: "boolean" },
|
||||
socBasedPlanning: { control: "boolean" },
|
||||
vehicleSoc: { control: "number" },
|
||||
},
|
||||
};
|
||||
|
||||
const Template = (args) => ({
|
||||
components: { ChargingPlan },
|
||||
setup() {
|
||||
return { args };
|
||||
},
|
||||
template: '<ChargingPlan v-bind="args" />',
|
||||
});
|
||||
|
||||
export const None = Template.bind({});
|
||||
None.args = {
|
||||
id: "1",
|
||||
};
|
||||
|
||||
export const SocBasedCharging = Template.bind({});
|
||||
SocBasedCharging.args = {
|
||||
id: "1",
|
||||
effectivePlanTime: hoursFromNow(4),
|
||||
effectivePlanSoc: 77,
|
||||
socBasedCharging: true,
|
||||
socBasedPlanning: true,
|
||||
};
|
||||
|
||||
export const EnergyBasedCharging = Template.bind({});
|
||||
EnergyBasedCharging.args = {
|
||||
id: "1",
|
||||
effectivePlanTime: hoursFromNow(12),
|
||||
planEnergy: 77,
|
||||
socBasedCharging: false,
|
||||
socBasedPlanning: false,
|
||||
};
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
<script setup>
|
||||
import ChargingPlan from "./ChargingPlan.vue";
|
||||
|
||||
const hoursFromNow = function (hours) {
|
||||
const now = new Date();
|
||||
now.setHours(now.getHours() + hours);
|
||||
return now.toISOString();
|
||||
};
|
||||
</script>
|
||||
<template>
|
||||
<Story>
|
||||
<Variant title="none">
|
||||
<ChargingPlan id="1" />
|
||||
</Variant>
|
||||
<Variant title="target charge">
|
||||
<ChargingPlan id="1" :targetTime="hoursFromNow(14)" />
|
||||
</Variant>
|
||||
<Variant title="min soc">
|
||||
<ChargingPlan id="1" :minSoc="30" :vehicleSoc="25" />
|
||||
</Variant>
|
||||
</Story>
|
||||
</template>
|
||||
114
assets/js/components/ChargingPlans/Preview.stories.js
Normal file
114
assets/js/components/ChargingPlans/Preview.stories.js
Normal file
|
|
@ -0,0 +1,114 @@
|
|||
import Preview from "./Preview.vue";
|
||||
|
||||
const now = new Date();
|
||||
|
||||
function createDate(hoursFromNow) {
|
||||
const result = new Date(now.getTime());
|
||||
result.setHours(result.getHours() + hoursFromNow);
|
||||
return result;
|
||||
}
|
||||
|
||||
function createRate(price, hoursFromNow, durationHours = 1) {
|
||||
const start = new Date(now.getTime());
|
||||
start.setHours(start.getHours() + hoursFromNow);
|
||||
start.setMinutes(0);
|
||||
start.setSeconds(0);
|
||||
start.setMilliseconds(0);
|
||||
const end = new Date(start.getTime());
|
||||
end.setHours(start.getHours() + durationHours);
|
||||
end.setMinutes(0);
|
||||
end.setSeconds(0);
|
||||
end.setMilliseconds(0);
|
||||
return { start: start.toISOString(), end: end.toISOString(), price };
|
||||
}
|
||||
|
||||
// Scenario data
|
||||
const co2Data = {
|
||||
rates: [
|
||||
545, 518, 545, 518, 0, 545, 527, 527, 536, 518, 400, 336, 336, 339, 344, 336, 336, 336, 372,
|
||||
400, 555, 555, 545, 555, 564, 545, 555, 545, 536, 545, 527, 536, 518, 545, 509, 336, 336, 336,
|
||||
].map((price, i) => createRate(price, i)),
|
||||
duration: 8695,
|
||||
plan: [createRate(213, 4), createRate(336, 11), createRate(336, 12)],
|
||||
smartCostType: "co2",
|
||||
targetTime: createDate(14),
|
||||
};
|
||||
|
||||
const fixedData = {
|
||||
rates: [createRate(0.442, 0, 50)],
|
||||
duration: 8695,
|
||||
plan: [createRate(0.442, 12, 3)],
|
||||
smartCostType: "price",
|
||||
currency: "EUR",
|
||||
targetTime: createDate(14),
|
||||
};
|
||||
|
||||
const zonedData = {
|
||||
rates: [
|
||||
createRate(3.72, 0, 4),
|
||||
createRate(2.39, 4, 12),
|
||||
createRate(3.72, 16, 12),
|
||||
createRate(2.39, 28, 12),
|
||||
createRate(3.72, 40, 12),
|
||||
],
|
||||
duration: 8695,
|
||||
plan: [createRate(2.39, 13, 3)],
|
||||
smartCostType: "price",
|
||||
currency: "DKK",
|
||||
targetTime: createDate(17),
|
||||
};
|
||||
|
||||
const unknownData = {
|
||||
rates: co2Data.rates.slice(0, 16),
|
||||
duration: 8695,
|
||||
plan: [createRate(213, 4), createRate(336, 11), createRate(336, 12)],
|
||||
smartCostType: "co2",
|
||||
targetTime: createDate(14),
|
||||
};
|
||||
|
||||
const dynamicData = {
|
||||
rates: [0.12, 0.15, 0, -0.05, -0.11, -0.24, -0.08, 0.12, 0.25, 0.29, 0.22, 0.31, 0.31, 0.33].map(
|
||||
(price, i) => createRate(price, i)
|
||||
),
|
||||
duration: 8695,
|
||||
plan: [createRate(0.23, 2, 5)],
|
||||
smartCostType: "price",
|
||||
currency: "EUR",
|
||||
targetTime: createDate(13),
|
||||
};
|
||||
|
||||
export default {
|
||||
title: "ChargingPlans/Preview",
|
||||
component: Preview,
|
||||
argTypes: {
|
||||
rates: { control: "object" },
|
||||
duration: { control: "number" },
|
||||
plan: { control: "object" },
|
||||
smartCostType: { control: "select", options: ["co2", "price"] },
|
||||
currency: { control: "text" },
|
||||
targetTime: { control: "text" },
|
||||
},
|
||||
};
|
||||
|
||||
const Template = (args) => ({
|
||||
components: { Preview },
|
||||
setup() {
|
||||
return { args };
|
||||
},
|
||||
template: '<Preview v-bind="args" />',
|
||||
});
|
||||
|
||||
export const Co2 = Template.bind({});
|
||||
Co2.args = co2Data;
|
||||
|
||||
export const Fixed = Template.bind({});
|
||||
Fixed.args = fixedData;
|
||||
|
||||
export const Zoned = Template.bind({});
|
||||
Zoned.args = zonedData;
|
||||
|
||||
export const Unknown = Template.bind({});
|
||||
Unknown.args = unknownData;
|
||||
|
||||
export const Dynamic = Template.bind({});
|
||||
Dynamic.args = dynamicData;
|
||||
|
|
@ -1,100 +0,0 @@
|
|||
<script setup>
|
||||
import Preview from "./Preview.vue";
|
||||
|
||||
const now = new Date();
|
||||
|
||||
function createDate(hoursFromNow) {
|
||||
const result = new Date(now.getTime());
|
||||
result.setHours(result.getHours() + hoursFromNow);
|
||||
return result;
|
||||
}
|
||||
|
||||
function createRate(price, hoursFromNow, durationHours = 1) {
|
||||
const start = new Date(now.getTime());
|
||||
start.setHours(start.getHours() + hoursFromNow);
|
||||
start.setMinutes(0);
|
||||
start.setSeconds(0);
|
||||
start.setMilliseconds(0);
|
||||
const end = new Date(start.getTime());
|
||||
end.setHours(start.getHours() + durationHours);
|
||||
end.setMinutes(0);
|
||||
end.setSeconds(0);
|
||||
end.setMilliseconds(0);
|
||||
return { start: start.toISOString(), end: end.toISOString(), price };
|
||||
}
|
||||
|
||||
const co2 = {
|
||||
rates: [
|
||||
545, 518, 545, 518, 0, 545, 527, 527, 536, 518, 400, 336, 336, 339, 344, 336, 336, 336, 372,
|
||||
400, 555, 555, 545, 555, 564, 545, 555, 545, 536, 545, 527, 536, 518, 545, 509, 336, 336,
|
||||
336,
|
||||
].map((price, i) => createRate(price, i)),
|
||||
duration: 8695,
|
||||
plan: [createRate(213, 4), createRate(336, 11), createRate(336, 12)],
|
||||
smartCostType: "co2",
|
||||
targetTime: createDate(14),
|
||||
};
|
||||
|
||||
const fixed = {
|
||||
rates: [createRate(0.442, 0, 50)],
|
||||
duration: 8695,
|
||||
plan: [createRate(0.442, 12, 3)],
|
||||
smartCostType: "price",
|
||||
currency: "EUR",
|
||||
targetTime: createDate(14),
|
||||
};
|
||||
|
||||
const zoned = {
|
||||
rates: [
|
||||
createRate(3.72, 0, 4),
|
||||
createRate(2.39, 4, 12),
|
||||
createRate(3.72, 16, 12),
|
||||
createRate(2.39, 28, 12),
|
||||
createRate(3.72, 40, 12),
|
||||
],
|
||||
duration: 8695,
|
||||
plan: [createRate(2.39, 13, 3)],
|
||||
smartCostType: "price",
|
||||
currency: "DKK",
|
||||
targetTime: createDate(17),
|
||||
};
|
||||
|
||||
const unknown = {
|
||||
rates: co2.rates.slice(0, 16),
|
||||
duration: 8695,
|
||||
plan: [createRate(213, 4), createRate(336, 11), createRate(336, 12)],
|
||||
smartCostType: "co2",
|
||||
targetTime: createDate(14),
|
||||
};
|
||||
|
||||
const dynamic = {
|
||||
rates: [
|
||||
0.12, 0.15, 0, -0.05, -0.11, -0.24, -0.08, 0.12, 0.25, 0.29, 0.22, 0.31, 0.31, 0.33,
|
||||
].map((price, i) => createRate(price, i)),
|
||||
duration: 8695,
|
||||
plan: [createRate(0.23, 2, 5)],
|
||||
smartCostType: "price",
|
||||
currency: "EUR",
|
||||
targetTime: createDate(13),
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Story title="ChargingPlanPreview">
|
||||
<Variant title="co2">
|
||||
<Preview v-bind="co2" />
|
||||
</Variant>
|
||||
<Variant title="fixed">
|
||||
<Preview v-bind="fixed" />
|
||||
</Variant>
|
||||
<Variant title="zoned">
|
||||
<Preview v-bind="zoned" />
|
||||
</Variant>
|
||||
<Variant title="unknown">
|
||||
<Preview v-bind="unknown" />
|
||||
</Variant>
|
||||
<Variant title="dynamic">
|
||||
<Preview v-bind="dynamic" />
|
||||
</Variant>
|
||||
</Story>
|
||||
</template>
|
||||
34
assets/js/components/Energyflow/BatteryIcon.stories.js
Normal file
34
assets/js/components/Energyflow/BatteryIcon.stories.js
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
import BatteryIcon from "./BatteryIcon.vue";
|
||||
|
||||
export default {
|
||||
title: "Energyflow/BatteryIcon",
|
||||
component: BatteryIcon,
|
||||
parameters: {
|
||||
layout: "centered",
|
||||
},
|
||||
};
|
||||
|
||||
const createStory = (args) => {
|
||||
const story = () => ({
|
||||
components: { BatteryIcon },
|
||||
setup() {
|
||||
return { args };
|
||||
},
|
||||
template: '<BatteryIcon v-bind="args" />',
|
||||
});
|
||||
story.args = args;
|
||||
return story;
|
||||
};
|
||||
|
||||
export const Empty = createStory({ soc: 0 });
|
||||
export const Soc10 = createStory({ soc: 10 });
|
||||
export const Soc20 = createStory({ soc: 20 });
|
||||
export const Soc30 = createStory({ soc: 30 });
|
||||
export const Soc40 = createStory({ soc: 40 });
|
||||
export const Soc50 = createStory({ soc: 50 });
|
||||
export const Soc60 = createStory({ soc: 60 });
|
||||
export const Soc70 = createStory({ soc: 70 });
|
||||
export const Soc80 = createStory({ soc: 80 });
|
||||
export const Soc90 = createStory({ soc: 90 });
|
||||
export const Hold = createStory({ hold: true });
|
||||
export const GridCharge = createStory({ gridCharge: true });
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
<script setup>
|
||||
import BatteryIcon from "./BatteryIcon.vue";
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Story :layout="{ type: 'grid', width: 150 }">
|
||||
<Variant title="0%"> <BatteryIcon :soc="0" /> </Variant>
|
||||
<Variant title="10%"> <BatteryIcon :soc="10" /> </Variant>
|
||||
<Variant title="20%"> <BatteryIcon :soc="20" /> </Variant>
|
||||
<Variant title="30%"> <BatteryIcon :soc="30" /> </Variant>
|
||||
<Variant title="40%"> <BatteryIcon :soc="40" /> </Variant>
|
||||
<Variant title="50%"> <BatteryIcon :soc="50" /> </Variant>
|
||||
<Variant title="60%"> <BatteryIcon :soc="60" /> </Variant>
|
||||
<Variant title="70%"> <BatteryIcon :soc="70" /> </Variant>
|
||||
<Variant title="80%"> <BatteryIcon :soc="80" /> </Variant>
|
||||
<Variant title="90%"> <BatteryIcon :soc="90" /> </Variant>
|
||||
<Variant title="hold"> <BatteryIcon hold /> </Variant>
|
||||
<Variant title="grid charge"> <BatteryIcon grid-charge /> </Variant>
|
||||
</Story>
|
||||
</template>
|
||||
215
assets/js/components/Energyflow/Energyflow.stories.js
Normal file
215
assets/js/components/Energyflow/Energyflow.stories.js
Normal file
|
|
@ -0,0 +1,215 @@
|
|||
import Energyflow from "./Energyflow.vue";
|
||||
|
||||
export default {
|
||||
title: "Energyflow/Energyflow",
|
||||
component: Energyflow,
|
||||
};
|
||||
|
||||
const Template = (args) => ({
|
||||
components: { Energyflow },
|
||||
setup() {
|
||||
return { args };
|
||||
},
|
||||
template: '<Energyflow v-bind="args" />',
|
||||
});
|
||||
|
||||
export const GridAndPV = Template.bind({});
|
||||
GridAndPV.args = {
|
||||
gridConfigured: true,
|
||||
pvConfigured: true,
|
||||
pvPower: 7300,
|
||||
gridPower: -2300,
|
||||
homePower: 800,
|
||||
loadpointsCompact: [
|
||||
{ power: 1000, icon: "car", charging: true },
|
||||
{ power: 1000, icon: "bike", charging: true },
|
||||
{ power: 2200, icon: "car", charging: true },
|
||||
],
|
||||
tariffGrid: 0.25,
|
||||
tariffFeedIn: 0.08,
|
||||
tariffEffectivePrice: 0.08,
|
||||
smartCostType: "price",
|
||||
smartCostAvailable: true,
|
||||
currency: "EUR",
|
||||
siteTitle: "Home",
|
||||
pv: [{ power: 5000 }, { power: 2300 }],
|
||||
};
|
||||
|
||||
export const BatteryAndGrid = Template.bind({});
|
||||
BatteryAndGrid.args = {
|
||||
gridConfigured: true,
|
||||
pvConfigured: true,
|
||||
batteryConfigured: true,
|
||||
pvPower: 0,
|
||||
gridPower: 1200,
|
||||
homePower: 2000,
|
||||
batteryPower: 800,
|
||||
batterySoc: 77,
|
||||
tariffGrid: 0.25,
|
||||
tariffFeedIn: 0.08,
|
||||
tariffEffectivePrice: 0.08,
|
||||
currency: "EUR",
|
||||
siteTitle: "Home",
|
||||
battery: [
|
||||
{ soc: 44.999, capacity: 13.3, power: 350 },
|
||||
{ soc: 82.3331, capacity: 21, power: 450 },
|
||||
],
|
||||
};
|
||||
|
||||
export const BatteryCharging = Template.bind({});
|
||||
BatteryCharging.args = {
|
||||
gridConfigured: true,
|
||||
pvConfigured: true,
|
||||
batteryConfigured: true,
|
||||
pvPower: 5000,
|
||||
gridPower: -1300,
|
||||
homePower: 800,
|
||||
loadpointsCompact: [{ power: 1400, icon: "car", charging: true }],
|
||||
batteryPower: -1500,
|
||||
batterySoc: 75,
|
||||
siteTitle: "Home",
|
||||
};
|
||||
|
||||
export const GridPVAndBattery = Template.bind({});
|
||||
GridPVAndBattery.args = {
|
||||
gridConfigured: true,
|
||||
pvConfigured: true,
|
||||
batteryConfigured: true,
|
||||
pvPower: 1000,
|
||||
gridPower: 700,
|
||||
homePower: 3300,
|
||||
batteryPower: 1500,
|
||||
batterySoc: 30,
|
||||
siteTitle: "Home",
|
||||
};
|
||||
|
||||
export const BatteryThresholds = Template.bind({});
|
||||
BatteryThresholds.args = {
|
||||
gridConfigured: true,
|
||||
pvConfigured: true,
|
||||
batteryConfigured: true,
|
||||
pvPower: 8700,
|
||||
gridPower: -500,
|
||||
loadpointsCompact: [
|
||||
{ power: 5000, icon: "car", charging: true },
|
||||
{ power: 2500, icon: "bus", charging: true },
|
||||
],
|
||||
batteryPower: -700,
|
||||
batterySoc: 95,
|
||||
siteTitle: "Home",
|
||||
};
|
||||
|
||||
export const PVThresholds = Template.bind({});
|
||||
PVThresholds.args = {
|
||||
gridConfigured: true,
|
||||
pvConfigured: true,
|
||||
batteryConfigured: true,
|
||||
pvPower: 300,
|
||||
gridPower: 6500,
|
||||
homePower: 1000,
|
||||
loadpointsCompact: [
|
||||
{ power: 5000, icon: "car", charging: true },
|
||||
{ power: 1600, icon: "car", charging: true },
|
||||
],
|
||||
batteryPower: 800,
|
||||
batterySoc: 76,
|
||||
siteTitle: "Home",
|
||||
};
|
||||
|
||||
export const GridOnly = Template.bind({});
|
||||
GridOnly.args = {
|
||||
gridConfigured: true,
|
||||
pvConfigured: true,
|
||||
batteryConfigured: true,
|
||||
pvPower: 0,
|
||||
gridPower: 6500,
|
||||
homePower: 1000,
|
||||
loadpointsCompact: [
|
||||
{ power: 5500, icon: "car", charging: true },
|
||||
{ power: 0, icon: "car", charging: false },
|
||||
{ power: 0, icon: "car", charging: false },
|
||||
{ power: 0, icon: "car", charging: false },
|
||||
],
|
||||
batteryPower: 0,
|
||||
batterySoc: 0,
|
||||
siteTitle: "Home",
|
||||
};
|
||||
|
||||
export const LowPower = Template.bind({});
|
||||
LowPower.args = {
|
||||
gridConfigured: true,
|
||||
pvConfigured: true,
|
||||
batteryConfigured: true,
|
||||
pvPower: 700,
|
||||
gridPower: -300,
|
||||
homePower: 300,
|
||||
batteryPower: -100,
|
||||
batterySoc: 55,
|
||||
tariffGrid: 0.25,
|
||||
tariffFeedIn: 0.08,
|
||||
tariffEffectivePrice: 0.08,
|
||||
currency: "EUR",
|
||||
siteTitle: "Home",
|
||||
};
|
||||
|
||||
export const CO2 = Template.bind({});
|
||||
CO2.args = {
|
||||
gridConfigured: true,
|
||||
pvConfigured: true,
|
||||
pvPower: 7300,
|
||||
gridPower: -2300,
|
||||
homePower: 800,
|
||||
loadpointsCompact: [
|
||||
{ power: 1000, icon: "car", charging: true },
|
||||
{ power: 1000, icon: "car", charging: true },
|
||||
{ power: 2200, icon: "car", charging: true },
|
||||
],
|
||||
tariffGrid: 0.25,
|
||||
tariffFeedIn: 0.08,
|
||||
tariffEffectivePrice: 0.08,
|
||||
tariffCo2: 723,
|
||||
tariffEffectiveCo2: 0,
|
||||
smartCostType: "co2",
|
||||
smartCostAvailable: true,
|
||||
currency: "EUR",
|
||||
siteTitle: "Home",
|
||||
pv: [{ power: 5000 }, { power: 2300 }],
|
||||
};
|
||||
|
||||
export const UnknownInput = Template.bind({});
|
||||
UnknownInput.args = {
|
||||
gridConfigured: true,
|
||||
pvConfigured: true,
|
||||
pvPower: 2000,
|
||||
gridPower: -2000,
|
||||
loadpointsCompact: [{ power: 1000, icon: "car", charging: true }],
|
||||
};
|
||||
|
||||
export const UnknownInputFill = Template.bind({});
|
||||
UnknownInputFill.args = {
|
||||
gridConfigured: true,
|
||||
pvConfigured: true,
|
||||
batteryConfigured: true,
|
||||
pvPower: 500,
|
||||
gridPower: 0,
|
||||
batteryPower: -1000,
|
||||
loadpointsCompact: [],
|
||||
};
|
||||
|
||||
export const UnknownOutput = Template.bind({});
|
||||
UnknownOutput.args = {
|
||||
gridConfigured: true,
|
||||
pvConfigured: true,
|
||||
pvPower: 3000,
|
||||
gridPower: -1000,
|
||||
loadpointsCompact: [{ power: 1700, icon: "car", charging: true }],
|
||||
};
|
||||
|
||||
export const UnknownOutputLessThan10Percent = Template.bind({});
|
||||
UnknownOutputLessThan10Percent.args = {
|
||||
gridConfigured: true,
|
||||
pvConfigured: true,
|
||||
pvPower: 3000,
|
||||
gridPower: -1000,
|
||||
loadpointsCompact: [{ power: 1800, icon: "car", charging: true }],
|
||||
};
|
||||
|
|
@ -1,209 +0,0 @@
|
|||
<script setup>
|
||||
import Energyflow from "./Energyflow.vue";
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Story>
|
||||
<Variant title="grid and pv">
|
||||
<Energyflow
|
||||
gridConfigured
|
||||
pvConfigured
|
||||
:pvPower="7300"
|
||||
:gridPower="-2300"
|
||||
:homePower="800"
|
||||
:loadpointsCompact="[
|
||||
{ power: 1000, icon: 'car', charging: true },
|
||||
{ power: 1000, icon: 'bike', charging: true },
|
||||
{ power: 2200, icon: 'car', charging: true },
|
||||
]"
|
||||
:tariffGrid="0.25"
|
||||
:tariffFeedIn="0.08"
|
||||
:tariffEffectivePrice="0.08"
|
||||
smartCostType="price"
|
||||
smartCostAvailable
|
||||
currency="EUR"
|
||||
siteTitle="Home"
|
||||
:pv="[{ power: 5000 }, { power: 2300 }]"
|
||||
/>
|
||||
</Variant>
|
||||
<Variant title="battery and grid">
|
||||
<Energyflow
|
||||
gridConfigured
|
||||
pvConfigured
|
||||
batteryConfigured
|
||||
:pvPower="0"
|
||||
:gridPower="1200"
|
||||
:homePower="2000"
|
||||
:batteryPower="800"
|
||||
:batterySoc="77"
|
||||
:tariffGrid="0.25"
|
||||
:tariffFeedIn="0.08"
|
||||
:tariffEffectivePrice="0.08"
|
||||
currency="EUR"
|
||||
siteTitle="Home"
|
||||
:battery="[
|
||||
{ soc: 44.999, capacity: 13.3, power: 350 },
|
||||
{ soc: 82.3331, capacity: 21, power: 450 },
|
||||
]"
|
||||
/>
|
||||
</Variant>
|
||||
<Variant title="battery charging">
|
||||
<Energyflow
|
||||
gridConfigured
|
||||
pvConfigured
|
||||
batteryConfigured
|
||||
:pvPower="5000"
|
||||
:gridPower="-1300"
|
||||
:homePower="800"
|
||||
:loadpointsCompact="[{ power: 1400, icon: 'car', charging: true }]"
|
||||
:batteryPower="-1500"
|
||||
:batterySoc="75"
|
||||
siteTitle="Home"
|
||||
/>
|
||||
</Variant>
|
||||
<Variant title="grid, pv and battery">
|
||||
<Energyflow
|
||||
gridConfigured
|
||||
pvConfigured
|
||||
batteryConfigured
|
||||
:pvPower="1000"
|
||||
:gridPower="700"
|
||||
:homePower="3300"
|
||||
:batteryPower="1500"
|
||||
:batterySoc="30"
|
||||
siteTitle="Home"
|
||||
/>
|
||||
</Variant>
|
||||
<Variant title="battery thresholds">
|
||||
<Energyflow
|
||||
gridConfigured
|
||||
pvConfigured
|
||||
batteryConfigured
|
||||
:pvPower="8700"
|
||||
:gridPower="-500"
|
||||
:loadpointsCompact="[
|
||||
{ power: 5000, icon: 'car', charging: true },
|
||||
{ power: 2500, icon: 'bus', charging: true },
|
||||
]"
|
||||
:batteryPower="-700"
|
||||
:batterySoc="95"
|
||||
siteTitle="Home"
|
||||
/>
|
||||
</Variant>
|
||||
<Variant title="pv thresholds">
|
||||
<Energyflow
|
||||
gridConfigured
|
||||
pvConfigured
|
||||
batteryConfigured
|
||||
:pvPower="300"
|
||||
:gridPower="6500"
|
||||
:homePower="1000"
|
||||
:loadpointsCompact="[
|
||||
{ power: 5000, icon: 'car', charging: true },
|
||||
{ power: 1600, icon: 'car', charging: true },
|
||||
]"
|
||||
:batteryPower="800"
|
||||
:batterySoc="76"
|
||||
siteTitle="Home"
|
||||
/>
|
||||
</Variant>
|
||||
<Variant title="grid only">
|
||||
<Energyflow
|
||||
gridConfigured
|
||||
pvConfigured
|
||||
batteryConfigured
|
||||
:pvPower="0"
|
||||
:gridPower="6500"
|
||||
:homePower="1000"
|
||||
:loadpointsCompact="[
|
||||
{ power: 5500, icon: 'car', charging: true },
|
||||
{ power: 0, icon: 'car', charging: false },
|
||||
{ power: 0, icon: 'car', charging: false },
|
||||
{ power: 0, icon: 'car', charging: false },
|
||||
]"
|
||||
:batteryPower="0"
|
||||
:batterySoc="0"
|
||||
siteTitle="Home"
|
||||
/>
|
||||
</Variant>
|
||||
<Variant title="low power">
|
||||
<Energyflow
|
||||
gridConfigured
|
||||
pvConfigured
|
||||
batteryConfigured
|
||||
:pvPower="700"
|
||||
:gridPower="-300"
|
||||
:homePower="300"
|
||||
:batteryPower="-100"
|
||||
:batterySoc="55"
|
||||
:tariffGrid="0.25"
|
||||
:tariffFeedIn="0.08"
|
||||
:tariffEffectivePrice="0.08"
|
||||
currency="EUR"
|
||||
siteTitle="Home"
|
||||
/>
|
||||
</Variant>
|
||||
<Variant title="co2">
|
||||
<Energyflow
|
||||
gridConfigured
|
||||
pvConfigured
|
||||
:pvPower="7300"
|
||||
:gridPower="-2300"
|
||||
:homePower="800"
|
||||
:loadpointsCompact="[
|
||||
{ power: 1000, icon: 'car', charging: true },
|
||||
{ power: 1000, icon: 'car', charging: true },
|
||||
{ power: 2200, icon: 'car', charging: true },
|
||||
]"
|
||||
:tariffGrid="0.25"
|
||||
:tariffFeedIn="0.08"
|
||||
:tariffEffectivePrice="0.08"
|
||||
:tariffCo2="723"
|
||||
:tariffEffectiveCo2="0"
|
||||
smartCostType="co2"
|
||||
smartCostAvailable
|
||||
currency="EUR"
|
||||
siteTitle="Home"
|
||||
:pv="[{ power: 5000 }, { power: 2300 }]"
|
||||
/>
|
||||
</Variant>
|
||||
<Variant title="unknown input">
|
||||
<Energyflow
|
||||
gridConfigured
|
||||
pvConfigured
|
||||
:pvPower="2000"
|
||||
:gridPower="-2000"
|
||||
:loadpointsCompact="[{ power: 1000, icon: 'car', charging: true }]"
|
||||
/>
|
||||
</Variant>
|
||||
<Variant title="unknown input fill">
|
||||
<Energyflow
|
||||
gridConfigured
|
||||
pvConfigured
|
||||
batteryConfigured
|
||||
:pvPower="500"
|
||||
:gridPower="0"
|
||||
:batteryPower="-1000"
|
||||
:loadpointsCompact="[]"
|
||||
/>
|
||||
</Variant>
|
||||
<Variant title="unknown output">
|
||||
<Energyflow
|
||||
gridConfigured
|
||||
pvConfigured
|
||||
:pvPower="3000"
|
||||
:gridPower="-1000"
|
||||
:loadpointsCompact="[{ power: 1700, icon: 'car', charging: true }]"
|
||||
/>
|
||||
</Variant>
|
||||
<Variant title="unknown output (< 10%)">
|
||||
<Energyflow
|
||||
gridConfigured
|
||||
pvConfigured
|
||||
:pvPower="3000"
|
||||
:gridPower="-1000"
|
||||
:loadpointsCompact="[{ power: 1800, icon: 'car', charging: true }]"
|
||||
/>
|
||||
</Variant>
|
||||
</Story>
|
||||
</template>
|
||||
58
assets/js/components/Footer/Footer.stories.js
Normal file
58
assets/js/components/Footer/Footer.stories.js
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
import Footer from "./Footer.vue";
|
||||
|
||||
export default {
|
||||
title: "Footer/Footer",
|
||||
component: Footer,
|
||||
argTypes: {
|
||||
version: { control: "object" },
|
||||
savings: { control: "object" },
|
||||
},
|
||||
};
|
||||
|
||||
const Template = (args) => ({
|
||||
components: { Footer },
|
||||
setup() {
|
||||
return { args };
|
||||
},
|
||||
template: '<Footer v-bind="args" />',
|
||||
});
|
||||
|
||||
export const Default = Template.bind({});
|
||||
Default.args = {
|
||||
version: {
|
||||
installed: "0.401",
|
||||
available: "0.401",
|
||||
},
|
||||
savings: {
|
||||
statistics: {
|
||||
"30d": { solarPercentage: 50 },
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const UpdateNightly = Template.bind({});
|
||||
UpdateNightly.args = {
|
||||
version: {
|
||||
installed: "0.400",
|
||||
available: "0.400",
|
||||
commit: "5ce7be4",
|
||||
},
|
||||
savings: {
|
||||
statistics: {
|
||||
"30d": { solarPercentage: 22 },
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const UpdateAvailable = Template.bind({});
|
||||
UpdateAvailable.args = {
|
||||
version: {
|
||||
installed: "0.400",
|
||||
available: "0.500",
|
||||
},
|
||||
savings: {
|
||||
statistics: {
|
||||
"30d": { solarPercentage: 100 },
|
||||
},
|
||||
},
|
||||
};
|
||||
|
|
@ -1,31 +0,0 @@
|
|||
<script setup>
|
||||
import { reactive } from "vue";
|
||||
import Footer from "./Footer.vue";
|
||||
|
||||
const state = reactive({
|
||||
version: {
|
||||
installed: "0.401",
|
||||
available: "0.401",
|
||||
},
|
||||
savings: {
|
||||
selfConsumptionPercent: 88,
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Story>
|
||||
<Variant title="default">
|
||||
<Footer v-bind="state" />
|
||||
</Variant>
|
||||
<Variant title="update nightly">
|
||||
<Footer
|
||||
v-bind="state"
|
||||
:version="{ installed: '0.400', available: '0.400', commit: '5ce7be4' }"
|
||||
/>
|
||||
</Variant>
|
||||
<Variant title="update availabe">
|
||||
<Footer v-bind="state" :version="{ installed: '0.400', available: '0.500' }" />
|
||||
</Variant>
|
||||
</Story>
|
||||
</template>
|
||||
32
assets/js/components/Footer/OfflineIndicator.stories.js
Normal file
32
assets/js/components/Footer/OfflineIndicator.stories.js
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
import OfflineIndicator from "./OfflineIndicator.vue";
|
||||
|
||||
export default {
|
||||
title: "Footer/OfflineIndicator",
|
||||
component: OfflineIndicator,
|
||||
argTypes: {
|
||||
offline: { control: "boolean" },
|
||||
fatal: { control: "object" },
|
||||
},
|
||||
};
|
||||
|
||||
const Template = (args) => ({
|
||||
components: { OfflineIndicator },
|
||||
setup() {
|
||||
return { args };
|
||||
},
|
||||
template: '<OfflineIndicator v-bind="args" />',
|
||||
});
|
||||
|
||||
export const Offline = Template.bind({});
|
||||
Offline.args = {
|
||||
offline: true,
|
||||
};
|
||||
|
||||
export const Fatal = Template.bind({});
|
||||
Fatal.args = {
|
||||
fatal: {
|
||||
error: `cannot create charger 'wallbox_cphl': cannot create charger type 'template': cannot create
|
||||
charger type 'hardybarth-ecbl: Post http://192.168.2.219/api/v1/chargecontrols/1/mode: context deadline exceeded (Client.Timeout
|
||||
exceeded while awaiting headers)`,
|
||||
},
|
||||
};
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
<script setup>
|
||||
import OfflineIndicator from "./OfflineIndicator.vue";
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Story>
|
||||
<Variant title="offline">
|
||||
<OfflineIndicator offline />
|
||||
</Variant>
|
||||
<Variant title="fatal">
|
||||
<OfflineIndicator
|
||||
:fatal="{
|
||||
error: `cannot create charger 'wallbox_cphl': cannot create charger type 'template': cannot create
|
||||
charger type 'hardybarth-ecbl: Post http://192.168.2.219/api/v1/chargecontrols/1/mode: context deadline exceeded (Client.Timeout
|
||||
exceeded while awaiting headers)`,
|
||||
}"
|
||||
/>
|
||||
</Variant>
|
||||
</Story>
|
||||
</template>
|
||||
61
assets/js/components/Footer/Version.stories.js
Normal file
61
assets/js/components/Footer/Version.stories.js
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
import Version from "./Version.vue";
|
||||
|
||||
const releaseNotes =
|
||||
'<h1>0.141</h1><p>Bug fixes:</p><ul><li>Withdraw Kia and Hyundai apis (<a class="issue-link js-issue-link" data-error-text="Failed to load title" data-id="817450535" data-permission-text="Title is private" data-url="https://github.com/evcc-io/evcc/issues/699" data-hovercard-type="pull_request" data-hovercard-url="/evcc-io/evcc/pull/699/hovercard" href="https://github.com/evcc-io/evcc/pull/699">#699</a>)</li><li>Simplify Tesla integration and fix upstream changes (<a class="issue-link js-issue-link" data-error-text="Failed to load title" data-id="817525274" data-permission-text="Title is private" data-url="https://github.com/evcc-io/evcc/issues/700" data-hovercard-type="pull_request" data-hovercard-url="/evcc-io/evcc/pull/700/hovercard" href="https://github.com/evcc-io/evcc/pull/700">#700</a>)</li><li>SHM: Check connected status (<a class="issue-link js-issue-link" data-error-text="Failed to load title" data-id="811567860" data-permission-text="Title is private" data-url="https://github.com/evcc-io/evcc/issues/673" data-hovercard-type="pull_request" data-hovercard-url="/evcc-io/evcc/pull/673/hovercard" href="https://github.com/evcc-io/evcc/pull/673">#673</a>)</li></ul><p>Enhancements:</p><ul><li>Add Seat api (<a class="issue-link js-issue-link" data-error-text="Failed to load title" data-id="812885392" data-permission-text="Title is private" data-url="https://github.com/evcc-io/evcc/issues/681" data-hovercard-type="pull_request" data-hovercard-url="/evcc-io/evcc/pull/681/hovercard" href="https://github.com/evcc-io/evcc/pull/681">#681</a>)</li><li>Add Skoda api (<a class="issue-link js-issue-link" data-error-text="Failed to load title" data-id="812883796" data-permission-text="Title is private" data-url="https://github.com/evcc-io/evcc/issues/680" data-hovercard-type="pull_request" data-hovercard-url="/evcc-io/evcc/pull/680/hovercard" href="https://github.com/evcc-io/evcc/pull/680">#680</a>)</li><li>Add Peugeot/Opel/Citroen api (<a class="issue-link js-issue-link" data-error-text="Failed to load title" data-id="815612446" data-permission-text="Title is private" data-url="https://github.com/evcc-io/evcc/issues/692" data-hovercard-type="pull_request" data-hovercard-url="/evcc-io/evcc/pull/692/hovercard" href="https://github.com/evcc-io/evcc/pull/692">#692</a>)</li><li>KEBA: Support mA current resolution adjustments (<a class="issue-link js-issue-link" data-error-text="Failed to load title" data-id="801358501" data-permission-text="Title is private" data-url="https://github.com/evcc-io/evcc/issues/646" data-hovercard-type="pull_request" data-hovercard-url="/evcc-io/evcc/pull/646/hovercard" href="https://github.com/evcc-io/evcc/pull/646">#646</a>)</li></ul>';
|
||||
|
||||
export default {
|
||||
title: "Footer/Version",
|
||||
component: Version,
|
||||
parameters: {
|
||||
layout: "centered",
|
||||
},
|
||||
argTypes: {
|
||||
installed: { control: "text" },
|
||||
available: { control: "text" },
|
||||
commit: { control: "text" },
|
||||
releaseNotes: { control: "text" },
|
||||
hasUpdater: { control: "boolean" },
|
||||
},
|
||||
};
|
||||
|
||||
const Template = (args) => ({
|
||||
components: { Version },
|
||||
setup() {
|
||||
return { args };
|
||||
},
|
||||
template: '<Version v-bind="args" />',
|
||||
});
|
||||
|
||||
export const Latest = Template.bind({});
|
||||
Latest.args = {
|
||||
installed: "0.141",
|
||||
available: "0.141",
|
||||
};
|
||||
|
||||
export const Nightly = Template.bind({});
|
||||
Nightly.args = {
|
||||
installed: "0.141",
|
||||
available: "0.141",
|
||||
commit: "5ce7be4",
|
||||
};
|
||||
|
||||
export const UpdateAvailable = Template.bind({});
|
||||
UpdateAvailable.args = {
|
||||
installed: "0.141",
|
||||
available: "0.142",
|
||||
releaseNotes,
|
||||
};
|
||||
|
||||
export const AutoUpdater = Template.bind({});
|
||||
AutoUpdater.args = {
|
||||
installed: "0.141",
|
||||
available: "0.142",
|
||||
releaseNotes,
|
||||
hasUpdater: true,
|
||||
};
|
||||
|
||||
export const NoReleaseNotes = Template.bind({});
|
||||
NoReleaseNotes.args = {
|
||||
installed: "0.141",
|
||||
available: "0.142",
|
||||
};
|
||||
|
|
@ -1,32 +0,0 @@
|
|||
<script setup>
|
||||
import { reactive } from "vue";
|
||||
import Version from "./Version.vue";
|
||||
|
||||
const state = reactive({
|
||||
installed: "0.141",
|
||||
available: "0.141",
|
||||
});
|
||||
|
||||
const releaseNotes =
|
||||
'<h1>0.141</h1><p>Bug fixes:</p><ul><li>Withdraw Kia and Hyundai apis (<a class="issue-link js-issue-link" data-error-text="Failed to load title" data-id="817450535" data-permission-text="Title is private" data-url="https://github.com/evcc-io/evcc/issues/699" data-hovercard-type="pull_request" data-hovercard-url="/evcc-io/evcc/pull/699/hovercard" href="https://github.com/evcc-io/evcc/pull/699">#699</a>)</li><li>Simplify Tesla integration and fix upstream changes (<a class="issue-link js-issue-link" data-error-text="Failed to load title" data-id="817525274" data-permission-text="Title is private" data-url="https://github.com/evcc-io/evcc/issues/700" data-hovercard-type="pull_request" data-hovercard-url="/evcc-io/evcc/pull/700/hovercard" href="https://github.com/evcc-io/evcc/pull/700">#700</a>)</li><li>SHM: Check connected status (<a class="issue-link js-issue-link" data-error-text="Failed to load title" data-id="811567860" data-permission-text="Title is private" data-url="https://github.com/evcc-io/evcc/issues/673" data-hovercard-type="pull_request" data-hovercard-url="/evcc-io/evcc/pull/673/hovercard" href="https://github.com/evcc-io/evcc/pull/673">#673</a>)</li></ul><p>Enhancements:</p><ul><li>Add Seat api (<a class="issue-link js-issue-link" data-error-text="Failed to load title" data-id="812885392" data-permission-text="Title is private" data-url="https://github.com/evcc-io/evcc/issues/681" data-hovercard-type="pull_request" data-hovercard-url="/evcc-io/evcc/pull/681/hovercard" href="https://github.com/evcc-io/evcc/pull/681">#681</a>)</li><li>Add Skoda api (<a class="issue-link js-issue-link" data-error-text="Failed to load title" data-id="812883796" data-permission-text="Title is private" data-url="https://github.com/evcc-io/evcc/issues/680" data-hovercard-type="pull_request" data-hovercard-url="/evcc-io/evcc/pull/680/hovercard" href="https://github.com/evcc-io/evcc/pull/680">#680</a>)</li><li>Add Peugeot/Opel/Citroen api (<a class="issue-link js-issue-link" data-error-text="Failed to load title" data-id="815612446" data-permission-text="Title is private" data-url="https://github.com/evcc-io/evcc/issues/692" data-hovercard-type="pull_request" data-hovercard-url="/evcc-io/evcc/pull/692/hovercard" href="https://github.com/evcc-io/evcc/pull/692">#692</a>)</li><li>KEBA: Support mA current resolution adjustments (<a class="issue-link js-issue-link" data-error-text="Failed to load title" data-id="801358501" data-permission-text="Title is private" data-url="https://github.com/evcc-io/evcc/issues/646" data-hovercard-type="pull_request" data-hovercard-url="/evcc-io/evcc/pull/646/hovercard" href="https://github.com/evcc-io/evcc/pull/646">#646</a>)</li></ul>';
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Story :layout="{ type: 'grid', width: 600 }">
|
||||
<Variant title="latest">
|
||||
<Version v-bind="state" />
|
||||
</Variant>
|
||||
<Variant title="nightly">
|
||||
<Version v-bind="state" commit="5ce7be4" />
|
||||
</Variant>
|
||||
<Variant title="update available">
|
||||
<Version v-bind="state" available="0.142" :releaseNotes="releaseNotes" />
|
||||
</Variant>
|
||||
<Variant title="auto updater">
|
||||
<Version v-bind="state" available="0.142" :releaseNotes="releaseNotes" hasUpdater />
|
||||
</Variant>
|
||||
<Variant title="no release notes">
|
||||
<Version v-bind="state" available="0.142" />
|
||||
</Variant>
|
||||
</Story>
|
||||
</template>
|
||||
74
assets/js/components/Loadpoints/Loadpoint.stories.js
Normal file
74
assets/js/components/Loadpoints/Loadpoint.stories.js
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
import Loadpoint from "./Loadpoint.vue";
|
||||
|
||||
export default {
|
||||
title: "Loadpoints/Loadpoint",
|
||||
component: Loadpoint,
|
||||
parameters: {
|
||||
layout: "centered",
|
||||
},
|
||||
};
|
||||
|
||||
const baseState = {
|
||||
id: 0,
|
||||
pvConfigured: true,
|
||||
chargePower: 2800,
|
||||
chargedEnergy: 11e3,
|
||||
chargeDuration: 95 * 60,
|
||||
vehicleTitle: "Mein Auto",
|
||||
vehicleName: "meinauto",
|
||||
enabled: true,
|
||||
connected: true,
|
||||
mode: "pv",
|
||||
charging: true,
|
||||
vehicleSoc: 66,
|
||||
limitSoc: 90,
|
||||
chargeCurrent: 7,
|
||||
minCurrent: 6,
|
||||
maxCurrent: 16,
|
||||
activePhases: 2,
|
||||
};
|
||||
|
||||
const createStory = (props) => {
|
||||
const story = (args) => ({
|
||||
components: { Loadpoint },
|
||||
setup() {
|
||||
return { args };
|
||||
},
|
||||
template: '<Loadpoint v-bind="args" />',
|
||||
});
|
||||
story.args = { ...baseState, ...props };
|
||||
return story;
|
||||
};
|
||||
|
||||
export const Standard = createStory({});
|
||||
|
||||
export const WithoutSoc = createStory({
|
||||
vehicleTitle: "",
|
||||
vehicleName: "",
|
||||
});
|
||||
|
||||
export const Idle = createStory({
|
||||
enabled: false,
|
||||
connected: false,
|
||||
vehicleName: "",
|
||||
mode: "off",
|
||||
charging: false,
|
||||
chargeCurrent: 0,
|
||||
});
|
||||
|
||||
export const DisabledLongTitle = createStory({
|
||||
title: "Charging point with a very very very long title!!!1!",
|
||||
remoteDisabled: "soft",
|
||||
remoteDisabledSource: "Sunny Home Manager",
|
||||
mode: "now",
|
||||
enabled: false,
|
||||
charging: false,
|
||||
chargePower: 0,
|
||||
});
|
||||
|
||||
export const ChargerIconNoVehicle = createStory({
|
||||
chargerIcon: "heater",
|
||||
title: "Heating device with long name",
|
||||
mode: "now",
|
||||
chargerFeatureIntegratedDevice: true,
|
||||
});
|
||||
|
|
@ -1,67 +0,0 @@
|
|||
<script setup>
|
||||
import Loadpoint from "./Loadpoint.vue";
|
||||
import { reactive } from "vue";
|
||||
|
||||
const state = reactive({
|
||||
id: 0,
|
||||
pvConfigured: true,
|
||||
chargePower: 2800,
|
||||
chargedEnergy: 11e3,
|
||||
chargeDuration: 95 * 60,
|
||||
vehicleTitle: "Mein Auto",
|
||||
vehicleName: "meinauto",
|
||||
enabled: true,
|
||||
connected: true,
|
||||
mode: "pv",
|
||||
charging: true,
|
||||
vehicleSoc: 66,
|
||||
limitSoc: 90,
|
||||
chargeCurrent: 7,
|
||||
minCurrent: 6,
|
||||
maxCurrent: 16,
|
||||
activePhases: 2,
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Story>
|
||||
<Variant title="standard">
|
||||
<Loadpoint v-bind="state" />
|
||||
</Variant>
|
||||
<Variant title="without soc">
|
||||
<Loadpoint v-bind="state" vehicleTitle="" vehicleName="" />
|
||||
</Variant>
|
||||
<Variant title="idle">
|
||||
<Loadpoint
|
||||
v-bind="state"
|
||||
:enabled="false"
|
||||
:connected="false"
|
||||
vehicleName=""
|
||||
mode="off"
|
||||
:charging="false"
|
||||
:chargeCurrent="0"
|
||||
/>
|
||||
</Variant>
|
||||
<Variant title="disabled, long title">
|
||||
<Loadpoint
|
||||
v-bind="state"
|
||||
title="Charging point with a very very very long title!!!1!"
|
||||
remoteDisabled="soft"
|
||||
remoteDisabledSource="Sunny Home Manager"
|
||||
mode="now"
|
||||
:enabled="false"
|
||||
:charging="false"
|
||||
:chargePower="0"
|
||||
/>
|
||||
</Variant>
|
||||
<Variant title="charger icon, no vehicle">
|
||||
<Loadpoint
|
||||
v-bind="state"
|
||||
chargerIcon="heater"
|
||||
title="Heating device with long name"
|
||||
mode="now"
|
||||
chargerFeatureIntegratedDevice
|
||||
/>
|
||||
</Variant>
|
||||
</Story>
|
||||
</template>
|
||||
53
assets/js/components/Loadpoints/Loadpoints.stories.js
Normal file
53
assets/js/components/Loadpoints/Loadpoints.stories.js
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
import Loadpoints from "./Loadpoints.vue";
|
||||
|
||||
function loadpoint(opts = {}) {
|
||||
const base = {
|
||||
id: 0,
|
||||
pvConfigured: true,
|
||||
chargePower: 2800,
|
||||
chargedEnergy: 11e3,
|
||||
chargeDuration: 95 * 60,
|
||||
vehicleName: "tesla",
|
||||
vehicleTitle: "Tesla Model 3",
|
||||
enabled: true,
|
||||
connected: true,
|
||||
mode: "pv",
|
||||
charging: true,
|
||||
vehicleSoc: 66,
|
||||
limitSoc: 90,
|
||||
chargeCurrent: 7,
|
||||
minCurrent: 6,
|
||||
maxCurrent: 16,
|
||||
activePhases: 2,
|
||||
};
|
||||
return { ...base, ...opts };
|
||||
}
|
||||
|
||||
export default {
|
||||
title: "Loadpoints/Loadpoints",
|
||||
component: Loadpoints,
|
||||
parameters: {
|
||||
layout: "centered",
|
||||
},
|
||||
};
|
||||
|
||||
const Template = (args) => ({
|
||||
components: { Loadpoints },
|
||||
setup() {
|
||||
return { args };
|
||||
},
|
||||
template: '<Loadpoints v-bind="args" />',
|
||||
});
|
||||
|
||||
export const Standard = Template.bind({});
|
||||
Standard.args = {
|
||||
vehicles: [],
|
||||
loadpoints: [
|
||||
loadpoint({ title: "Carport" }),
|
||||
loadpoint({
|
||||
title: "Water Heater",
|
||||
chargerFeatureIntegratedDevice: true,
|
||||
chargerIcon: "waterheater",
|
||||
}),
|
||||
],
|
||||
};
|
||||
|
|
@ -1,47 +0,0 @@
|
|||
<script setup>
|
||||
import Loadpoints from "./Loadpoints.vue";
|
||||
import { reactive } from "vue";
|
||||
|
||||
function loadpoint(opts) {
|
||||
const base = {
|
||||
id: 0,
|
||||
pvConfigured: true,
|
||||
chargePower: 2800,
|
||||
chargedEnergy: 11e3,
|
||||
chargeDuration: 95 * 60,
|
||||
vehicleName: "tesla",
|
||||
vehicleTitle: "Tesla Model 3",
|
||||
enabled: true,
|
||||
connected: true,
|
||||
mode: "pv",
|
||||
charging: true,
|
||||
vehicleSoc: 66,
|
||||
limitSoc: 90,
|
||||
chargeCurrent: 7,
|
||||
minCurrent: 6,
|
||||
maxCurrent: 16,
|
||||
activePhases: 2,
|
||||
};
|
||||
return { ...base, ...opts };
|
||||
}
|
||||
|
||||
const state = reactive({
|
||||
vehicles: [],
|
||||
loadpoints: [
|
||||
loadpoint({ title: "Carport" }),
|
||||
loadpoint({
|
||||
title: "Water Heater",
|
||||
chargerFeatureIntegratedDevice: true,
|
||||
chargerIcon: "waterheater",
|
||||
}),
|
||||
],
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Story>
|
||||
<Variant title="standard">
|
||||
<Loadpoints v-bind="state" />
|
||||
</Variant>
|
||||
</Story>
|
||||
</template>
|
||||
44
assets/js/components/Loadpoints/Mode.stories.js
Normal file
44
assets/js/components/Loadpoints/Mode.stories.js
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
import Mode from "./Mode.vue";
|
||||
|
||||
export default {
|
||||
title: "Loadpoints/Mode",
|
||||
component: Mode,
|
||||
argTypes: {
|
||||
mode: {
|
||||
control: "select",
|
||||
options: ["off", "now", "minpv", "pv"],
|
||||
description: "Charging mode",
|
||||
},
|
||||
pvPossible: { control: "boolean", description: "Whether PV is possible" },
|
||||
hasSmartCost: { control: "boolean", description: "Whether smart cost is available" },
|
||||
},
|
||||
parameters: {
|
||||
layout: "centered",
|
||||
},
|
||||
};
|
||||
|
||||
const createStory = (props) => {
|
||||
const story = (args) => ({
|
||||
components: { Mode },
|
||||
setup() {
|
||||
return { args };
|
||||
},
|
||||
template: '<Mode v-bind="args" />',
|
||||
});
|
||||
story.args = props;
|
||||
return story;
|
||||
};
|
||||
|
||||
export const Minimal = createStory({ mode: "now" });
|
||||
|
||||
export const Full = createStory({
|
||||
mode: "pv",
|
||||
pvPossible: true,
|
||||
hasSmartCost: true,
|
||||
});
|
||||
|
||||
export const SmartGridOnly = createStory({
|
||||
mode: "pv",
|
||||
pvPossible: false,
|
||||
hasSmartCost: true,
|
||||
});
|
||||
|
|
@ -1,28 +0,0 @@
|
|||
<script setup>
|
||||
import { reactive } from "vue";
|
||||
import Mode from "./Mode.vue";
|
||||
|
||||
const state = reactive({
|
||||
mode: "pv",
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Story auto-props-disabled>
|
||||
<Variant title="standard">
|
||||
<Mode :mode="state.mode" />
|
||||
<template #controls>
|
||||
<HstSelect
|
||||
v-model="state.mode"
|
||||
title="mode"
|
||||
:options="{
|
||||
off: 'off',
|
||||
now: 'now',
|
||||
minpv: 'minpv',
|
||||
pv: 'pv',
|
||||
}"
|
||||
/>
|
||||
</template>
|
||||
</Variant>
|
||||
</Story>
|
||||
</template>
|
||||
94
assets/js/components/Loadpoints/Phases.stories.js
Normal file
94
assets/js/components/Loadpoints/Phases.stories.js
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
import Phases from "./Phases.vue";
|
||||
|
||||
export default {
|
||||
title: "Loadpoints/Phases",
|
||||
component: Phases,
|
||||
parameters: {
|
||||
layout: "centered",
|
||||
},
|
||||
argTypes: {
|
||||
phasesActive: { control: { type: "number" } },
|
||||
minCurrent: { control: { type: "number" } },
|
||||
maxCurrent: { control: { type: "number" } },
|
||||
chargeCurrent: { control: { type: "number" } },
|
||||
chargeCurrents: { control: { type: "object" } },
|
||||
},
|
||||
};
|
||||
|
||||
const Template = (args) => ({
|
||||
components: { Phases },
|
||||
setup() {
|
||||
return { args };
|
||||
},
|
||||
template: '<Phases v-bind="args" />',
|
||||
});
|
||||
|
||||
export const OnePhase = Template.bind({});
|
||||
OnePhase.args = {
|
||||
phasesActive: 1,
|
||||
minCurrent: 6,
|
||||
maxCurrent: 16,
|
||||
chargeCurrent: 8,
|
||||
chargeCurrents: null,
|
||||
};
|
||||
|
||||
export const TwoPhase = Template.bind({});
|
||||
TwoPhase.args = {
|
||||
...OnePhase.args,
|
||||
phasesActive: 2,
|
||||
};
|
||||
|
||||
export const ThreePhase = Template.bind({});
|
||||
ThreePhase.args = {
|
||||
...OnePhase.args,
|
||||
phasesActive: 3,
|
||||
};
|
||||
|
||||
export const RealCurrents = Template.bind({});
|
||||
RealCurrents.args = {
|
||||
...OnePhase.args,
|
||||
phasesActive: 3,
|
||||
chargeCurrent: 13,
|
||||
chargeCurrents: [11, 9, 12],
|
||||
};
|
||||
|
||||
export const OnePhaseMoreAvailable = Template.bind({});
|
||||
OnePhaseMoreAvailable.args = {
|
||||
...OnePhase.args,
|
||||
chargeCurrent: 12,
|
||||
chargeCurrents: [6, 0.2, 0],
|
||||
};
|
||||
|
||||
export const TwoPhasesActive = Template.bind({});
|
||||
TwoPhasesActive.args = {
|
||||
...OnePhase.args,
|
||||
phasesActive: 2,
|
||||
chargeCurrent: 16,
|
||||
chargeCurrents: [16, 16, 0.3],
|
||||
};
|
||||
|
||||
export const AsymetricPhases = Template.bind({});
|
||||
AsymetricPhases.args = {
|
||||
...OnePhase.args,
|
||||
phasesActive: 2,
|
||||
chargeCurrent: 16,
|
||||
chargeCurrents: [8, 0.9, 14],
|
||||
};
|
||||
|
||||
export const OnlySecondPhase = Template.bind({});
|
||||
OnlySecondPhase.args = {
|
||||
...OnePhase.args,
|
||||
phasesActive: 1,
|
||||
chargeCurrent: 13,
|
||||
chargeCurrents: [0, 13, 0],
|
||||
};
|
||||
|
||||
export const MainlyThirdPhase = Template.bind({});
|
||||
MainlyThirdPhase.args = {
|
||||
...OnePhase.args,
|
||||
phasesActive: 1,
|
||||
chargeCurrent: 10,
|
||||
chargeCurrents: [0.007, 0.009, 5.945],
|
||||
minCurrent: 6,
|
||||
maxCurrent: 20,
|
||||
};
|
||||
|
|
@ -1,71 +0,0 @@
|
|||
<script setup>
|
||||
import Phases from "./Phases.vue";
|
||||
import { reactive } from "vue";
|
||||
|
||||
const state = reactive({
|
||||
phasesActive: 1,
|
||||
minCurrent: 6,
|
||||
maxCurrent: 16,
|
||||
chargeCurrent: 8,
|
||||
chargeCurrents: null,
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Story :layout="{ type: 'grid', width: 300 }">
|
||||
<Variant title="one phase">
|
||||
<Phases v-bind="state" />
|
||||
</Variant>
|
||||
<Variant title="two phase">
|
||||
<Phases v-bind="state" :phasesActive="2" />
|
||||
</Variant>
|
||||
<Variant title="three phase">
|
||||
<Phases v-bind="state" :phasesActive="3" />
|
||||
</Variant>
|
||||
<Variant title="real currents">
|
||||
<Phases
|
||||
v-bind="state"
|
||||
:phasesActive="3"
|
||||
:chargeCurrent="13"
|
||||
:chargeCurrents="[11, 9, 12]"
|
||||
/>
|
||||
</Variant>
|
||||
<Variant title="one phase, more available">
|
||||
<Phases v-bind="state" :chargeCurrent="12" :chargeCurrents="[6, 0.2, 0]" />
|
||||
</Variant>
|
||||
<Variant title="two phases active">
|
||||
<Phases
|
||||
v-bind="state"
|
||||
:phasesActive="2"
|
||||
:chargeCurrent="16"
|
||||
:chargeCurrents="[16, 16, 0.3]"
|
||||
/>
|
||||
</Variant>
|
||||
<Variant title="asymetric phases">
|
||||
<Phases
|
||||
v-bind="state"
|
||||
:phasesActive="2"
|
||||
:chargeCurrent="16"
|
||||
:chargeCurrents="[8, 0.9, 14]"
|
||||
/>
|
||||
</Variant>
|
||||
<Variant title="only second phase">
|
||||
<Phases
|
||||
v-bind="state"
|
||||
:phasesActive="1"
|
||||
:chargeCurrent="13"
|
||||
:chargeCurrents="[0, 13, 0]"
|
||||
/>
|
||||
</Variant>
|
||||
<Variant title="mainly third phase">
|
||||
<Phases
|
||||
v-bind="state"
|
||||
:phasesActive="1"
|
||||
:chargeCurrent="10"
|
||||
:chargeCurrents="[0.007, 0.009, 5.945]"
|
||||
:minCurrent="6"
|
||||
:maxCurrent="20"
|
||||
/>
|
||||
</Variant>
|
||||
</Story>
|
||||
</template>
|
||||
44
assets/js/components/MultiIcon/MultiIcon.stories.js
Normal file
44
assets/js/components/MultiIcon/MultiIcon.stories.js
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
import MultiIcon from "./MultiIcon.vue";
|
||||
|
||||
export default {
|
||||
title: "MultiIcon/MultiIcon",
|
||||
component: MultiIcon,
|
||||
argTypes: {
|
||||
count: {
|
||||
control: { type: "number", min: 1, max: 10 },
|
||||
description: "Number of icons to display",
|
||||
},
|
||||
size: {
|
||||
control: { type: "select", options: ["sm", "md", "lg", "xl"] },
|
||||
description: "Size of the icons",
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const SingleIcon = (args) => ({
|
||||
components: { MultiIcon },
|
||||
setup() {
|
||||
return { args };
|
||||
},
|
||||
template: '<MultiIcon v-bind="args" />',
|
||||
});
|
||||
|
||||
export const AllCounts = (args) => ({
|
||||
components: { MultiIcon },
|
||||
setup() {
|
||||
const counts = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
|
||||
return { counts, args };
|
||||
},
|
||||
template: `
|
||||
<div style="display: grid; grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: 30px;">
|
||||
<div v-for="count in counts" :key="count" style="display: flex; flex-direction: column; align-items: center; gap: 10px;">
|
||||
<MultiIcon :count="count" :size="args.size" />
|
||||
<small>{{ count }}</small>
|
||||
</div>
|
||||
</div>
|
||||
`,
|
||||
});
|
||||
|
||||
AllCounts.args = {
|
||||
size: "xl",
|
||||
};
|
||||
|
|
@ -1,15 +0,0 @@
|
|||
<script setup>
|
||||
import MultiIcon from "./MultiIcon.vue";
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Story :layout="{ type: 'grid', width: 150 }">
|
||||
<Variant
|
||||
v-for="count in [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]"
|
||||
:key="`${count}`"
|
||||
:title="`${count}`"
|
||||
>
|
||||
<MultiIcon :count="count" size="xl" />
|
||||
</Variant>
|
||||
</Story>
|
||||
</template>
|
||||
16
assets/js/components/Savings/LiveCommunity.stories.js
Normal file
16
assets/js/components/Savings/LiveCommunity.stories.js
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
import LiveCommunity from "./LiveCommunity.vue";
|
||||
|
||||
export default {
|
||||
title: "Savings/LiveCommunity",
|
||||
component: LiveCommunity,
|
||||
parameters: {
|
||||
layout: "centered",
|
||||
},
|
||||
};
|
||||
|
||||
const Template = () => ({
|
||||
components: { LiveCommunity },
|
||||
template: "<LiveCommunity />",
|
||||
});
|
||||
|
||||
export const Default = Template.bind({});
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
<script setup>
|
||||
import LiveCommunity from "./LiveCommunity.vue";
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Story>
|
||||
<LiveCommunity />
|
||||
</Story>
|
||||
</template>
|
||||
|
|
@ -1,31 +0,0 @@
|
|||
<script setup>
|
||||
import { reactive } from "vue";
|
||||
import Savings from "./Savings.vue";
|
||||
|
||||
const state = reactive({
|
||||
totalCharged: 408.88948736396026,
|
||||
selfConsumptionPercent: 80.3,
|
||||
since: 1666776003,
|
||||
amount: 11.924127889686181,
|
||||
effectivePrice: 0.22583777338821223,
|
||||
gridCharged: 313.4964642464708,
|
||||
selfConsumptionCharged: 95.39302311748945,
|
||||
gridPrice: 0.34,
|
||||
feedInPrice: 0.13,
|
||||
currency: "EUR",
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Story>
|
||||
<Variant title="no sponsor">
|
||||
<Savings v-bind="state" />
|
||||
</Variant>
|
||||
<Variant title="sponsor">
|
||||
<Savings v-bind="state" sponsor="naltatis" />
|
||||
</Variant>
|
||||
<Variant title="no data">
|
||||
<Savings />
|
||||
</Variant>
|
||||
</Story>
|
||||
</template>
|
||||
59
assets/js/components/Savings/Sponsor.stories.js
Normal file
59
assets/js/components/Savings/Sponsor.stories.js
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
import Sponsor from "./Sponsor.vue";
|
||||
|
||||
export default {
|
||||
title: "Savings/Sponsor",
|
||||
component: Sponsor,
|
||||
parameters: {
|
||||
layout: "centered",
|
||||
},
|
||||
argTypes: {
|
||||
name: {
|
||||
control: "text",
|
||||
description: "Sponsor name (use 'trial' or 'victron' for special states)",
|
||||
},
|
||||
expiresAt: {
|
||||
control: "text",
|
||||
description: "When the sponsorship expires (ISO date string)",
|
||||
},
|
||||
expiresSoon: {
|
||||
control: "boolean",
|
||||
description: "Whether the sponsorship is expiring soon",
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
// Template for rendering the component
|
||||
const Template = (args) => ({
|
||||
components: { Sponsor },
|
||||
setup() {
|
||||
return { args };
|
||||
},
|
||||
template: '<Sponsor v-bind="args" />',
|
||||
});
|
||||
|
||||
// Create stories for each variant
|
||||
export const NoSponsor = Template.bind({});
|
||||
NoSponsor.args = {};
|
||||
|
||||
export const Trial = Template.bind({});
|
||||
Trial.args = {
|
||||
name: "trial",
|
||||
};
|
||||
|
||||
export const IndividualSponsor = Template.bind({});
|
||||
IndividualSponsor.args = {
|
||||
name: "naltatis",
|
||||
};
|
||||
|
||||
export const VictronDevice = Template.bind({});
|
||||
VictronDevice.args = {
|
||||
name: "victron",
|
||||
};
|
||||
|
||||
// Add an extra story showing the expiring state
|
||||
export const ExpiringSponsor = Template.bind({});
|
||||
ExpiringSponsor.args = {
|
||||
name: "naltatis",
|
||||
expiresSoon: true,
|
||||
expiresAt: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000).toISOString(), // 7 days from now
|
||||
};
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
<script setup>
|
||||
import Sponsor from "./Sponsor.vue";
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Story :layout="{ type: 'grid', width: 600 }">
|
||||
<Variant title="no sponsor">
|
||||
<Sponsor />
|
||||
</Variant>
|
||||
<Variant title="trial">
|
||||
<Sponsor sponsor="trial" />
|
||||
</Variant>
|
||||
<Variant title="sponsor">
|
||||
<Sponsor sponsor="naltatis" />
|
||||
</Variant>
|
||||
<Variant title="victron device">
|
||||
<Sponsor sponsor="victron" />
|
||||
</Variant>
|
||||
</Story>
|
||||
</template>
|
||||
33
assets/js/components/Savings/SponsorTokenExpires.stories.js
Normal file
33
assets/js/components/Savings/SponsorTokenExpires.stories.js
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
import SponsorTokenExpires from "./SponsorTokenExpires.vue";
|
||||
|
||||
export default {
|
||||
title: "Savings/SponsorTokenExpires",
|
||||
component: SponsorTokenExpires,
|
||||
parameters: {
|
||||
layout: "centered",
|
||||
},
|
||||
argTypes: {
|
||||
expiresSoon: { control: "boolean", description: "Whether sponsorship is expiring soon" },
|
||||
expiresAt: { control: "text", description: "Date when the sponsor token expires" },
|
||||
name: { control: "text", description: "Name of the sponsor token" },
|
||||
},
|
||||
};
|
||||
|
||||
// Template for rendering the component
|
||||
const Template = (args) => ({
|
||||
components: { SponsorTokenExpires },
|
||||
setup() {
|
||||
return { args };
|
||||
},
|
||||
template: '<SponsorTokenExpires v-bind="args" />',
|
||||
});
|
||||
|
||||
// Create stories for each variant
|
||||
export const SomeDay = Template.bind({});
|
||||
SomeDay.args = {
|
||||
expiresSoon: true,
|
||||
expiresAt: new Date(Date.now() + 22 * 20 * 60 * 1000).toISOString(),
|
||||
};
|
||||
|
||||
export const Empty = Template.bind({});
|
||||
Empty.args = {};
|
||||
|
|
@ -1,14 +0,0 @@
|
|||
<script setup>
|
||||
import SponsorTokenExpires from "./SponsorTokenExpires.vue";
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Story title="Sponsor Token Expires">
|
||||
<Variant title="some day">
|
||||
<SponsorTokenExpires :sponsorSoon="true" expiresAt="2055-01-01T00:00:00Z" />
|
||||
</Variant>
|
||||
<Variant title="empty">
|
||||
<SponsorTokenExpires />
|
||||
</Variant>
|
||||
</Story>
|
||||
</template>
|
||||
37
assets/js/components/Savings/Tile.stories.js
Normal file
37
assets/js/components/Savings/Tile.stories.js
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
import Tile from "./Tile.vue";
|
||||
|
||||
export default {
|
||||
title: "Savings/Tile",
|
||||
component: Tile,
|
||||
parameters: {
|
||||
layout: "centered",
|
||||
},
|
||||
argTypes: {
|
||||
icon: { control: "text", description: "Icon name to display" },
|
||||
title: { control: "text", description: "Title of the tile" },
|
||||
value: { control: "text", description: "Primary value to display" },
|
||||
unit: { control: "text", description: "Unit for the value" },
|
||||
sub1: { control: "text", description: "First subtitle" },
|
||||
sub2: { control: "text", description: "Second subtitle" },
|
||||
},
|
||||
};
|
||||
|
||||
// Template for rendering the component
|
||||
const Template = (args) => ({
|
||||
components: { Tile },
|
||||
setup() {
|
||||
return { args };
|
||||
},
|
||||
template: '<Tile v-bind="args" />',
|
||||
});
|
||||
|
||||
// Create default story with the example props
|
||||
export const Default = Template.bind({});
|
||||
Default.args = {
|
||||
icon: "coinjar",
|
||||
title: "Ersparnis",
|
||||
value: "14,2",
|
||||
unit: "€",
|
||||
sub1: "gegenüber Netzbezug",
|
||||
sub2: "seit Dezember 2022",
|
||||
};
|
||||
|
|
@ -1,16 +0,0 @@
|
|||
<script setup>
|
||||
import Tile from "./Tile.vue";
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Story>
|
||||
<Tile
|
||||
icon="coinjar"
|
||||
title="Ersparnis"
|
||||
value="14,2"
|
||||
unit="€"
|
||||
sub1="gegenüber Netzbezug"
|
||||
sub2="seit Dezember 2022"
|
||||
/>
|
||||
</Story>
|
||||
</template>
|
||||
57
assets/js/components/Top/Navigation.stories.js
Normal file
57
assets/js/components/Top/Navigation.stories.js
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
import Navigation from "./Navigation.vue";
|
||||
|
||||
export default {
|
||||
title: "Top/Navigation",
|
||||
component: Navigation,
|
||||
parameters: {
|
||||
layout: "centered",
|
||||
},
|
||||
argTypes: {
|
||||
vehicleLogins: { control: "object" },
|
||||
sponsor: { control: "object" },
|
||||
},
|
||||
};
|
||||
|
||||
const Template = (args) => ({
|
||||
components: { Navigation },
|
||||
setup() {
|
||||
return { args };
|
||||
},
|
||||
template: '<Navigation v-bind="args" />',
|
||||
});
|
||||
|
||||
export const Standard = Template.bind({});
|
||||
Standard.args = {};
|
||||
|
||||
export const VehicleLogins = Template.bind({});
|
||||
VehicleLogins.args = {
|
||||
vehicleLogins: {
|
||||
"Mercedes EQS": {
|
||||
authenticated: true,
|
||||
uri: "https://login-provider-a.test/",
|
||||
},
|
||||
"Nissan Leaf Pro": {
|
||||
authenticated: true,
|
||||
uri: "https://login-provider-b.test/",
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const PendingVehicleLogins = Template.bind({});
|
||||
PendingVehicleLogins.args = {
|
||||
vehicleLogins: {
|
||||
"Mercedes EQS": {
|
||||
authenticated: true,
|
||||
uri: "https://login-provider-a.test/",
|
||||
},
|
||||
"Nissan Leaf Pro": {
|
||||
authenticated: false,
|
||||
uri: "https://login-provider-b.test/",
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const TokenExpires = Template.bind({});
|
||||
TokenExpires.args = {
|
||||
sponsor: { expiresSoon: true },
|
||||
};
|
||||
|
|
@ -1,42 +0,0 @@
|
|||
<script setup>
|
||||
import Navigation from "./Navigation.vue";
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Story title="Top Navigation">
|
||||
<Variant title="standard">
|
||||
<Navigation />
|
||||
</Variant>
|
||||
<Variant title="vehicle logins">
|
||||
<Navigation
|
||||
:vehicleLogins="{
|
||||
['Mercedes EQS']: {
|
||||
authenticated: true,
|
||||
uri: 'https://login-provider-a.test/',
|
||||
},
|
||||
['Nissan Leaf Pro']: {
|
||||
authenticated: true,
|
||||
uri: 'https://login-provider-b.test/',
|
||||
},
|
||||
}"
|
||||
/>
|
||||
</Variant>
|
||||
<Variant title="pending vehicle logins">
|
||||
<Navigation
|
||||
:vehicleLogins="{
|
||||
['Mercedes EQS']: {
|
||||
authenticated: true,
|
||||
uri: 'https://login-provider-a.test/',
|
||||
},
|
||||
['Nissan Leaf Pro']: {
|
||||
authenticated: false,
|
||||
uri: 'https://login-provider-b.test/',
|
||||
},
|
||||
}"
|
||||
/>
|
||||
</Variant>
|
||||
<Variant title="token expires">
|
||||
<Navigation :sponsor="{ expiresSoon: true }" />
|
||||
</Variant>
|
||||
</Story>
|
||||
</template>
|
||||
80
assets/js/components/Top/Notifications.stories.js
Normal file
80
assets/js/components/Top/Notifications.stories.js
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
import Notifications from "./Notifications.vue";
|
||||
|
||||
const timeAgo = (hours = 0, minutes = 0, seconds = 0) => {
|
||||
const date = new Date();
|
||||
date.setHours(date.getHours() - hours);
|
||||
date.setMinutes(date.getMinutes() - minutes);
|
||||
date.setSeconds(date.getSeconds() - seconds);
|
||||
return date;
|
||||
};
|
||||
|
||||
const notificationsData = [
|
||||
{
|
||||
message: "Server unavailable",
|
||||
level: "error",
|
||||
time: timeAgo(),
|
||||
count: 1,
|
||||
},
|
||||
{
|
||||
message: "charger out of sync: expected disabled, got enabled",
|
||||
level: "warn",
|
||||
count: 4,
|
||||
time: timeAgo(0, 0, 42),
|
||||
},
|
||||
{
|
||||
message: "Sponsortoken: x509: certificate has expired",
|
||||
level: "error",
|
||||
count: 1,
|
||||
time: timeAgo(1, 12, 44),
|
||||
},
|
||||
{
|
||||
message:
|
||||
"Block device full: can not write to /tmp/evcc/foobarloremawefhwuiehfwuiehfwiauhefjkajowaeigjwalvmoweivwail",
|
||||
level: "error",
|
||||
count: 1,
|
||||
time: timeAgo(1, 12, 44),
|
||||
},
|
||||
{
|
||||
message: "charger out of sync: expected disabled, got enabled",
|
||||
level: "warn",
|
||||
count: 4,
|
||||
time: timeAgo(1, 22, 0),
|
||||
},
|
||||
{
|
||||
message: "vehicle remote charge start: invalid character '<' looking for beginning of value",
|
||||
level: "warn",
|
||||
count: 3,
|
||||
time: timeAgo(4, 2, 0),
|
||||
},
|
||||
{
|
||||
message:
|
||||
"Amet irure quis incididunt voluptate esse. Commodo ea sunt est ipsum tempor nisi laboris voluptate labore elit laborum. Ex irure commodo reprehenderit consequat consequat do ad tempor aliquip deserunt eu. Laboris minim nostrud quis nisi. Dolor occaecat reprehenderit velit dolore exercitation cupidatat et voluptate. Nulla pariatur deserunt esse minim nisi nisi nulla. Sit eiusmod do incididunt sint minim pariatur aute.",
|
||||
level: "warn",
|
||||
count: 1,
|
||||
time: timeAgo(5, 2, 44),
|
||||
},
|
||||
];
|
||||
|
||||
export default {
|
||||
title: "Top/Notifications",
|
||||
component: Notifications,
|
||||
parameters: {
|
||||
layout: "centered",
|
||||
},
|
||||
argTypes: {
|
||||
notifications: { control: "object" },
|
||||
},
|
||||
};
|
||||
|
||||
const Template = (args) => ({
|
||||
components: { Notifications },
|
||||
setup() {
|
||||
return { args };
|
||||
},
|
||||
template: '<Notifications v-bind="args" />',
|
||||
});
|
||||
|
||||
export const Default = Template.bind({});
|
||||
Default.args = {
|
||||
notifications: notificationsData,
|
||||
};
|
||||
|
|
@ -1,70 +0,0 @@
|
|||
<script setup>
|
||||
import { reactive } from "vue";
|
||||
import Notifications from "./Notifications.vue";
|
||||
|
||||
const timeAgo = (hours = 0, minutes = 0, seconds = 0) => {
|
||||
const date = new Date();
|
||||
date.setHours(date.getHours() - hours);
|
||||
date.setMinutes(date.getMinutes() - minutes);
|
||||
date.setSeconds(date.getSeconds() - seconds);
|
||||
return date;
|
||||
};
|
||||
|
||||
const state = reactive({
|
||||
notifications: [
|
||||
{
|
||||
message: "Server unavailable",
|
||||
level: "error",
|
||||
time: timeAgo(),
|
||||
count: 1,
|
||||
},
|
||||
{
|
||||
message: "charger out of sync: expected disabled, got enabled",
|
||||
level: "warn",
|
||||
count: 4,
|
||||
time: timeAgo(0, 0, 42),
|
||||
},
|
||||
{
|
||||
message: "Sponsortoken: x509: certificate has expired",
|
||||
level: "error",
|
||||
count: 1,
|
||||
time: timeAgo(1, 12, 44),
|
||||
},
|
||||
{
|
||||
message:
|
||||
"Block device full: can not write to /tmp/evcc/foobarloremawefhwuiehfwuiehfwiauhefjkajowaeigjwalvmoweivwail",
|
||||
level: "error",
|
||||
count: 1,
|
||||
time: timeAgo(1, 12, 44),
|
||||
},
|
||||
{
|
||||
message: "charger out of sync: expected disabled, got enabled",
|
||||
level: "warn",
|
||||
count: 4,
|
||||
time: timeAgo(1, 22, 0),
|
||||
},
|
||||
{
|
||||
message:
|
||||
"vehicle remote charge start: invalid character '<' looking for beginning of value",
|
||||
level: "warn",
|
||||
count: 3,
|
||||
time: timeAgo(4, 2, 0),
|
||||
},
|
||||
{
|
||||
message:
|
||||
"Amet irure quis incididunt voluptate esse. Commodo ea sunt est ipsum tempor nisi laboris voluptate labore elit laborum. Ex irure commodo reprehenderit consequat consequat do ad tempor aliquip deserunt eu. Laboris minim nostrud quis nisi. Dolor occaecat reprehenderit velit dolore exercitation cupidatat et voluptate. Nulla pariatur deserunt esse minim nisi nisi nulla. Sit eiusmod do incididunt sint minim pariatur aute.",
|
||||
level: "warn",
|
||||
count: 1,
|
||||
time: timeAgo(5, 2, 44),
|
||||
},
|
||||
],
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Story>
|
||||
<Variant title="default">
|
||||
<Notifications v-bind="state" />
|
||||
</Variant>
|
||||
</Story>
|
||||
</template>
|
||||
103
assets/js/components/VehicleIcon/VehicleIcon.stories.js
Normal file
103
assets/js/components/VehicleIcon/VehicleIcon.stories.js
Normal file
|
|
@ -0,0 +1,103 @@
|
|||
import VehicleIcon from "./VehicleIcon.vue";
|
||||
|
||||
const icons = [
|
||||
"car",
|
||||
"bike",
|
||||
"bus",
|
||||
"moped",
|
||||
"motorcycle",
|
||||
"rocket",
|
||||
"scooter",
|
||||
"taxi",
|
||||
"tractor",
|
||||
"rickshaw",
|
||||
"shuttle",
|
||||
"van",
|
||||
"airpurifier",
|
||||
"battery",
|
||||
"bulb",
|
||||
"climate",
|
||||
"coffeemaker",
|
||||
"compute",
|
||||
"cooking",
|
||||
"cooler",
|
||||
"desktop",
|
||||
"device",
|
||||
"dishwasher",
|
||||
"dryer",
|
||||
"floorlamp",
|
||||
"generic",
|
||||
"heater",
|
||||
"heatexchange",
|
||||
"heatpump",
|
||||
"kettle",
|
||||
"laundry",
|
||||
"laundry2",
|
||||
"machine",
|
||||
"meter",
|
||||
"microwave",
|
||||
"pump",
|
||||
"tool",
|
||||
"waterheater",
|
||||
];
|
||||
|
||||
export default {
|
||||
title: "VehicleIcon/VehicleIcon",
|
||||
component: VehicleIcon,
|
||||
argTypes: {
|
||||
name: {
|
||||
control: "select",
|
||||
options: icons,
|
||||
},
|
||||
names: { control: "object" },
|
||||
size: {
|
||||
control: "select",
|
||||
options: ["sm", "md", "lg", "xl"],
|
||||
defaultValue: "xl",
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const Template = (args) => ({
|
||||
components: { VehicleIcon },
|
||||
setup() {
|
||||
return { args };
|
||||
},
|
||||
template: '<VehicleIcon v-bind="args" />',
|
||||
});
|
||||
|
||||
// Single icon story that can be controlled via args
|
||||
export const SingleIcon = Template.bind({});
|
||||
SingleIcon.args = {
|
||||
name: "car",
|
||||
size: "xl",
|
||||
};
|
||||
|
||||
// Multiple icons stories
|
||||
export const TwoCars = Template.bind({});
|
||||
TwoCars.args = {
|
||||
names: ["car", "car"],
|
||||
size: "xl",
|
||||
};
|
||||
|
||||
export const CarAndThreeBikes = Template.bind({});
|
||||
CarAndThreeBikes.args = {
|
||||
names: ["car", "bike", "bike", "bike"],
|
||||
size: "xl",
|
||||
};
|
||||
|
||||
// Story showing all icons at once
|
||||
export const AllIcons = () => ({
|
||||
components: { VehicleIcon },
|
||||
setup() {
|
||||
return { icons };
|
||||
},
|
||||
template: `
|
||||
<div style="display: grid; grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: 30px;">
|
||||
<div v-for="icon in icons" :key="icon" style="display: flex; flex-direction: column; align-items: center; gap: 10px;">
|
||||
<VehicleIcon :name="icon" size="xl" />
|
||||
<small>{{ icon }}</small>
|
||||
</div>
|
||||
</div>
|
||||
`,
|
||||
});
|
||||
|
|
@ -1,58 +0,0 @@
|
|||
<script setup>
|
||||
import VehicleIcon from "./VehicleIcon.vue";
|
||||
|
||||
const icons = [
|
||||
"car",
|
||||
"bike",
|
||||
"bus",
|
||||
"moped",
|
||||
"motorcycle",
|
||||
"rocket",
|
||||
"scooter",
|
||||
"taxi",
|
||||
"tractor",
|
||||
"rickshaw",
|
||||
"shuttle",
|
||||
"van",
|
||||
"airpurifier",
|
||||
"battery",
|
||||
"bulb",
|
||||
"climate",
|
||||
"coffeemaker",
|
||||
"compute",
|
||||
"cooking",
|
||||
"cooler",
|
||||
"desktop",
|
||||
"device",
|
||||
"dishwasher",
|
||||
"dryer",
|
||||
"floorlamp",
|
||||
"generic",
|
||||
"heater",
|
||||
"heatexchange",
|
||||
"heatpump",
|
||||
"kettle",
|
||||
"laundry",
|
||||
"laundry2",
|
||||
"machine",
|
||||
"meter",
|
||||
"microwave",
|
||||
"pump",
|
||||
"tool",
|
||||
"waterheater",
|
||||
];
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Story :layout="{ type: 'grid', width: 150 }">
|
||||
<Variant v-for="icon in icons" :key="icon" :title="icon">
|
||||
<VehicleIcon :name="icon" size="xl" />
|
||||
</Variant>
|
||||
<Variant title="2 cars">
|
||||
<VehicleIcon :names="['car', 'car']" size="xl" />
|
||||
</Variant>
|
||||
<Variant title="car & 3 bikes">
|
||||
<VehicleIcon :names="['car', 'bike', 'bike', 'bike']" size="xl" />
|
||||
</Variant>
|
||||
</Story>
|
||||
</template>
|
||||
188
assets/js/components/Vehicles/Vehicle.stories.js
Normal file
188
assets/js/components/Vehicles/Vehicle.stories.js
Normal file
|
|
@ -0,0 +1,188 @@
|
|||
import Vehicle from "./Vehicle.vue";
|
||||
|
||||
const hoursFromNow = (hours) => {
|
||||
const now = new Date();
|
||||
now.setHours(now.getHours() + hours);
|
||||
return now.toISOString();
|
||||
};
|
||||
|
||||
const baseState = {
|
||||
vehicle: { title: "Mein Auto", icon: "car", capacity: 72, features: [] },
|
||||
enabled: false,
|
||||
connected: true,
|
||||
vehicleName: "meinauto",
|
||||
vehicleSoc: 42.742,
|
||||
vehicleRange: 231,
|
||||
limitSoc: 90,
|
||||
chargedEnergy: 14123,
|
||||
socBasedCharging: true,
|
||||
id: 0,
|
||||
};
|
||||
|
||||
export default {
|
||||
title: "Vehicles/Vehicle",
|
||||
component: Vehicle,
|
||||
argTypes: {
|
||||
chargedEnergy: { control: "number" },
|
||||
charging: { control: "boolean" },
|
||||
vehicleClimaterActive: { control: "boolean" },
|
||||
vehicleWelcomeActive: { control: "boolean" },
|
||||
connected: { control: "boolean" },
|
||||
currency: { control: "text" },
|
||||
effectiveLimitSoc: { control: "number" },
|
||||
effectivePlanSoc: { control: "number" },
|
||||
effectivePlanTime: { control: "text" },
|
||||
batteryBoostActive: { control: "boolean" },
|
||||
enabled: { control: "boolean" },
|
||||
heating: { control: "boolean" },
|
||||
id: { control: "text" },
|
||||
integratedDevice: { control: "boolean" },
|
||||
limitEnergy: { control: "number" },
|
||||
mode: { control: "text" },
|
||||
chargerStatusReason: { control: "text" },
|
||||
vehicle: { control: "object" },
|
||||
vehicleName: { control: "text" },
|
||||
vehicleRange: { control: "number" },
|
||||
vehicleSoc: { control: "number" },
|
||||
vehicleLimitSoc: { control: "number" },
|
||||
socBasedCharging: { control: "boolean" },
|
||||
},
|
||||
};
|
||||
|
||||
const Template = (args) => ({
|
||||
components: { Vehicle },
|
||||
setup() {
|
||||
return { args };
|
||||
},
|
||||
template: '<Vehicle v-bind="args" />',
|
||||
});
|
||||
|
||||
export const Disconnected = Template.bind({});
|
||||
Disconnected.args = {
|
||||
...baseState,
|
||||
connected: false,
|
||||
};
|
||||
|
||||
export const Connected = Template.bind({});
|
||||
Connected.args = {
|
||||
...baseState,
|
||||
};
|
||||
|
||||
export const Ready = Template.bind({});
|
||||
Ready.args = {
|
||||
...baseState,
|
||||
enabled: true,
|
||||
};
|
||||
|
||||
export const Charging = Template.bind({});
|
||||
Charging.args = {
|
||||
...baseState,
|
||||
enabled: true,
|
||||
charging: true,
|
||||
};
|
||||
|
||||
export const UnknownVehicle = Template.bind({});
|
||||
UnknownVehicle.args = {
|
||||
...baseState,
|
||||
enabled: true,
|
||||
charging: true,
|
||||
vehicleName: "",
|
||||
socBasedCharging: false,
|
||||
vehicle: { ...baseState.vehicle, capacity: null },
|
||||
mode: "pv",
|
||||
};
|
||||
|
||||
export const OfflineVehicle = Template.bind({});
|
||||
OfflineVehicle.args = {
|
||||
...baseState,
|
||||
enabled: true,
|
||||
charging: true,
|
||||
socBasedCharging: false,
|
||||
vehicle: {
|
||||
...baseState.vehicle,
|
||||
title: "Opel Corsa-e",
|
||||
capacity: 72,
|
||||
features: ["Offline"],
|
||||
},
|
||||
mode: "pv",
|
||||
};
|
||||
|
||||
export const OfflineVehicleWithTarget = Template.bind({});
|
||||
OfflineVehicleWithTarget.args = {
|
||||
...baseState,
|
||||
enabled: true,
|
||||
charging: true,
|
||||
socBasedCharging: false,
|
||||
vehicle: {
|
||||
...baseState.vehicle,
|
||||
title: "Opel Corsa-e",
|
||||
capacity: 72,
|
||||
features: ["Offline"],
|
||||
},
|
||||
targetEnergy: 30,
|
||||
mode: "pv",
|
||||
};
|
||||
|
||||
export const VehicleLimit = Template.bind({});
|
||||
VehicleLimit.args = {
|
||||
...baseState,
|
||||
enabled: true,
|
||||
charging: true,
|
||||
vehicleLimitSoc: 80,
|
||||
};
|
||||
|
||||
export const VehicleLimitReached = Template.bind({});
|
||||
VehicleLimitReached.args = {
|
||||
...baseState,
|
||||
enabled: true,
|
||||
vehicleLimitSoc: 80,
|
||||
vehicleSoc: 80,
|
||||
};
|
||||
|
||||
export const TargetChargePlanned = Template.bind({});
|
||||
TargetChargePlanned.args = {
|
||||
...baseState,
|
||||
targetTime: hoursFromNow(34),
|
||||
mode: "pv",
|
||||
};
|
||||
|
||||
export const TargetChargeActive = Template.bind({});
|
||||
TargetChargeActive.args = {
|
||||
...baseState,
|
||||
enabled: true,
|
||||
charging: true,
|
||||
targetTime: hoursFromNow(14),
|
||||
mode: "pv",
|
||||
};
|
||||
|
||||
export const SmartChargeCostLimitActive = Template.bind({});
|
||||
SmartChargeCostLimitActive.args = {
|
||||
...baseState,
|
||||
enabled: true,
|
||||
charging: true,
|
||||
smartCostLimit: 0.13,
|
||||
mode: "pv",
|
||||
};
|
||||
|
||||
export const PvEnableTimer = Template.bind({});
|
||||
PvEnableTimer.args = {
|
||||
...baseState,
|
||||
pvAction: "enable",
|
||||
pvRemainingInterpolated: 32,
|
||||
};
|
||||
|
||||
export const PvDisableTimer = Template.bind({});
|
||||
PvDisableTimer.args = {
|
||||
...baseState,
|
||||
enabled: true,
|
||||
charging: true,
|
||||
pvAction: "disable",
|
||||
pvRemainingInterpolated: 155,
|
||||
};
|
||||
|
||||
export const VehicleSwitch = Template.bind({});
|
||||
VehicleSwitch.args = {
|
||||
...baseState,
|
||||
vehicleTitle: "Blauer e-Golf",
|
||||
vehicles: ["Blauer e-Golf", "Weißes Model 3"],
|
||||
};
|
||||
|
|
@ -1,119 +0,0 @@
|
|||
<script setup>
|
||||
import { reactive } from "vue";
|
||||
import Vehicle from "./Vehicle.vue";
|
||||
|
||||
const state = reactive({
|
||||
vehicle: { title: "Mein Auto", icon: "car", capacity: 72, features: [] },
|
||||
enabled: false,
|
||||
connected: true,
|
||||
vehicleName: "meinauto",
|
||||
vehicleSoc: 42.742,
|
||||
vehicleRange: 231,
|
||||
limitSoc: 90,
|
||||
chargedEnergy: 14123,
|
||||
socBasedCharging: true,
|
||||
id: 0,
|
||||
});
|
||||
|
||||
const hoursFromNow = function (hours) {
|
||||
const now = new Date();
|
||||
now.setHours(now.getHours() + hours);
|
||||
return now.toISOString();
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Story>
|
||||
<Variant title="disconnected">
|
||||
<Vehicle v-bind="state" :connected="false" />
|
||||
</Variant>
|
||||
<Variant title="connected">
|
||||
<Vehicle v-bind="state" />
|
||||
</Variant>
|
||||
<Variant title="ready">
|
||||
<Vehicle v-bind="state" enabled />
|
||||
</Variant>
|
||||
<Variant title="charging">
|
||||
<Vehicle v-bind="state" enabled charging />
|
||||
</Variant>
|
||||
<Variant title="charging">
|
||||
<Vehicle v-bind="state" enabled charging />
|
||||
</Variant>
|
||||
<Variant title="unknown vehicle">
|
||||
<Vehicle
|
||||
v-bind="state"
|
||||
enabled
|
||||
charging
|
||||
vehicleName=""
|
||||
:socBasedCharging="false"
|
||||
:vehicle="{ ...state.vehicle, capacity: null }"
|
||||
mode="pv"
|
||||
/>
|
||||
</Variant>
|
||||
<Variant title="offline vehicle">
|
||||
<Vehicle
|
||||
v-bind="state"
|
||||
enabled
|
||||
charging
|
||||
:socBasedCharging="false"
|
||||
:vehicle="{
|
||||
...state.vehicle,
|
||||
title: 'Opel Corsa-e',
|
||||
capacity: 72,
|
||||
features: ['Offline'],
|
||||
}"
|
||||
mode="pv"
|
||||
/>
|
||||
</Variant>
|
||||
<Variant title="offline vehicle with target">
|
||||
<Vehicle
|
||||
v-bind="state"
|
||||
enabled
|
||||
charging
|
||||
:socBasedCharging="false"
|
||||
:vehicle="{
|
||||
...state.vehicle,
|
||||
title: 'Opel Corsa-e',
|
||||
capacity: 72,
|
||||
features: ['Offline'],
|
||||
}"
|
||||
:targetEnergy="30"
|
||||
mode="pv"
|
||||
/>
|
||||
</Variant>
|
||||
<Variant title="vehicle limit">
|
||||
<Vehicle v-bind="state" enabled charging :vehicleLimitSoc="80" />
|
||||
</Variant>
|
||||
<Variant title="vehicle limit reached">
|
||||
<Vehicle v-bind="state" enabled :vehicleLimitSoc="80" :vehicleSoc="80" />
|
||||
</Variant>
|
||||
<Variant title="target charge planned">
|
||||
<Vehicle v-bind="state" :targetTime="hoursFromNow(34)" mode="pv" />
|
||||
</Variant>
|
||||
<Variant title="target charge active">
|
||||
<Vehicle v-bind="state" enabled charging :targetTime="hoursFromNow(14)" mode="pv" />
|
||||
</Variant>
|
||||
<Variant title="smart charge cost limit active">
|
||||
<Vehicle v-bind="state" enabled charging :smartCostLimit="0.13" mode="pv" />
|
||||
</Variant>
|
||||
<Variant title="pv enable timer">
|
||||
<Vehicle v-bind="state" pvAction="enable" :pvRemainingInterpolated="32" />
|
||||
</Variant>
|
||||
<Variant title="pv disable timer">
|
||||
<Vehicle
|
||||
v-bind="state"
|
||||
enabled
|
||||
charging
|
||||
pvAction="disable"
|
||||
:pvRemainingInterpolated="155"
|
||||
/>
|
||||
</Variant>
|
||||
<Variant title="vehicle switch">
|
||||
<Vehicle
|
||||
v-bind="state"
|
||||
vehicleTitle="Blauer e-Golf"
|
||||
:vehicles="['Blauer e-Golf', 'Weißes Model 3']"
|
||||
/>
|
||||
</Variant>
|
||||
</Story>
|
||||
</template>
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
import { defineConfig } from "histoire";
|
||||
import { HstVue } from "@histoire/plugin-vue";
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [HstVue()],
|
||||
setupFile: "./histoire.setup.js",
|
||||
viteNodeInlineDeps: [/!axios/, /shopicons/],
|
||||
routerMode: "hash",
|
||||
backgroundPresets: [
|
||||
{
|
||||
label: "default",
|
||||
color: "var(--evcc-background)",
|
||||
contrastColor: "var(--evcc-default-text)",
|
||||
},
|
||||
{
|
||||
label: "box",
|
||||
color: "var(--evcc-box)",
|
||||
contrastColor: "var(--evcc-default-text)",
|
||||
},
|
||||
],
|
||||
});
|
||||
3698
package-lock.json
generated
3698
package-lock.json
generated
File diff suppressed because it is too large
Load diff
26
package.json
26
package.json
|
|
@ -7,9 +7,9 @@
|
|||
"test": "TZ=Europe/Berlin vitest",
|
||||
"lint": "prettier assets './**/*.{yml,yaml,sh}' --write && eslint assets/**/*.js assets/**/*.vue --fix --max-warnings=0",
|
||||
"dev": "vite",
|
||||
"histoire": "histoire dev",
|
||||
"playwright": "playwright test --ui",
|
||||
"simulator": "vite tests/simulator"
|
||||
"simulator": "vite tests/simulator",
|
||||
"storybook": "storybook dev -p 6006"
|
||||
},
|
||||
"type": "module",
|
||||
"main": "index.js",
|
||||
|
|
@ -18,7 +18,6 @@
|
|||
"@eslint/js": "^9.13.0",
|
||||
"@guolao/vue-monaco-editor": "^1.5.1",
|
||||
"@h2d2/shopicons": "^1.2.0",
|
||||
"@histoire/plugin-vue": "^1.0.0-alpha.1",
|
||||
"@playwright/test": "^1.46.1",
|
||||
"@popperjs/core": "^2.11.5",
|
||||
"@unhead/vue": "^1.8.9",
|
||||
|
|
@ -47,7 +46,6 @@
|
|||
"eslint-plugin-vue": "^9.3.0",
|
||||
"globals": "^16.0.0",
|
||||
"happy-dom": "^17.4.4",
|
||||
"histoire": "^1.0.0-alpha.1",
|
||||
"markty-toml": "^0.1.1",
|
||||
"prettier": "^3.0.0",
|
||||
"prettier-plugin-sh": "^0.14.0",
|
||||
|
|
@ -72,5 +70,21 @@
|
|||
"node": ">=22.0.0"
|
||||
},
|
||||
"license": "MIT",
|
||||
"repository": "github:evcc-io/evcc"
|
||||
}
|
||||
"repository": "github:evcc-io/evcc",
|
||||
"eslintConfig": {
|
||||
"extends": [
|
||||
"plugin:storybook/recommended"
|
||||
]
|
||||
},
|
||||
"devDependencies": {
|
||||
"@chromatic-com/storybook": "^3.2.5",
|
||||
"@storybook/addon-essentials": "^8.6.4",
|
||||
"@storybook/addon-interactions": "^8.6.4",
|
||||
"@storybook/blocks": "^8.6.4",
|
||||
"@storybook/test": "^8.6.4",
|
||||
"@storybook/vue3": "^8.6.4",
|
||||
"@storybook/vue3-vite": "^8.6.4",
|
||||
"eslint-plugin-storybook": "^0.11.4",
|
||||
"storybook": "^8.6.4"
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue