Plan UI: always show settings (#26488)

This commit is contained in:
Michael Geers 2026-01-07 14:37:28 +01:00 • committed by GitHub
parent 91edce9d69
commit f37484985a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 33 additions and 8 deletions

View file

@ -1,7 +1,13 @@
<template>
<div class="strategy-wrapper" :class="{ open: show }">
<div class="strategy-content">
<div class="row">
<div v-if="disabled" class="row mb-4">
<div class="small text-muted">
<strong class="text-primary">{{ $t("general.note") }}</strong>
{{ $t("main.chargingPlan.strategyDisabledDescription") }}
</div>
</div>
<div v-else class="row">
<div class="col-12 col-sm-6 col-lg-3 offset-lg-3 mb-3">
<div class="row">
<label :for="formId('continuous')" class="col-form-label col-5 col-sm-12">
@ -68,6 +74,7 @@ export default defineComponent({
show: Boolean,
precondition: { type: Number, default: 0 },
continuous: { type: Boolean, default: false },
disabled: Boolean,
},
emits: ["update"],
data() {

View file

@ -44,7 +44,6 @@
<span v-else-if="alreadyReached">{{ $t("main.targetCharge.goalReached") }}</span>
<span v-else>{{ nextPlanTitle }}</span>
<button
v-if="showStrategy"
type="button"
class="btn btn-sm"
:class="strategyOpen ? 'btn-secondary' : 'evcc-gray'"
@ -57,8 +56,8 @@
</div>
</h5>
<ChargingPlanStrategy
v-if="showStrategy"
v-bind="chargingPlanStrategyProps"
:disabled="strategyDisabled"
:show="strategyOpen"
@update="updatePlanStrategy"
/>
@ -173,11 +172,12 @@ export default defineComponent({
nextPlanTitle(): string {
return `${this.$t("main.targetCharge.nextPlan")} #${this.nextPlanId}`;
},
showStrategy(): boolean {
// only show option if planner forecast has different values
strategyDisabled(): boolean {
// options only make sense if there are variable prices
// TODO: make this logic more robust (api fails, missing data)
const slots = this.forecast?.planner || [];
const values = new Set(slots.map(({ value }) => value));
return values.size > 1;
return values.size <= 1;
},
},
watch: {

View file

@ -760,6 +760,9 @@
"solar": "Solar"
}
},
"general": {
"note": "Hinweis:"
},
"header": {
"about": "Über",
"blog": "Blog",
@ -893,6 +896,7 @@
"repeating": "wiederholend",
"repeatingPlans": "Wiederholende Pläne",
"selectAll": "Alle wählen",
"strategyDisabledDescription": "Das Laden startet so spät wie möglich und wird rechtzeitig zur Abfahrt abgeschlossen. Mit dynamischen Netzpreisen oder einem CO₂-Tarif stehen hier weitere Optionen zur Verfügung.",
"strategySettings": "Strategie-Einstellungen",
"time": "Zeit",
"title": "Plan",

View file

@ -762,6 +762,9 @@
"solar": "Solar"
}
},
"general": {
"note": "Note:"
},
"header": {
"about": "About",
"blog": "Blog",
@ -895,6 +898,7 @@
"repeating": "repeating",
"repeatingPlans": "Repeating plans",
"selectAll": "Select all",
"strategyDisabledDescription": "Charging starts as late as possible to finish just in time for departure. With dynamic grid prices or CO₂ tariff, more options are available here.",
"strategySettings": "Strategy settings",
"time": "Time",
"title": "Plan",

View file

@ -12,6 +12,10 @@ const BINARY = "./evcc";
const IS_CI = !!process.env["GITHUB_ACTIONS"];
const LOG_ENABLED = !IS_CI;
console.log(
"REMINDER: Playwright tests run against the ./evcc binary. Rebuild with 'make ui build' after application changes."
);
// sometimes evcc startup fails due to infra issues in runner ususally fixed by retry. allowing some fails to avoid github annotations clutter
let allowedStartupFails = IS_CI ? 2 : 0;
@ -105,6 +109,7 @@ async function _start(config?: string, flags: string | string[] = []) {
const configArgs = config ? ["--config", config.includes("/") ? config : `tests/${config}`] : [];
const port = workerPort();
const ocpp = ocppPort();
log(`wait until port ${port} is available`);
// wait for port to be available
await waitOn({ resources: [`tcp:${port}`], reverse: true, log: LOG_ENABLED });

View file

@ -687,8 +687,13 @@ test.describe("plan strategy", async () => {
await lp1.getByTestId("charging-plan").getByRole("button", { name: "none" }).click();
const modal = page.getByTestId("charging-plan-modal");
await expect(modal.getByTestId("static-plan-active")).toBeVisible();
// Strategy toggle should not be visible when no dynamic tariff exists
await expect(modal.getByRole("button", { name: "Strategy settings" })).not.toBeVisible();
// Strategy toggle should be visible but expand to show informational note only
await expect(modal.getByRole("button", { name: "Strategy settings" })).toBeVisible();
await modal.getByRole("button", { name: "Strategy settings" }).click();
await expect(modal.getByLabel("Optimization")).not.toBeVisible();
await expect(modal.getByLabel("Late Charging")).not.toBeVisible();
await expect(modal).toContainText("just in time for departure");
});
test("visible and functional on mobile", async ({ page }) => {