Всем привет! Меня зовут Александр Цай, я ведущий инженер-аналитик в МТС Web Services.
Занимаюсь всем, что связано с данными и ИИ. Найти, заполучить, обработать, спроектировать, развернуть — это все ко мне.
Опущу все нудные подробности, решили мы развернуть себе Superset 6.1.0 — последнюю версию, но столкнулись с тем, что заказчику категорически не нравится штатный фильтр по датам и он не хочет пересаживаться со своего BI, а хочет он календарик. А еще — пару плагинов с оглядкой на PowerBI. Поэтому встал вопрос создания собственного плагина-фильтра.
Казалось бы, задача несложная. Пара-тройка гайдов в en сегменте имеется, есть даже документация и штатный генератор шаблонов. Но по факту оказалось, что все это не работает. Генератор так вообще не обновлялся аж с 2024 года: выдает шаблон, который ссылается на уже несуществующие классы и типы, что автоматически делает все гайды неактуальными…
А что все это значит? Время написать свой собственный!
Об этом и расскажу в сегодняшнем материале.
Дисклеймер: Я питонист до мозга костей и в тайпскрипте, да и фронте в целом, понимаю не очень много. Простите меня, адепты сего языка программирования, но код самого плагина навайбкожен (хоть и проверен насколько я смог).
В npm не запушил тоже умышленно, цель статьи — дать исходники и показать, как можно собрать плагин. Любой желающий может что-то додумать и доработать, собрать или разобрать и так далее.

