mirror of
https://github.com/dartdavros/chatballs.git
synced 2026-10-05 01:14:58 +03:00
📝 docs(webchat): добавить статью о классах виджета
This commit is contained in:
1 parent
29e2232fc3
commit
52524287ae
5 files changed
+174
-4
No files matched your search
@@ -0,0 +1,29 @@
|
||||
---
|
||||
id: T-024
|
||||
title: Статья справки «Классы виджета»
|
||||
milestone: M04
|
||||
status: done
|
||||
depends_on:
|
||||
- T-022
|
||||
order: 6
|
||||
spec: "0021"
|
||||
created: 2026-09-29
|
||||
branch: skaro/T-024-statya-spravki-klassy-vidzheta
|
||||
---
|
||||
|
||||
## Цель
|
||||
|
||||
Администратор знает, какие классы можно стилизовать своим CSS (R-9).
|
||||
|
||||
## Критерии приёмки
|
||||
|
||||
- [x] В справке опубликована и доступна по адресу /articles/klassy-vidzheta статья со списком классов виджета, назначением и примером CSS; проверка ссылки из раздела «Оформление» относится к T-023 и не блокирует приёмку T-024.
|
||||
- [x] Статья совпадает со списком классов в коде.
|
||||
|
||||
## Заметки
|
||||
|
||||
Статьи справки: docs/portal-curation/chatballs.json (там же статья veb-vidzhet); ссылки — shared/help helpArticleUrl.
|
||||
|
||||
## Итог
|
||||
|
||||
Статья «Классы виджета» опубликована и доступна в действующем центре помощи по /articles/klassy-vidzheta; применены подготовленные порядок и связи статей. Браузерная проверка и реальный API подтвердили таблицу, назначение и CSS-пример, а опубликованный текст и все 26 классов совпадают с исходниками. Оба актуальных критерия T-024 проверены и выполнены; изменения репозитория находятся в коммите b8654529.
|
||||
@@ -1,8 +1,28 @@
|
||||
# Редакционный порядок центра помощи Chatballs
|
||||
|
||||
`chatballs.json` задаёт порядок опубликованных статей внутри каждого раздела и
|
||||
направленные связи между статьями. Карта охватывает 72 русскоязычные статьи и
|
||||
170 переходов. Она не меняет тексты, категории и статусы публикации.
|
||||
направленные связи между статьями. Карта охватывает 73 русскоязычные статьи и
|
||||
172 перехода. Она не меняет тексты, категории и статусы публикации.
|
||||
|
||||
`articles/klassy-vidzheta.yml` содержит статью «Классы виджета» в штатном
|
||||
формате импорта статей портала. Её текст сверяется с
|
||||
`apps/web-chat/src/widgetClasses.ts` тестом `widgetClassesDocumentation.test.ts`:
|
||||
|
||||
```sh
|
||||
npx vitest run docs/portal-curation/widgetClassesDocumentation.test.ts
|
||||
```
|
||||
|
||||
При реализации раздела «Оформление» ссылка «Классы виджета» должна получить
|
||||
адрес `helpArticleUrl("klassy-vidzheta")` из `shared/help`. В текущей версии
|
||||
`WebIntegrationPage` этот раздел отключён; подключение ссылки и проверка
|
||||
перехода зависят от реализации раздела в T-023.
|
||||
|
||||
Добавление YAML в репозиторий само по себе не публикует статью. Перед
|
||||
применением обновлённой карты нужно импортировать YAML через библиотеку
|
||||
статей центра помощи в существующий раздел «Точки входа» и опубликовать
|
||||
ревизию. Изменения действующего портала выполняются только после
|
||||
согласования с владельцем. До публикации новой статьи проверка карты
|
||||
завершится ошибкой состава статей.
|
||||
|
||||
Команда `apply_portal_curation` сначала проверяет, что карта полностью совпадает
|
||||
с опубликованными статьями выбранного портала: состав, разделы, язык и ссылки.
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
articles:
|
||||
- slug: "klassy-vidzheta"
|
||||
categoryPath:
|
||||
- "Точки входа"
|
||||
locale: "ru"
|
||||
title: "Классы виджета"
|
||||
summary: "Какие части окна чата можно оформить своим CSS: классы, их назначение и пример стилей."
|
||||
content: |
|
||||
Свои CSS-стили меняют оформление внутри окна чата. Откройте настройки веб-подключения, раздел **Оформление**, и вставьте правила в поле **Свои CSS-стили**. Сохраните изменения: код виджета на сайте менять не требуется.
|
||||
|
||||
Стили применяются внутри iframe чата и не влияют на страницу сайта. Цвет, иконку, положение, размер и форму кнопки открытия задавайте настройками раздела «Оформление»: перечисленные ниже классы относятся к окну чата, а не к этой кнопке.
|
||||
|
||||
## Классы окна чата
|
||||
|
||||
В CSS перед именем класса ставьте точку: например, `.cb-header`.
|
||||
|
||||
| Класс | Назначение |
|
||||
| --- | --- |
|
||||
| `cb-widget` | Корневой контейнер окна чата. |
|
||||
| `cb-header` | Шапка окна чата. |
|
||||
| `cb-header-icon` | Иконка в шапке: стандартный знак или загруженное изображение. Отсутствует, если иконка отключена. |
|
||||
| `cb-header-title` | Заголовок в шапке. |
|
||||
| `cb-header-button` | Кнопки разворачивания и сворачивания окна в шапке. |
|
||||
| `cb-body` | Прокручиваемая область чата. |
|
||||
| `cb-message` | Контейнер сообщения клиента, агента или специалиста. |
|
||||
| `cb-message--client` | Дополнительный класс контейнера сообщения клиента. |
|
||||
| `cb-message-content` | Обёртка содержимого сообщения, задающая его максимальную ширину. |
|
||||
| `cb-bubble` | Пузырь сообщения клиента, агента или специалиста. |
|
||||
| `cb-bubble--client` | Дополнительный класс пузыря сообщения клиента. |
|
||||
| `cb-bubble--agent` | Дополнительный класс пузыря ответа агента или специалиста. |
|
||||
| `cb-bubble--operator` | Дополнительный класс пузыря ответа специалиста; применяется вместе с `cb-bubble--agent`. |
|
||||
| `cb-bubble--audio` | Дополнительный класс пузыря голосового сообщения. |
|
||||
| `cb-message-time` | Время или состояние отправки под сообщением клиента. |
|
||||
| `cb-composer` | Область ввода сообщения, включая режим записи голосового. |
|
||||
| `cb-composer-input` | Текстовое поле ввода сообщения. |
|
||||
| `cb-composer-button` | Кнопки в области ввода: вложение, запись, отправка и отмена записи. |
|
||||
| `cb-send-button` | Дополнительный класс кнопки отправки текста, вложения или голосового сообщения. |
|
||||
| `cb-record-button` | Дополнительный класс кнопки начала записи голосового сообщения. |
|
||||
| `cb-start-footer` | Область с кнопкой начала чата. |
|
||||
| `cb-start-button` | Кнопка согласия и начала чата. |
|
||||
| `cb-form` | Форма передачи номера телефона по запросу в чате. |
|
||||
| `cb-form-field` | Поле номера телефона в этой форме. |
|
||||
| `cb-form-submit` | Кнопка отправки номера телефона. |
|
||||
| `cb-brand` | Нижняя область с подписью Chatballs. |
|
||||
|
||||
Классы с `--` дополняют основной класс на том же элементе. Например, сообщение клиента имеет классы `cb-bubble` и `cb-bubble--client`. Общее правило для `.cb-bubble` затронет все пузыри; правило для `.cb-bubble--client` — только сообщения клиента.
|
||||
|
||||
Чтобы изменить только ответы агента, исключите ответы специалиста: `.cb-bubble--agent:not(.cb-bubble--operator)`.
|
||||
|
||||
## Пример CSS
|
||||
|
||||
Этот пример меняет шрифт шапки, скругление сообщений клиента и регистр текста кнопки начала чата:
|
||||
|
||||
```css
|
||||
.cb-header {
|
||||
font-family: "PT Sans", sans-serif;
|
||||
}
|
||||
|
||||
.cb-bubble--client {
|
||||
border-radius: 18px 4px 18px 18px;
|
||||
}
|
||||
|
||||
.cb-start-button {
|
||||
text-transform: uppercase;
|
||||
}
|
||||
```
|
||||
|
||||
Если PT Sans не установлен у посетителя, браузер использует шрифт без засечек. Шрифт, подключённый только на странице сайта, не становится доступным внутри iframe.
|
||||
|
||||
Базовые стили этих классов имеют низкую специфичность, а свои CSS-стили добавляются после них. Для обычного изменения оформления достаточно селектора класса без `!important`. Вложенные элементы могут иметь собственные стили: например, правило для контейнера не меняет автоматически цвета всех его потомков.
|
||||
|
||||
## Ограничения и сброс
|
||||
|
||||
Размер своих CSS-стилей — до 10 КБ. При сохранении удаляются `@import`, внешние адреса в `url(...)` и `expression`. Подключать таким способом внешние шрифты или изображения нельзя.
|
||||
|
||||
Чтобы вернуться к базовому оформлению, нажмите **Сбросить** возле поля CSS и сохраните настройки.
|
||||
|
||||
О подключении чата к сайту читайте в статье [Чат на сайте](/articles/veb-vidzhet).
|
||||
@@ -5,7 +5,7 @@
|
||||
"start": ["chto-takoe-chatballs", "pervyj-den", "demo-dannye", "roli-i-prava"],
|
||||
"installation": ["pervyj-zapusk", "adres-i-https", "pochta-smtp", "yazyk-ustanovki", "hranilishche-fajlov", "rezervnoe-kopirovanie", "obnovlenie", "diagnostika", "zvonki-relay"],
|
||||
"organization": ["organizaciya-nastrojki", "ai-provajder", "gruppy", "kogda-zvat-na-pomoshch", "neskolko-organizacij"],
|
||||
"entry-points": ["tochki-vhoda-obzor", "telegram", "token-telegram", "max", "token-max", "veb-vidzhet", "pochtovyj-yashchik", "vkontakte", "golosovye-i-zvonki", "bot-uvedomlenij"],
|
||||
"entry-points": ["tochki-vhoda-obzor", "telegram", "token-telegram", "max", "token-max", "veb-vidzhet", "klassy-vidzheta", "pochtovyj-yashchik", "vkontakte", "golosovye-i-zvonki", "bot-uvedomlenij"],
|
||||
"agents": ["kartochka-agenta", "model-i-provajder", "instrukcii-agenta", "znaniya-agenta", "zapusk-i-ostanovka", "agent-ne-znaet-otveta", "yazyk-otveta"],
|
||||
"knowledge": ["material-i-vlozheniya", "kategorii-znanij", "kak-agent-nahodit-otvet", "import-yaml-znaniya", "massovye-dejstviya"],
|
||||
"portals": ["sozdanie-portala", "kategorii-portala", "statya-portala", "revizii-i-publikaciya", "oformlenie-portala", "svoj-domen-portala", "import-statej", "portal-i-agent"],
|
||||
@@ -39,7 +39,8 @@
|
||||
"token-telegram": ["telegram", "bot-uvedomlenij"],
|
||||
"max": ["token-max", "telegram"],
|
||||
"token-max": ["max", "token-telegram"],
|
||||
"veb-vidzhet": ["golosovye-i-zvonki", "oformlenie-portala", "tochki-vhoda-obzor"],
|
||||
"veb-vidzhet": ["klassy-vidzheta", "golosovye-i-zvonki", "oformlenie-portala", "tochki-vhoda-obzor"],
|
||||
"klassy-vidzheta": ["veb-vidzhet"],
|
||||
"pochtovyj-yashchik": ["pochta-smtp", "tochki-vhoda-obzor"],
|
||||
"vkontakte": ["tochki-vhoda-obzor", "kartochka-agenta"],
|
||||
"golosovye-i-zvonki": ["fajly-golosovye-zvonki", "zvonki-relay"],
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
import { createElement } from "react";
|
||||
import { renderToStaticMarkup } from "react-dom/server";
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
import articleYaml from "./articles/klassy-vidzheta.yml?raw";
|
||||
import plan from "./chatballs.json";
|
||||
import { widgetClasses } from "../../apps/web-chat/src/widgetClasses";
|
||||
import { helpArticleUrl } from "../../apps/internal-ui/src/shared/help";
|
||||
import { parseArticleYaml } from "../../apps/internal-ui/src/features/support-portals/parseArticleYaml";
|
||||
import { MarkdownContent, parseMarkdown } from "../../apps/internal-ui/src/features/help-center/MarkdownContent";
|
||||
|
||||
const { articles: [article] } = parseArticleYaml(articleYaml);
|
||||
|
||||
describe("widget classes help article", () => {
|
||||
it("documents every stable class exactly once with a purpose", () => {
|
||||
const rows = [...article.content.matchAll(/^\| `(cb-[a-z-]+)` \| (.+) \|$/gm)];
|
||||
expect(rows.map((row) => row[1]).sort()).toEqual(Object.values(widgetClasses).sort());
|
||||
expect(rows.every((row) => row[2].trim().length > 0)).toBe(true);
|
||||
|
||||
const html = renderToStaticMarkup(createElement(MarkdownContent, { content: article.content }));
|
||||
expect(html).toContain("<table>");
|
||||
expect(html).toContain('class="language-css"');
|
||||
const sample = parseMarkdown(article.content).blocks.find((block) => block.kind === "code");
|
||||
if (!sample) throw new Error("Missing CSS example");
|
||||
const sampleClasses = [...sample.text.matchAll(/\.(cb-[a-z-]+)\s*\{/g)].map((match) => match[1]);
|
||||
expect(sampleClasses.length).toBeGreaterThan(0);
|
||||
for (const name of sampleClasses) expect(Object.values(widgetClasses)).toContain(name);
|
||||
});
|
||||
|
||||
it("registers the importable article next to web widget help with reciprocal links", () => {
|
||||
expect(article.title).toBe("Классы виджета");
|
||||
expect(article.locale).toBe(plan.locale);
|
||||
expect(article.categoryPath).toEqual(["Точки входа"]);
|
||||
const entries: string[] = plan.order["entry-points"];
|
||||
expect(entries[entries.indexOf("veb-vidzhet") + 1]).toBe(article.slug);
|
||||
expect(Object.values(plan.order).flat().filter((slug) => slug === article.slug)).toHaveLength(1);
|
||||
expect(plan.related["veb-vidzhet"]).toContain(article.slug);
|
||||
const related: Record<string, string[]> = plan.related;
|
||||
expect(related[article.slug]).toContain("veb-vidzhet");
|
||||
expect(helpArticleUrl(article.slug)).toBe("https://chatballs.com.edevs.tech/articles/klassy-vidzheta");
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user