Если вы работаете с BI‑системами, наверняка сталкивались с ситуацией, когда стандартных визуализаций не хватает. Хочется добавить свой график, который идеально подходит под задачи бизнеса.
В Modus BI такая возможность встроена в саму платформу — вы можете создавать свои плагины визуализаций. В этой статье мы шаг за шагом разберем, как собрать с нуля ��ростой, но гибко настраиваемый прогресс‑бар. Руководство будет полезным для разработчиков, которые хотят самостоятельно создавать уникальные визуализации на базе Modus BI.
Что понадобится:
Node.js (версия старше 14.21.3);
шаблон плагина из GitHub‑репозитория Modus BI;
доступ к порталу Modus BI;
редактор кода (VS Code, WebStorm и другие);
Шаг 1. Запускаем проект
Начните с шаблона плагина. Скачайте ZIP‑архив из ветки main репозитория Modus BI, распакуйте его и откройте папку custom-chart в редакторе кода.
Установите зависимости, используя npm:
npm install
Скачайте ядро проекта, следуя официальной инструкции. В корне создайте папку prebuild и распакуйте туда содержимое ядра.
Результат должен выглядеть так:

Запустите проект командой:
npm run start:dev
По умолчанию приложение откроется на http://localhost:7000.
При необходимости измените порт в файле ./config/index.js (свойство server_port).

Теперь в Modus BI откройте новый или существующий отчет и включите режим редактирования.

В списке визуализаций появится плитка DEV — это ваш будущий плагин. Перетащите ее в область «Компонент отображения не задан».
После этого появится пустой шаблон — это значит, плагин подключен и готов к разработке.

Шаг 2. Настраиваем редактор компонента
Настройки редактора компонента подключаются в файле src\modules\CustomSettings\Settings.js и автоматически отображаются в боковой панели редактора. Modus BI предоставляет готовые стандартные секции для базовых настроек.
Этот набор можно расширять набор собственными элементами управления. Наприм��р, добавим настройку для выбора цвета фона.
Сначала добавьте свойство для цвета фона в defaultConfig.json:
{
"bgColor": null,
// ... остальные настройки
}
Для удобного доступа к настройкам добавьте файл getDefaultConfig.js:
import defaultConfig from './defaultConfig.json';
export function getDefaultConfig() {
return defaultConfig;
}
/
Теперь создайте компонент выбора цвета.
В папке src/modules/CustomSettings/CommonSettingItems/ добавьте файл ColorSettingItem.jsx.
const ColorSettingItem = ({ pluginImports, title, onChange, color }) => { const { SettingsItem, SettingsColorPicker } = pluginImports.components; return ( <SettingsItem type='inline' title={title}> <SettingsColorPicker color={color} onChange={onChange} /> </SettingsItem> ); };
Здесь мы используем встроенные компоненты API Modus BI (SettingsItem, SettingsColorPicker). Они реализуют стандартную логику отображения, а мы добавляем дополнительную настройку — выбор цвета фона.
Добавьте обработчик изменения цвета в файле src\modules\CustomReducers\changeCustomChartReducer.js. Этот файл нужен для того, чтобы сохранять и применять настройки диаграммы, относящиеся к плагину. Через API платформы разработчик реализует свою логику обработки команд (например, изменение цвета фона).
if (action.command === 'changeStyleBgColor') { configDraft.bgColor = action.settings.value; }
Теперь в редакторе отчета появится опция «Цвет фона». Выбор цвета будет сразу применяться к плагину.

