Merge pull request 'refactor: merge DashboardView and GridStackView into a single dashboard (#19)' (#31) from ai/issue-19-merge-dashboard into main
Reviewed-on: #31
This commit was merged in pull request #31.
This commit is contained in:
@@ -39,7 +39,7 @@ Since `apex-chart.ts` renders into light DOM (`createRenderRoot()` returns `this
|
|||||||
|
|
||||||
**`GridStackLayout`/`GridStackItem`** (`components/`) wrap [gridstack.js](https://gridstack.js.org) 13.1.0 for draggable/resizable grids. Unlike `ApexChart`, the bridge (`frontend/components/grid-stack.ts`) is a plain `HTMLElement`, not a Lit component — its children are server-rendered `GridStackItem`s living in light DOM, and a Lit render root would fight gridstack for ownership of them. A `MutationObserver` calls `makeWidget`/`removeWidget` as Flow adds/removes children, so there's no explicit add/remove protocol to the client. Every `GridStackItem` needs a stable `gs-id` (auto-generated if not given) — `GridStackLayout.setStorageKey(...)` persists drag/resize state to browser `localStorage` keyed on it and restores by matching ids, so items lose their saved position if their id changes between reloads. Item styling (`.grid-stack-item-content`) extends the `--dialect-*` alias layer in `styles.css`, same as `.dialect-card`.
|
**`GridStackLayout`/`GridStackItem`** (`components/`) wrap [gridstack.js](https://gridstack.js.org) 13.1.0 for draggable/resizable grids. Unlike `ApexChart`, the bridge (`frontend/components/grid-stack.ts`) is a plain `HTMLElement`, not a Lit component — its children are server-rendered `GridStackItem`s living in light DOM, and a Lit render root would fight gridstack for ownership of them. A `MutationObserver` calls `makeWidget`/`removeWidget` as Flow adds/removes children, so there's no explicit add/remove protocol to the client. Every `GridStackItem` needs a stable `gs-id` (auto-generated if not given) — `GridStackLayout.setStorageKey(...)` persists drag/resize state to browser `localStorage` keyed on it and restores by matching ids, so items lose their saved position if their id changes between reloads. Item styling (`.grid-stack-item-content`) extends the `--dialect-*` alias layer in `styles.css`, same as `.dialect-card`.
|
||||||
|
|
||||||
**Views** (`views/`): `MainLayout` (`@Layout`, applies to all routes) is the `AppLayout` shell — navbar + `SideNav` drawer, with one `SideNavItem` per route. Routes: `DashboardView` (`@Route("")`) wraps each chart in a `Card` (`components/Card.java`); `FormView` (`@Route("formular")`) demonstrates form controls bound via `Binder`; `TableView` (`@Route("tabelle")`) demonstrates a `Grid` over dummy data with a live text filter (`GridListDataView.addFilter`, `TextField` in `ValueChangeMode.EAGER`). New views should reuse `Card` to wrap their content rather than adding components directly, and get a matching `SideNavItem` in `MainLayout`.
|
**Views** (`views/`): `MainLayout` (`@Layout`, applies to all routes) is the `AppLayout` shell — navbar + `SideNav` drawer, with one `SideNavItem` per route. Routes: `DashboardView` (`@Route("")`) puts each chart in a `Card` (`components/Card.java`) inside a `GridStackItem` of a `GridStackLayout`, so widgets are draggable/resizable and the layout is persisted to `localStorage`; charts use `width: 100%` / `height: 100%` to fill their widget rather than a fixed pixel height; `FormView` (`@Route("formular")`) demonstrates form controls bound via `Binder`; `TableView` (`@Route("tabelle")`) demonstrates a `Grid` over dummy data with a live text filter (`GridListDataView.addFilter`, `TextField` in `ValueChangeMode.EAGER`). New views should reuse `Card` to wrap their content rather than adding components directly, and get a matching `SideNavItem` in `MainLayout`.
|
||||||
|
|
||||||
**Styling**: `src/main/resources/META-INF/resources/styles.css` is the one project-level stylesheet (loaded via `@StyleSheet("styles.css")` in `Application.java`). It defines `--dialect-*` design tokens (Dialect design system: primary orange `#E86C00`, cool-gray background, card radius/shadow) and aliases them onto Aura's own CSS custom properties (`--aura-accent-color-*`, `--aura-background-color-*`, `--aura-orange`, `--aura-yellow`) rather than fighting the theme. Aura tokens use OKLCH + relative-color syntax and accept plain hex overrides. When restyling, prefer extending this alias layer over hardcoding new colors in components.
|
**Styling**: `src/main/resources/META-INF/resources/styles.css` is the one project-level stylesheet (loaded via `@StyleSheet("styles.css")` in `Application.java`). It defines `--dialect-*` design tokens (Dialect design system: primary orange `#E86C00`, cool-gray background, card radius/shadow) and aliases them onto Aura's own CSS custom properties (`--aura-accent-color-*`, `--aura-background-color-*`, `--aura-orange`, `--aura-yellow`) rather than fighting the theme. Aura tokens use OKLCH + relative-color syntax and accept plain hex overrides. When restyling, prefer extending this alias layer over hardcoding new colors in components.
|
||||||
|
|
||||||
|
|||||||
@@ -1,63 +1,135 @@
|
|||||||
package com.example.views;
|
package com.example.views;
|
||||||
|
|
||||||
|
import com.example.components.ApexChart;
|
||||||
import com.example.components.BarChart;
|
import com.example.components.BarChart;
|
||||||
import com.example.components.Card;
|
import com.example.components.Card;
|
||||||
|
import com.example.components.Fa;
|
||||||
|
import com.example.components.GridStackItem;
|
||||||
|
import com.example.components.GridStackLayout;
|
||||||
import com.example.components.LineChart;
|
import com.example.components.LineChart;
|
||||||
import com.example.components.PieChart;
|
import com.example.components.PieChart;
|
||||||
|
import com.vaadin.flow.component.Component;
|
||||||
|
import com.vaadin.flow.component.button.Button;
|
||||||
|
import com.vaadin.flow.component.button.ButtonVariant;
|
||||||
|
import com.vaadin.flow.component.html.Paragraph;
|
||||||
|
import com.vaadin.flow.component.html.Span;
|
||||||
import com.vaadin.flow.component.notification.Notification;
|
import com.vaadin.flow.component.notification.Notification;
|
||||||
|
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
|
||||||
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
|
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
|
||||||
import com.vaadin.flow.router.HasDynamicTitle;
|
import com.vaadin.flow.router.HasDynamicTitle;
|
||||||
import com.vaadin.flow.router.Route;
|
import com.vaadin.flow.router.Route;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The dashboard: a {@link GridStackLayout} of draggable/resizable cards whose
|
||||||
|
* layout is persisted per browser, plus controls to add widgets and reset the
|
||||||
|
* layout at runtime. Widgets are removed by their own close button (see
|
||||||
|
* {@link GridStackItem#setClosable(boolean)}), not from the toolbar.
|
||||||
|
*/
|
||||||
@Route("")
|
@Route("")
|
||||||
public class DashboardView extends VerticalLayout implements HasDynamicTitle {
|
public class DashboardView extends VerticalLayout implements HasDynamicTitle {
|
||||||
|
|
||||||
|
private static final String STORAGE_KEY = "dashboard";
|
||||||
|
|
||||||
|
private final GridStackLayout grid = new GridStackLayout();
|
||||||
|
private final Span status = new Span();
|
||||||
|
private int extraWidgetCount;
|
||||||
|
|
||||||
public DashboardView() {
|
public DashboardView() {
|
||||||
addClassName("dialect-content");
|
addClassName("dialect-content");
|
||||||
|
|
||||||
List<String> months = List.of(
|
grid.setWidthFull();
|
||||||
|
grid.setStorageKey(STORAGE_KEY);
|
||||||
|
grid.addLayoutChangeListener(e -> status.setText(
|
||||||
|
getTranslation("gridstack.status", e.getPositions().size())));
|
||||||
|
|
||||||
|
grid.add(
|
||||||
|
new GridStackItem("revenue-trend", 0, 0, 6, 3,
|
||||||
|
new Card(getTranslation("card.revenueTrend"), lineChart())),
|
||||||
|
new GridStackItem("revenue-month", 6, 0, 6, 3,
|
||||||
|
new Card(getTranslation("card.revenueByMonth"), barChart())),
|
||||||
|
new GridStackItem("revenue-region", 0, 3, 5, 3,
|
||||||
|
new Card(getTranslation("card.revenueByRegion"), pieChart())),
|
||||||
|
new GridStackItem("hint", 5, 3, 7, 3,
|
||||||
|
new Card(getTranslation("card.gridstackHint"),
|
||||||
|
new Paragraph(getTranslation("gridstack.hint")))));
|
||||||
|
|
||||||
|
status.setText(getTranslation("gridstack.statusInitial"));
|
||||||
|
status.addClassName("dialect-muted");
|
||||||
|
|
||||||
|
add(toolbar(), grid);
|
||||||
|
}
|
||||||
|
|
||||||
|
private HorizontalLayout toolbar() {
|
||||||
|
Button add = new Button(getTranslation("gridstack.addWidget"), Fa.ADD.create(),
|
||||||
|
e -> addWidget());
|
||||||
|
add.addThemeVariants(ButtonVariant.LUMO_PRIMARY);
|
||||||
|
|
||||||
|
Button reset = new Button(getTranslation("gridstack.reset"), Fa.RESET.create(),
|
||||||
|
e -> grid.resetLayout());
|
||||||
|
|
||||||
|
HorizontalLayout toolbar = new HorizontalLayout(add, reset, status);
|
||||||
|
toolbar.setPadding(false);
|
||||||
|
toolbar.setWidthFull();
|
||||||
|
toolbar.setAlignItems(Alignment.CENTER);
|
||||||
|
return toolbar;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addWidget() {
|
||||||
|
// The id must stay stable across reloads for the saved layout to match
|
||||||
|
// it again, so it is derived from a counter rather than a random UUID.
|
||||||
|
// The counter only ever grows: closing a widget must not hand its id to
|
||||||
|
// the next one, or the new widget would inherit the closed one's saved
|
||||||
|
// position.
|
||||||
|
extraWidgetCount++;
|
||||||
|
String title = getTranslation("gridstack.widget", extraWidgetCount);
|
||||||
|
grid.add(new GridStackItem("extra-" + extraWidgetCount, 0, 0, 4, 2,
|
||||||
|
new Card(title, new Paragraph(getTranslation("gridstack.widgetText")))));
|
||||||
|
}
|
||||||
|
|
||||||
|
private Component lineChart() {
|
||||||
|
LineChart chart = new LineChart();
|
||||||
|
chart.setData(getTranslation("chart.revenueSeries"),
|
||||||
|
List.of(30.0, 40.0, 35.0, 50.0, 49.0, 60.0), months());
|
||||||
|
chart.addPointClickListener(e -> Notification.show(getTranslation(
|
||||||
|
"chart.pointClick", e.getSeriesIndex(), e.getDataPointIndex())));
|
||||||
|
return sizeFull(chart);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Component barChart() {
|
||||||
|
BarChart chart = new BarChart();
|
||||||
|
chart.setData(getTranslation("chart.revenueSeries"),
|
||||||
|
List.of(30.0, 40.0, 35.0, 50.0, 49.0, 60.0), months());
|
||||||
|
chart.addPointClickListener(e -> Notification.show(getTranslation(
|
||||||
|
"chart.pointClick", e.getSeriesIndex(), e.getDataPointIndex())));
|
||||||
|
return sizeFull(chart);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Component pieChart() {
|
||||||
|
PieChart chart = new PieChart();
|
||||||
|
chart.setData(List.of(30.0, 40.0, 35.0, 50.0),
|
||||||
|
List.of(getTranslation("region.north"), getTranslation("region.south"),
|
||||||
|
getTranslation("region.east"), getTranslation("region.west")));
|
||||||
|
chart.addPointClickListener(e -> Notification.show(
|
||||||
|
getTranslation("chart.sliceClick", e.getDataPointIndex())));
|
||||||
|
return sizeFull(chart);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Charts fill their grid item instead of using a fixed pixel height, so
|
||||||
|
* resizing a widget resizes the chart (grid-stack.ts fires a window
|
||||||
|
* resize on resizestop, which ApexCharts reflows on). */
|
||||||
|
private Component sizeFull(ApexChart chart) {
|
||||||
|
chart.setWidthFull();
|
||||||
|
chart.setHeight("100%");
|
||||||
|
return chart;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<String> months() {
|
||||||
|
return List.of(
|
||||||
getTranslation("month.jan"), getTranslation("month.feb"),
|
getTranslation("month.jan"), getTranslation("month.feb"),
|
||||||
getTranslation("month.mar"), getTranslation("month.apr"),
|
getTranslation("month.mar"), getTranslation("month.apr"),
|
||||||
getTranslation("month.may"), getTranslation("month.jun"));
|
getTranslation("month.may"), getTranslation("month.jun"));
|
||||||
|
|
||||||
LineChart lineChart = new LineChart();
|
|
||||||
lineChart.setWidthFull();
|
|
||||||
lineChart.setHeight("400px");
|
|
||||||
lineChart.setData(getTranslation("chart.revenueSeries"),
|
|
||||||
List.of(30.0, 40.0, 35.0, 50.0, 49.0, 60.0),
|
|
||||||
months);
|
|
||||||
lineChart.addPointClickListener(e -> {
|
|
||||||
Notification.show(getTranslation("chart.pointClick",
|
|
||||||
e.getSeriesIndex(), e.getDataPointIndex()));
|
|
||||||
});
|
|
||||||
|
|
||||||
BarChart barChart = new BarChart();
|
|
||||||
barChart.setWidthFull();
|
|
||||||
barChart.setHeight("400px");
|
|
||||||
barChart.setData(getTranslation("chart.revenueSeries"),
|
|
||||||
List.of(30.0, 40.0, 35.0, 50.0, 49.0, 60.0),
|
|
||||||
months);
|
|
||||||
barChart.addPointClickListener(e -> {
|
|
||||||
Notification.show(getTranslation("chart.pointClick",
|
|
||||||
e.getSeriesIndex(), e.getDataPointIndex()));
|
|
||||||
});
|
|
||||||
|
|
||||||
PieChart pieChart = new PieChart();
|
|
||||||
pieChart.setWidthFull();
|
|
||||||
pieChart.setHeight("400px");
|
|
||||||
pieChart.setData(
|
|
||||||
List.of(30.0, 40.0, 35.0, 50.0),
|
|
||||||
List.of(getTranslation("region.north"), getTranslation("region.south"),
|
|
||||||
getTranslation("region.east"), getTranslation("region.west")));
|
|
||||||
pieChart.addPointClickListener(e -> {
|
|
||||||
Notification.show(getTranslation("chart.sliceClick", e.getDataPointIndex()));
|
|
||||||
});
|
|
||||||
|
|
||||||
add(new Card(getTranslation("card.revenueTrend"), lineChart),
|
|
||||||
new Card(getTranslation("card.revenueByMonth"), barChart),
|
|
||||||
new Card(getTranslation("card.revenueByRegion"), pieChart));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -1,132 +0,0 @@
|
|||||||
package com.example.views;
|
|
||||||
|
|
||||||
import com.example.components.BarChart;
|
|
||||||
import com.example.components.Card;
|
|
||||||
import com.example.components.Fa;
|
|
||||||
import com.example.components.GridStackItem;
|
|
||||||
import com.example.components.GridStackLayout;
|
|
||||||
import com.example.components.LineChart;
|
|
||||||
import com.example.components.PieChart;
|
|
||||||
import com.example.components.ApexChart;
|
|
||||||
import com.vaadin.flow.component.Component;
|
|
||||||
import com.vaadin.flow.component.button.Button;
|
|
||||||
import com.vaadin.flow.component.button.ButtonVariant;
|
|
||||||
import com.vaadin.flow.component.html.Paragraph;
|
|
||||||
import com.vaadin.flow.component.html.Span;
|
|
||||||
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
|
|
||||||
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
|
|
||||||
import com.vaadin.flow.router.HasDynamicTitle;
|
|
||||||
import com.vaadin.flow.router.Route;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Showcase for {@link GridStackLayout}: a dashboard of draggable/resizable
|
|
||||||
* cards whose layout is persisted per browser, plus controls to add widgets and
|
|
||||||
* reset the layout at runtime. Widgets are removed by their own close button
|
|
||||||
* (see {@link GridStackItem#setClosable(boolean)}), not from the toolbar.
|
|
||||||
*/
|
|
||||||
@Route("gridstack")
|
|
||||||
public class GridStackView extends VerticalLayout implements HasDynamicTitle {
|
|
||||||
|
|
||||||
private static final String STORAGE_KEY = "gridstack-demo";
|
|
||||||
|
|
||||||
private final GridStackLayout grid = new GridStackLayout();
|
|
||||||
private final Span status = new Span();
|
|
||||||
private int extraWidgetCount;
|
|
||||||
|
|
||||||
public GridStackView() {
|
|
||||||
addClassName("dialect-content");
|
|
||||||
|
|
||||||
grid.setWidthFull();
|
|
||||||
grid.setStorageKey(STORAGE_KEY);
|
|
||||||
grid.addLayoutChangeListener(e -> status.setText(
|
|
||||||
getTranslation("gridstack.status", e.getPositions().size())));
|
|
||||||
|
|
||||||
grid.add(
|
|
||||||
new GridStackItem("revenue-trend", 0, 0, 6, 3,
|
|
||||||
new Card(getTranslation("card.revenueTrend"), lineChart())),
|
|
||||||
new GridStackItem("revenue-month", 6, 0, 6, 3,
|
|
||||||
new Card(getTranslation("card.revenueByMonth"), barChart())),
|
|
||||||
new GridStackItem("revenue-region", 0, 3, 5, 3,
|
|
||||||
new Card(getTranslation("card.revenueByRegion"), pieChart())),
|
|
||||||
new GridStackItem("hint", 5, 3, 7, 3,
|
|
||||||
new Card(getTranslation("card.gridstackHint"),
|
|
||||||
new Paragraph(getTranslation("gridstack.hint")))));
|
|
||||||
|
|
||||||
status.setText(getTranslation("gridstack.statusInitial"));
|
|
||||||
status.addClassName("dialect-muted");
|
|
||||||
|
|
||||||
add(toolbar(), grid);
|
|
||||||
}
|
|
||||||
|
|
||||||
private HorizontalLayout toolbar() {
|
|
||||||
Button add = new Button(getTranslation("gridstack.addWidget"), Fa.ADD.create(),
|
|
||||||
e -> addWidget());
|
|
||||||
add.addThemeVariants(ButtonVariant.LUMO_PRIMARY);
|
|
||||||
|
|
||||||
Button reset = new Button(getTranslation("gridstack.reset"), Fa.RESET.create(),
|
|
||||||
e -> grid.resetLayout());
|
|
||||||
|
|
||||||
HorizontalLayout toolbar = new HorizontalLayout(add, reset, status);
|
|
||||||
toolbar.setPadding(false);
|
|
||||||
toolbar.setWidthFull();
|
|
||||||
toolbar.setAlignItems(Alignment.CENTER);
|
|
||||||
return toolbar;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void addWidget() {
|
|
||||||
// The id must stay stable across reloads for the saved layout to match
|
|
||||||
// it again, so it is derived from a counter rather than a random UUID.
|
|
||||||
// The counter only ever grows: closing a widget must not hand its id to
|
|
||||||
// the next one, or the new widget would inherit the closed one's saved
|
|
||||||
// position.
|
|
||||||
extraWidgetCount++;
|
|
||||||
String title = getTranslation("gridstack.widget", extraWidgetCount);
|
|
||||||
grid.add(new GridStackItem("extra-" + extraWidgetCount, 0, 0, 4, 2,
|
|
||||||
new Card(title, new Paragraph(getTranslation("gridstack.widgetText")))));
|
|
||||||
}
|
|
||||||
|
|
||||||
private Component lineChart() {
|
|
||||||
LineChart chart = new LineChart();
|
|
||||||
chart.setData(getTranslation("chart.revenueSeries"),
|
|
||||||
List.of(30.0, 40.0, 35.0, 50.0, 49.0, 60.0), months());
|
|
||||||
return sizeFull(chart);
|
|
||||||
}
|
|
||||||
|
|
||||||
private Component barChart() {
|
|
||||||
BarChart chart = new BarChart();
|
|
||||||
chart.setData(getTranslation("chart.revenueSeries"),
|
|
||||||
List.of(30.0, 40.0, 35.0, 50.0, 49.0, 60.0), months());
|
|
||||||
return sizeFull(chart);
|
|
||||||
}
|
|
||||||
|
|
||||||
private Component pieChart() {
|
|
||||||
PieChart chart = new PieChart();
|
|
||||||
chart.setData(List.of(30.0, 40.0, 35.0, 50.0),
|
|
||||||
List.of(getTranslation("region.north"), getTranslation("region.south"),
|
|
||||||
getTranslation("region.east"), getTranslation("region.west")));
|
|
||||||
return sizeFull(chart);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Charts fill their grid item instead of using a fixed pixel height, so
|
|
||||||
* resizing a widget resizes the chart (grid-stack.ts fires a window
|
|
||||||
* resize on resizestop, which ApexCharts reflows on). */
|
|
||||||
private Component sizeFull(ApexChart chart) {
|
|
||||||
chart.setWidthFull();
|
|
||||||
chart.setHeight("100%");
|
|
||||||
return chart;
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<String> months() {
|
|
||||||
return List.of(
|
|
||||||
getTranslation("month.jan"), getTranslation("month.feb"),
|
|
||||||
getTranslation("month.mar"), getTranslation("month.apr"),
|
|
||||||
getTranslation("month.may"), getTranslation("month.jun"));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getPageTitle() {
|
|
||||||
return getTranslation("page.gridstack");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -49,8 +49,6 @@ public class MainLayout extends AppLayout {
|
|||||||
Fa.FORM.create()));
|
Fa.FORM.create()));
|
||||||
nav.addItem(new SideNavItem(getTranslation("nav.table"), TableView.class,
|
nav.addItem(new SideNavItem(getTranslation("nav.table"), TableView.class,
|
||||||
Fa.TABLE.create()));
|
Fa.TABLE.create()));
|
||||||
nav.addItem(new SideNavItem(getTranslation("nav.gridstack"), GridStackView.class,
|
|
||||||
Fa.GRID.create()));
|
|
||||||
|
|
||||||
addToDrawer(nav);
|
addToDrawer(nav);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,12 +10,10 @@ lang.es=Español
|
|||||||
nav.dashboard=Dashboard
|
nav.dashboard=Dashboard
|
||||||
nav.form=Formular
|
nav.form=Formular
|
||||||
nav.table=Tabelle
|
nav.table=Tabelle
|
||||||
nav.gridstack=Raster
|
|
||||||
|
|
||||||
page.dashboard=Dashboard
|
page.dashboard=Dashboard
|
||||||
page.form=Formular
|
page.form=Formular
|
||||||
page.table=Tabelle
|
page.table=Tabelle
|
||||||
page.gridstack=Raster-Layout
|
|
||||||
|
|
||||||
card.revenueTrend=Umsatz-Entwicklung
|
card.revenueTrend=Umsatz-Entwicklung
|
||||||
card.revenueByMonth=Umsatz nach Monat
|
card.revenueByMonth=Umsatz nach Monat
|
||||||
|
|||||||
@@ -10,12 +10,10 @@ lang.es=Español
|
|||||||
nav.dashboard=Dashboard
|
nav.dashboard=Dashboard
|
||||||
nav.form=Form
|
nav.form=Form
|
||||||
nav.table=Table
|
nav.table=Table
|
||||||
nav.gridstack=Grid
|
|
||||||
|
|
||||||
page.dashboard=Dashboard
|
page.dashboard=Dashboard
|
||||||
page.form=Form
|
page.form=Form
|
||||||
page.table=Table
|
page.table=Table
|
||||||
page.gridstack=Grid layout
|
|
||||||
|
|
||||||
card.revenueTrend=Revenue Trend
|
card.revenueTrend=Revenue Trend
|
||||||
card.revenueByMonth=Revenue by Month
|
card.revenueByMonth=Revenue by Month
|
||||||
|
|||||||
@@ -10,12 +10,10 @@ lang.es=Español
|
|||||||
nav.dashboard=Panel
|
nav.dashboard=Panel
|
||||||
nav.form=Formulario
|
nav.form=Formulario
|
||||||
nav.table=Tabla
|
nav.table=Tabla
|
||||||
nav.gridstack=Cuadrícula
|
|
||||||
|
|
||||||
page.dashboard=Panel
|
page.dashboard=Panel
|
||||||
page.form=Formulario
|
page.form=Formulario
|
||||||
page.table=Tabla
|
page.table=Tabla
|
||||||
page.gridstack=Diseño de cuadrícula
|
|
||||||
|
|
||||||
card.revenueTrend=Evolución de ingresos
|
card.revenueTrend=Evolución de ingresos
|
||||||
card.revenueByMonth=Ingresos por mes
|
card.revenueByMonth=Ingresos por mes
|
||||||
|
|||||||
+19
-7
@@ -1,8 +1,11 @@
|
|||||||
package com.example.views;
|
package com.example.views;
|
||||||
|
|
||||||
import com.example.Application;
|
import com.example.Application;
|
||||||
|
import com.example.components.BarChart;
|
||||||
import com.example.components.GridStackItem;
|
import com.example.components.GridStackItem;
|
||||||
import com.example.components.GridStackLayout;
|
import com.example.components.GridStackLayout;
|
||||||
|
import com.example.components.LineChart;
|
||||||
|
import com.example.components.PieChart;
|
||||||
import com.vaadin.browserless.SpringBrowserlessTest;
|
import com.vaadin.browserless.SpringBrowserlessTest;
|
||||||
import com.vaadin.browserless.ViewPackages;
|
import com.vaadin.browserless.ViewPackages;
|
||||||
import com.vaadin.browserless.internal.ElementUtilsKt;
|
import com.vaadin.browserless.internal.ElementUtilsKt;
|
||||||
@@ -21,12 +24,12 @@ import static org.junit.jupiter.api.Assertions.assertNotNull;
|
|||||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
|
||||||
@SpringBootTest(classes = Application.class)
|
@SpringBootTest(classes = Application.class)
|
||||||
@ViewPackages(classes = GridStackView.class)
|
@ViewPackages(classes = DashboardView.class)
|
||||||
class GridStackViewTest extends SpringBrowserlessTest {
|
class DashboardViewTest extends SpringBrowserlessTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void view_rendersGridWithDefaultWidgets() {
|
void view_rendersGridWithDefaultWidgets() {
|
||||||
navigate(GridStackView.class);
|
navigate(DashboardView.class);
|
||||||
|
|
||||||
GridStackLayout grid = $view(GridStackLayout.class).first();
|
GridStackLayout grid = $view(GridStackLayout.class).first();
|
||||||
assertNotNull(grid);
|
assertNotNull(grid);
|
||||||
@@ -36,9 +39,18 @@ class GridStackViewTest extends SpringBrowserlessTest {
|
|||||||
assertTrue(layout.stream().allMatch(p -> p.w() > 0 && p.h() > 0));
|
assertTrue(layout.stream().allMatch(p -> p.w() > 0 && p.h() > 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void view_rendersTheThreeCharts() {
|
||||||
|
navigate(DashboardView.class);
|
||||||
|
|
||||||
|
assertNotNull($view(LineChart.class).first());
|
||||||
|
assertNotNull($view(BarChart.class).first());
|
||||||
|
assertNotNull($view(PieChart.class).first());
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void addAndCloseWidget_changesItemCount() {
|
void addAndCloseWidget_changesItemCount() {
|
||||||
navigate(GridStackView.class);
|
navigate(DashboardView.class);
|
||||||
|
|
||||||
GridStackLayout grid = $view(GridStackLayout.class).first();
|
GridStackLayout grid = $view(GridStackLayout.class).first();
|
||||||
int initial = grid.getLayout().size();
|
int initial = grid.getLayout().size();
|
||||||
@@ -52,7 +64,7 @@ class GridStackViewTest extends SpringBrowserlessTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
void closeButton_removesTheClickedWidgetOnly() {
|
void closeButton_removesTheClickedWidgetOnly() {
|
||||||
navigate(GridStackView.class);
|
navigate(DashboardView.class);
|
||||||
|
|
||||||
GridStackLayout grid = $view(GridStackLayout.class).first();
|
GridStackLayout grid = $view(GridStackLayout.class).first();
|
||||||
GridStackItem first = $view(GridStackItem.class).first();
|
GridStackItem first = $view(GridStackItem.class).first();
|
||||||
@@ -68,7 +80,7 @@ class GridStackViewTest extends SpringBrowserlessTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
void closeListener_firesOnceItemIsDetached() {
|
void closeListener_firesOnceItemIsDetached() {
|
||||||
navigate(GridStackView.class);
|
navigate(DashboardView.class);
|
||||||
|
|
||||||
GridStackLayout grid = $view(GridStackLayout.class).first();
|
GridStackLayout grid = $view(GridStackLayout.class).first();
|
||||||
GridStackItem item = $view(GridStackItem.class).first();
|
GridStackItem item = $view(GridStackItem.class).first();
|
||||||
@@ -83,7 +95,7 @@ class GridStackViewTest extends SpringBrowserlessTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
void nonClosableItem_hasNoCloseButton() {
|
void nonClosableItem_hasNoCloseButton() {
|
||||||
navigate(GridStackView.class);
|
navigate(DashboardView.class);
|
||||||
|
|
||||||
GridStackItem item = $view(GridStackItem.class).first();
|
GridStackItem item = $view(GridStackItem.class).first();
|
||||||
assertTrue(item.isClosable(), "items are closable by default");
|
assertTrue(item.isClosable(), "items are closable by default");
|
||||||
Reference in New Issue
Block a user