Где живут плагины и фильтры в Superset
Superset — это классический тандем Flask (бэкенд) + React/TypeScript (фронтенд), и вся плагинная кухня находится в папке superset-frontend/src/.
Штатные фильтры лежат в src/filters/components/. Их там пять:
Папка | Ключ | Что делает |
|---|---|---|
Select | filter_select | выпадающий список |
Range | filter_range | диапазон значений |
Time | filter_time | фильтр по времени |
TimeColumn | filter_timecolumn | выбор временной колонки |
TimeGrain | filter_timegrain | гранулярность времени |
Ключевая мысль здесь это то, что фильтр — это не отдельная магическая сущность, а обычный ChartPlugin, у которого в метаданных проставлено поведение Behavior.NativeFilter. Оно как раз помогает превратить обычный плагин в «native filter», который уже можно добавить на дашборд.
Список фильтров в редакторе дашборда строится из реестра плагинов. Superset берет getChartMetadataRegistry().items и оставляет только те, у кого в метаданных есть поведение Behavior.NativeFilter. Никакого хардкод-списка фильтров в коде нет, а значит, чтобы появился новый фильтр, достаточно зарегистрировать свой ChartPlugin — и он сам всплывет в «+ Add filter».
В Superset для этого есть штатная точка расширения — src/setup/setupPluginsExtra.ts (пустой файл с комментарием «For individual deployments to add custom overrides»). Регистрируем плагин там:
import CalendarFilterPlugin from '@superset-ui/plugin-filter-calendar'; // For individual deployments to add custom overrides export default function setupPluginsExtra() { new CalendarFilterPlugin().configure({ key: 'filter_calendar' }).register(); }
Чтобы в выборе колонки показывались только даты: правим запись filter_calendar: [GenericDataType.Temporal] в карте FILTER_SUPPORTED_TYPES. После пересборки фронтенда плагин появится в списке фильтров на дашборде.
Что берем за основу
Первое, что мы решили сделать, — открыть и внимательно изучить штатные фильтры. Для календаря ближе всего по смыслу Range и Time, они и стали для нас готовым референсом.
Каждый фильтр — это маленький самодостаточный пакет со стандартной структурой. В нашем репозитории он живет в src/, а при установке раскладывается в superset-frontend/plugins/plugin-filter-calendar/ - точно так же, как официальные plugin-chart-*:
src/ ├── index.ts # регистрация ChartPlugin ├── CalendarFilterPlugin.tsx # сам React-компонент ├── buildQuery.ts # построение запроса к БД ├── controlPanel.ts # панель настроек фильтра ├── transformProps.ts # преобразование данных в пропсы ├── types.ts # TypeScript-типы + локальные копии общих типов фильтров ├── common.ts # локальные копии FilterPluginStyle / StatusMessage / noOp └── images/thumbnail.png # иконка
Плюс в соседях есть готовые переиспользуемые штуки, которые нам пригодятся:
UI-кит из @superset-ui/core/components — DatePicker, RangePicker, FormItem. Это обертки над antd, так что сам календарик писать не надо — он уже есть, осталось надеть на него правильную «обвязку»;
dayjs — уже в зависимостях Superset, его юзаем для работы с датами.
Но важнее всего — понять механику DataMask. Фильтр не ходит сам в БД и не делает запросов за графики дашборда. Вместо этого он через хук setDataMask передает дашборду команду добавить к SQL нужные условия. Вся коммуникация устроена через объект с тремя полями:
extraFormData — сами условия фильтрации (что добавить в SQL);
filterState — текущее значение и подпись, которую показывать на дашборде;
ownState — состояние, которое надо сохранять и восстанавливать.
Как только фильтр отправляет новую маску, дашборд перезапрашивает все свои графики с учетом новых условий. Красиво и просто.
Что пишем
Разберу по файлам, что и зачем. Не буду углубляться в тайпскриптовские дебри, дам суть. Полные файлы в репозитории.
types.ts
Объявляем типы и дефолтные настройки плагина:
export interface PluginFilterCalendarCustomizeProps { defaultValue?: string[] | null; enableEmptyFilter?: boolean; // фильтр обязателен к заполнению enableSingleDate?: boolean; // одиночная дата вместо диапазона } export const DEFAULT_FORM_DATA = { defaultValue: null, enableEmptyFilter: false, enableSingleDate: false, };
index.ts
Регистрируем плагин. Класс наследуется от ChartPlugin, в метаданных указываем имя, описание, иконку и — главное — поведение:
export default class CalendarFilterPlugin extends ChartPlugin { constructor() { const metadata = new ChartMetadata({ name: t('Calendar filter'), description: t('Calendar date range filter plugin'), behaviors: [Behavior.InteractiveChart, Behavior.NativeFilter], // ← вот он фильтр enableNoResults: false, tags: [t('Experimental')], thumbnail, }); super({ buildQuery, controlPanel, loadChart: () => import('./CalendarFilterPlugin'), metadata, transformProps, }); } }
buildQuery.ts
Тут мы просим Superset сходить в БД и вернуть минимальную и максимальную дату по выбранной колонке. Пригодится, чтобы понимать, какие даты вообще есть в данных:
metrics: [ { aggregate: 'MIN', column: { column_name: column, type_generic: GenericDataType.Temporal }, label: 'min', }, { aggregate: 'MAX', column: { column_name: column, type_generic: GenericDataType.Temporal }, label: 'max', }, ]
controlPanel.ts
Панель настроек фильтра в редакторе дашборда. Тут два блока:
Query — выбор колонки с датами (обязательный);
UI Configuration — две галочки: «Filter value is required» (enableEmptyFilter) и «Single date» (enableSingleDate).
transformProps.ts
По сути это мостик между данными в Superset и пропсами React-компонента, который достает из chartProps готовые хуки дашборда (setDataMask, setHoveredFilter и так далее), данные запроса, текущее состояние фильтра и отдает все это компоненту.
CalendarFilterPlugin.tsx
Самое интересное — React-компонент на хуках, где ядро -— редьюсер поверх useImmerReducer, который накапливает DataMask:
const [dataMask, dispatchDataMask] = useImmerReducer(reducer, { extraFormData: {}, filterState: {}, ownState: {}, });
А дальше функция updateDataMask, которая превращает выбранные даты в SQL-условия:
const filters: QueryObjectFilterClause[] = []; if (dates[0] && dates[1] && dates[0] === dates[1]) { filters.push({ col, op: '==', val: dates[0] }); // одна дата } else { if (dates[0]) filters.push({ col, op: '>=', val: dates[0] }); // интервал if (dates[1]) filters.push({ col, op: '<=', val: dates[1] }); } extraFormData.filters = filters;
То есть выбор диапазона превращается в column >= start AND column <= end, выбор одной даты — в column == date. Ровно как PowerBI Date Slicer.
Изменения маски улетают на дашборд через useEffect с вызовом setDataMask(dataMask).
В JSX есть два варианта:
если включен enableSingleDate, рендерим DatePicker, иначе RangePicker с плейсхолдерами «Start date» / «End date»;
если фильтр обязательный и значение не выбрано, показываем StatusMessage с ошибкой валидации.
Ну и отдельная история с disabledDate. Изначально я хотел ограничивать выбор дат диапазоном MIN/MAX из данных, но календарь при этом открывался на другом месяце, и все даты выглядели задизейбленными, что путало коллег. Поэтому в коде есть закомменченый кусок.
🚀 Как интегрируем в платформу
Плагин ставится как полноценный npm-пакет в superset-frontend/plugins/plugin-filter-calendar/ — та же раскладка, что у официальных plugin-chart-*, плюс три однострочные правки в документированных точках расширения:
1. Пакет — кладем плагин в superset-frontend/plugins/plugin-filter-calendar/. npm workspaces (plugins/*) сам свяжет его в node_modules/@superset-ui/plugin-filter-calendar при npm install.
2. Регистрация — в src/setup/setupPluginsExtra.ts (штатный хук расширения) импортируем и регистрируем плагин - так он попадает в реестр и появляется в «+ Add filter».
3. Path alias — в superset-frontend/tsconfig.json добавляем @superset-ui/plugin-filter-calendar → ./plugins/plugin-filter-calendar/src, чтобы TS/IDE резолвили пакет как у официальных плагинов.
4. Тип колонки — в FILTER_SUPPORTED_TYPES (рядом с filter_timegrain) добавляем filter_calendar: [GenericDataType.Temporal], чтобы в выборе колонки были только даты.
Для удобства я набросал install.sh, который раскладывает пакет и вносит правки с проверкой, что плагин еще не зарегистрирован (повторный запуск не дублирует):
Что делает скрипт | Файл |
|---|---|
Устанавливает пакет плагина | superset-frontend/plugins/plugin-filter-calendar/ |
Регистрирует плагин в хуке расширения | src/setup/setupPluginsExtra.ts |
Добавляет path alias | superset-frontend/tsconfig.json |
Добавляет filter_calendar в FILTER_SUPPORTED_TYPES | FiltersConfigForm/constants.ts |
С ним установка сводится к командам:
git clone https://github.com/Kami-sama322/superset-plugin-filter-calendar.git;
chmod +x superset-plugin-filter-calendar/install.sh;
./superset-plugin-filter-calendar/install.sh ./superset; # корень репозитория Superset
Дальше пересобираем фронтенд. Надеюсь, к этому времени он у вас уже подготовлен. Если нет, то тут можно посмотреть процесс установки.
cd superset-frontend;
npm install; # свяжет workspace-пакет и поставит зависимости
npm run dev-server; # режим разработки
Ну и дальше:
Открываем дашборд → Edit → + Add filter → выбираем Calendar filter;
Указываем Column — колонку с датами из датасета (тип Temporal);
При желании включаем Single date;
Жмем Save — на дашборде появляется календарь;
Выбираем дату или диапазон, после чего все графики моментально отфильтровываются.


Быстрый старт в Docker для тех, кто дочитал до конца (или хотя бы долистал)
В корне репозитория лежат Dockerfile и docker-compose.yml — образ Superset с уже встроенным плагином:
docker compose build; # сборка фронтенда Superset с плагином (долго)
docker compose up -d; # запуск: инициализация БД, админ admin/admin, примеры
# открыть http://localhost:8088
Вместо заключения
Итак, что мы имеем в итоге:
Календарь как native-фильтр на дашбордах с диапазоном дат и одиночной датой без ограничений на выбор;
Фильтрация на уровне SQL: column >= start AND column <= end / column == date;
Автоустановка одной командой через install.sh.
Полезные ссылки: готовый плагин в GitHub и оригинальная инструкция по запуску Superset в dev-режиме.
Надеюсь, кому-нибудь это сэкономит вечер, а скорее всего — пару или даже тройку.
До встречи в следующих материалах!