Шаг 3. Подготовка структуры осей диаграммы (это правильнее по сути и меньше путаницы с шагом 4)
Прежде чем визуализация сможет работать с данными, нужно настроить для них структуру осей. В Modus BI структура осей (axes) настраивается через полки — области, куда пользователь перетаскивает поля из датасета. Для прогресс‑бара понадобятся:
категории (например, проекты или регионы);
значения (числовые показатели);
фильтры.
Пример структуры в defaultConfig.json:
{ "axes": [ { "name": "Значения", "title": "Значения", "type": "values", "fields": [] }, { "name": "Категории", "title": "Категории", "type": "categories", "fields": [] }, { "name": "filters", "title": "Фильтры", "type": "filters", "fields": [] } ] }
В файле CustomAxes/index.js настройте сортировку:
export function sortAxes(props) { const { config } = props; return config.axes; }
Кроме сорти��овки важно определить, как будут выглядеть и работать «пилюли» — визуальные элементы, которые отображают поля, добавленные на полки.
Для этого в CustomAxes/index.js добавьте функции, которые управляют отображением и активностью элементов. Добавляя каждую функцию, мы заменяем ее дефолтную версию на свою кастомную и так кастомизируем диаграмму в целом.
// Настройка видимости пунктов меню export function isVisibleAxeDragItemMenuOption(props) { const { optionName, field } = props; if (field.type === 'values') { return ['renderTitleInput', 'renderSortMenuItem', 'renderAggregationMenuItem'].includes(optionName); } if (field.type === 'categories') { return ['renderTitleInput', 'renderSortMenuItem'].includes(optionName); } if (field.type === 'filters') { return ['renderTitleInput', 'renderFilterSettings'].includes(optionName); } return false; } // Настройка визуальных элементов пилюли export function isVisibleAxeDragItemElement(props) { const { elementName, field } = props; if (field.type === 'values') { return ['renderAggregationSelector', 'renderSortSelector'].includes(elementName); } if (field.type === 'categories') { return ['renderSortSelector'].includes(elementName); } return false; }
Эти функции задают правила для разных типов полей:
для «Значений» отображается селектор агрегации и сортировки;
для «Категорий» — только сортировка;
для «Фильтров» — специфические настройки фильтрации.
В продвинутых случаях можно написать собственный ConfigEditor. Этот модуль отвечает за добавление, обновление и обработку полей, а также за автоматическую агрегацию. В большинстве случаев достаточно дефолтной реализации ConfigEditor.
Полный пример реализации доступен в репозитории Modus BI ConfigEditor.js.
Шаг 4. Работа с данными
Чтобы визуализация могла получать значения из портала, создаем адаптер на основе CommonDataAdaptor. Он нужен для связи сырых данных с компонентом графика.
import CommonDataAdaptor from '../../duplicates/adaptors/CommonChartAdaptor/CommonDataAdaptor';
export default class DataAdaptor extends CommonDataAdaptor {}
Основной метод в CommonDataAdaptor — remapData. Он подготавливает данные из портала и приводит их к структурированному формату, удобному для отображения.
Шаг 5. Создаем визуализацию
Чтобы плагин работал в системе, подключаем компонент в CustomChart.js — это точка входа д��я всех кастомных визуализаций.
Визуализация реализуется в компоненте ProgressBarChart.jsx. Он использует:
хук useStyles управляет цветами и отступами контейнера;
SCSS‑стили — задают структуру и оформление элементов;
Header.jsx — отдельный компонент для заголовка;
getDescription и getLocal — функции для описания и локализации;
LoadProgress — встроенный компонент для отображения загрузки данных.
Финальный вариант ProgressBarChart.jsx:
const ProgressBarChart = React.forwardRef(({ config, theme, componentId, pluginImports }, ref) => { const { LoadProgress } = pluginImports.components; const styles = useStyles(config, theme); const title = getLocal(config, 'title'); const showHeader = config?.showtitle || false; return ( <div className='hsChartContainer ProgressBarChart' style={styles.container} ref={ref}> {showHeader && <Header title={title} />} <div className='componentBody ProgressBarChartBody'> <div className='componentContainer ProgressBarChartComponent' style={styles.component}> <div> <LinearProgress value={40}> ProgressBarChart </LinearProgress> </div> </div> <LoadProgress /> </div> </div> ); });

