Skip to content

Latest commit

 

History

History
159 lines (119 loc) · 9.83 KB

File metadata and controls

159 lines (119 loc) · 9.83 KB

Implementing a widget

The purpose of this module is to let modules add dashboard widgets while remaining installable without Hyvä Commerce: only this contract module — distributed under the OSL — is required as a dependency, not the dashboard runtime itself.

What is an admin dashboard widget?

The Hyvä Admin Dashboard is a configurable grid of widgets shown in the Magento admin. Each widget is a self-contained panel — typically a chart or a single key figure (e.g. a bar chart of order volume, a number tile for today's revenue) — that an admin user can add, resize, and position on the dashboard.

A widget has two responsibilities:

  • Produce data to display, via getDisplayData(). The shape of that data depends on the widget's display_type (bar chart, line chart, pie chart, number, or date interval).
  • Declare its configuration — the title, which properties an admin can configure per instance (getConfigurableProperties()), and how the data is presented (getDisplayProperties()).

Providing a template is optional — for many widget types the framework can supply all the required rendering logic.

A widget type is the PHP class that implements this behaviour. A widget instance is a single configured placement of that type on a dashboard (with its own saved settings). Your class receives a read-only WidgetInstanceInterface so it can react to the instance's configuration, and a WidgetContextInterface ($ctx) that exposes merged config, ACL helpers, and chart-type defaults.

This package (hyva-themes/commerce-module-admin-dashboard-api) ships only the contract — the interfaces and a reusable WidgetContext. The runtime that renders the dashboard lives in hyva-themes/commerce-module-admin-dashboard, but you only need to depend on the api package to build and compile a widget.

Implementing the widget class

Implement Hyva\AdminDashboardApi\Api\V1\WidgetTypeInterface (or one of the chart-type marker interfaces such as BarChartWidgetTypeInterface, LineChartWidgetTypeInterface, PieChartWidgetTypeInterface, NumberWidgetTypeInterface, or DateIntervalWidgetTypeInterface) directly. Implementing a chart-type interface tells the framework which display type the widget uses and wires the matching defaults into $ctx.

Every method receives a WidgetContextInterface as the first argument. Read shared defaults — title, configurable/display properties, trailing action, ACL — from $ctx:

use Hyva\AdminDashboardApi\Api\V1\ChartType\BarChartWidgetTypeInterface;
use Hyva\AdminDashboardApi\Api\V1\WidgetContextInterface;
use Hyva\AdminDashboardApi\Api\V1\WidgetInstanceInterface;
use Magento\Framework\Phrase;

class OrderVolume implements BarChartWidgetTypeInterface
{
    public function __construct(private OrderRepositoryInterface $orderRepo) {}

    public function getDisplayData(WidgetContextInterface $ctx, WidgetInstanceInterface $i): array
    {
        return [
            'series' => [['name' => 'Orders', 'data' => $this->fetchSeries()]],
            'xaxis'  => ['categories' => ['Mon','Tue','Wed','Thu','Fri','Sat','Sun']],
        ];
    }

    public function getTitle(WidgetContextInterface $ctx, ?WidgetInstanceInterface $i): Phrase      { return $ctx->getTitle(); }
    public function getConfigurableProperties(WidgetContextInterface $ctx): array                                { return $ctx->getConfigurableProperties(); }
    public function getDisplayProperties(WidgetContextInterface $ctx): array                                     { return $ctx->getDisplayProperties(); }
    public function getTrailingAction(WidgetContextInterface $ctx, ?WidgetInstanceInterface $i): array           { return $ctx->getTrailingAction(); }
    public function isAllowed(WidgetContextInterface $ctx, ?WidgetInstanceInterface $i): bool                    { return $ctx->isAllowed($i); }
    public function beforeSave(WidgetContextInterface $ctx, WidgetInstanceInterface $i): WidgetInstanceInterface { return $i; }
    public function afterSave(WidgetContextInterface $ctx, WidgetInstanceInterface $i): WidgetInstanceInterface  { return $i; }

    private function fetchSeries(): array { /* ... */ }
}

Reading and extending defaults via $ctx

$ctx provides the standard defaults for the widget's chart type, so most methods just return the corresponding $ctx value directly. When you want to add your own properties on top of the defaults, merge them:

use Hyva\AdminDashboardApi\Api\V1\ChartType\DateIntervalWidgetTypeInterface;
use Hyva\AdminDashboardApi\Api\V1\Service\WidgetDateIntervalHelperInterface;
use Hyva\AdminDashboardApi\Api\V1\WidgetContextInterface;
use Hyva\AdminDashboardApi\Api\V1\WidgetInstanceInterface;
use Magento\Framework\Phrase;

class DailyOrderVolume implements DateIntervalWidgetTypeInterface
{
    public function __construct(
        private WidgetDateIntervalHelperInterface $intervalHelper,
    ) {}

    public function getDisplayData(WidgetContextInterface $ctx, WidgetInstanceInterface $i): array
    {
        return [
            'intervals' => $this->intervalHelper->getIntervalDataWithTimestamps(),
            // ...
        ];
    }

    public function getDisplayProperties(WidgetContextInterface $ctx): array
    {
        return array_merge($ctx->getDisplayProperties(), [
            'highlight_today' => [
                'label' => __('Highlight today'),
                'input' => ['type' => 'toggle'],
            ],
        ]);
    }

    public function getConfigurableProperties(WidgetContextInterface $ctx): array
    {
        return array_merge($ctx->getConfigurableProperties(), [
            'store_ids' => [
                'label' => __('Store Views'),
                'input' => ['type' => 'scope', 'attributes' => ['multiple' => true, 'required' => true]],
            ],
        ]);
    }

    public function getTitle(WidgetContextInterface $ctx, ?WidgetInstanceInterface $i): Phrase      { return $ctx->getTitle(); }
    public function getTrailingAction(WidgetContextInterface $ctx, ?WidgetInstanceInterface $i): array           { return $ctx->getTrailingAction(); }
    public function isAllowed(WidgetContextInterface $ctx, ?WidgetInstanceInterface $i): bool                    { return $ctx->isAllowed($i); }
    public function beforeSave(WidgetContextInterface $ctx, WidgetInstanceInterface $i): WidgetInstanceInterface { return $i; }
    public function afterSave(WidgetContextInterface $ctx, WidgetInstanceInterface $i): WidgetInstanceInterface  { return $i; }
}

Because DailyOrderVolume implements DateIntervalWidgetTypeInterface, the framework wires the date-interval defaults provider into $ctx. $ctx->getDisplayProperties() therefore returns the standard default_interval selector that all date-interval widgets share, and the widget's own array_merge(...) adds highlight_today on top.

The general rule: read everything from $ctx, and only array_merge(...) when you need to add properties of your own. Methods that don't add anything (e.g. getTitle, getTrailingAction, isAllowed) just return $ctx->method(...) directly.

Registering the widget

Register the widget in etc/adminhtml/hyva_dashboard_widget.xml:

<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:noNamespaceSchemaLocation="urn:magento:module:Hyva_AdminDashboardApi:etc/adminhtml/hyva_dashboard_widget.xsd">
    <widget id="order_volume">
        <title>Order Volume</title>
        <class>Acme\OrderVolumeWidget\Model\Widget\OrderVolume</class>
        <display_type>bar_chart</display_type>
        <category>sales</category>
        <icon>chart-column</icon>
        <min_height>6</min_height>
        <min_width>2</min_width>
    </widget>
</config>

The widget's module composer.json only needs to require this api package (widgets are usually shipped as add-ons inside an existing module rather than as a dedicated module):

{
    "require": {
        "hyva-themes/commerce-module-admin-dashboard-api": "^1.0"
    }
}

bin/magento setup:di:compile works without hyva-themes/commerce-module-admin-dashboard installed.

Legacy API

Earlier versions of the dashboard used an inheritance-based contract instead of the composition-based WidgetContextInterface shown above. New widgets should use the composition API; this section exists only to help when reading or migrating older code.

In the legacy contract:

  • Widgets implemented Hyva\AdminDashboardFramework\Api\V1\WidgetType\WidgetTypeInterface.
  • Instead of receiving a WidgetContextInterface, widgets extended the AbstractWidgetType base class and inherited shared defaults through it.
  • Defaults were obtained by calling the parent, e.g. array_merge(parent::getDisplayProperties(), [...]) or array_merge(parent::getConfigurableProperties(), [...]), to pick up things like the default_interval selector shared by all date-interval widgets.

Because the legacy contract and AbstractWidgetType base class live in hyva-themes/commerce-module-admin-dashboard, widgets using the legacy API require that module to be installed. If the dependency is not present, setup:di:compile will break. This is the key reason to prefer the composition API: a widget built against it depends only on this contract package and compiles without the dashboard runtime.

When migrating to the composition API, the mapping is mechanical: every parent::method() call becomes $ctx->method(), and the abstract base class is replaced by implementing WidgetTypeInterface (or a chart-type marker interface) directly. The merge ergonomics are the same — you still array_merge(...) your own properties on top — but the defaults now come from $ctx rather than from inheritance.

The legacy contract and the AbstractWidgetType base class continue to work unchanged. The dashboard runtime in hyva-themes/commerce-module-admin-dashboard dispatches to both contracts via runtime instanceof checks, so old and new widgets can coexist.