Полный код компонентов и стилей смотрите в репозитории Modus BI.
Шаг 6. Подключаем реальные данные
На предыдущем шаге мы добавили визуализацию — прогресс‑бар. Но пока он статичен. Чтобы связать его с реальными данными, используем адаптер.
Адаптер (remapData) — это функция, которая преобразует данные из системы Modus BI в формат, удобный для визуализации.Когда пользователь добавляет поля на полки (axes), данные автоматически проходят через метод remapData.В простейшем случае он возвращает числовое значение, которое мы передаем в компонент прогресс‑бара
Пример работы с данными:
import DataAdaptor from '../dataAdaptor'; export function useLoadData({ config, loadDatas, cacheId, data, editorActive, componentId, reloadDatas, inEditor }) { const [loaded, setLoaded] = useState(false); const [loading, setLoading] = useState(false); const [dataAdaptor, setDataAdaptor] = useState(null); const datasetId = getDatasetId(config); const queryObjects = DataAdaptor.getQueryObjects(config); const stringQueryObjects = JSON.stringify(queryObjects); useEffect(() => { if (!datasetId) return; runLoadData(); }, [datasetId, cacheId, stringQueryObjects, data, editorActive, inEditor]); const runLoadData = () => { if (!datasetId) return; if (!data && cacheId) { setLoading(true); loadDatas(datasetId, null, config.filters, queryObjects, { editor: editorActive, componentId }) .then((res) => { setLoaded(true); const adapter = new DataAdaptor(res.data, config, null, cacheId); setDataAdaptor(adapter); }) .finally(() => { setLoading(false); }); } else if (data && !data.fetching) { setDataAdaptor(new DataAdaptor(data.data, config, null, cacheId)); } }; return { dataAdaptor, loadedData: loaded, loadingData: loading, }; }
Теперь подключим хук в ProgressBarChart и передадим полученное значение в LinearProgress:
import { useLoadData } from './useLoadData'; const ProgressBarChart = React.forwardRef(({ config, datas, theme, pluginImports }, ref) => { const { LoadProgress } = pluginImports.components; const styles = useStyles(config, theme); const { dataAdaptor, loadingData } = useLoadData({ config, cacheId, loadDatas, reloadDatas, data, componentId, editorActive, inEditor}); let list = []; const valuesAxe = _.find(config.axes, ['type', 'values']); if (valuesAxe?.fields.length > 1) { list = dataAdaptor?.plotData.map(data => ({ id: data['ID0'], category: data.categories || 'Без категории', value: getPercentage(data['values0'], data['values1']) })) || []; } return ( <div className='hsChartContainer ProgressBarChart' style={styles.container} ref={ref}> <div className='componentBody ProgressBarChartBody'> <div className='componentContainer ProgressBarChartComponent' style={styles.component}> <div className='ProgressBarChartList'> {list.length === 0 ? <b>Нет данных</b> : null} {list.map((item) => ( <LinearProgress key={item.id} value={item.value} containerColor={'white'} linearColor={'red'}> {item.category} </LinearProgress> ))} </div> </div> <LoadProgress /> </div> </div> ); });
В итоге, получили плагин, который работает с данными.
Шаг 7. Проверка результата
После всех шагов убедитесь, что:
цвет фона меняется через настройки;
заголовок отображается;
отступы контейнера применяются;
данные подтягиваются и отображаются как прогресс‑бар;
оси и фильтры работают корректно.
Если все это выполняется — плагин готов к использованию.
Заключение
Вы создали плагин прогресс-бара для Modus BI, который:
1. Настраивается через редактор (цвет фона, заголовки)
2. Работает с данными через стандартные механизмы платформы
3. Отображает данные в виде прогресс-баров
4. Подключается к реальным данным из портала
Готовый ко�� доступен в ветке tutorial-progress репозитория Modus BI. Этот пример можно использовать как основу для создания других типов визуализаций.
