Именование и организация

Грамотное именование — основа поддерживаемости кода при работе с турами в интерфейсе. В контексте Shepherd.js важно учитывать, что туры и шаги становятся частью логики приложения, а значит должны быть читаемыми, предсказуемыми и масштабируемыми.

Имена туров

Тур (Tour) представляет собой последовательность шагов, поэтому его имя должно отражать цель или сценарий использования:

  • onboardingTour — первичное знакомство пользователя
  • profileSetupTour — настройка профиля
  • checkoutGuideTour — процесс оформления заказа

Ключевые правила:

  • Использовать camelCase
  • Отражать бизнес-смысл, а не техническую реализацию
  • Избегать общих имен вроде mainTour, testTour

Имена шагов

Каждый шаг внутри тура может иметь id, который используется для управления:

tour.addStep({
  id: 'enter-email',
  text: 'Введите email',
  attachTo: { element: '#email', on: 'bottom' }
});

Рекомендации:

  • Использовать kebab-case для идентификаторов (enter-email, confirm-password)
  • Делать имена уникальными в пределах тура
  • Отражать действие или контекст шага

Примеры:

  • welcome-message
  • click-settings
  • submit-form

Имена переменных и экземпляров

Экземпляры тура обычно создаются через:

const onboardingT our = new Shepherd.Tour({...});

Рекомендации:

  • Использовать const, так как тур редко переопределяется
  • Давать переменной имя, совпадающее с назначением тура
  • Избегать сокращений (tour1, t)

Структурирование кода

С ростом количества туров важно организовать код так, чтобы он оставался понятным и легко расширяемым.

Разделение по модулям

Каждый тур рекомендуется выносить в отдельный файл:

/tours
  ├── onboardingTour.js
  ├── dashboardTour.js
  └── checkoutTour.js

Пример структуры файла:

import Shepherd from 'shepherd.js';

export function createOnboardingTour() {
  const tour = new Shepherd.Tour({
    defaultStepOptions: {
      cancelIcon: { enabled: true },
      classes: 'shepherd-theme-default'
    }
  });

  tour.addStep({
    id: 'welcome',
    text: 'Добро пожаловать!',
    attachTo: { element: '.header', on: 'bottom' }
  });

  return tour;
}

Преимущества:

  • Изоляция логики
  • Повторное использование
  • Упрощение тестирования

Централизованное управление турами

Создание единой точки управления:

import { createOnboardingTour } from './tours/onboardingTour';
import { createCheckoutTour } from './tours/checkoutTour';

export const tours = {
  onboarding: createOnboardingTour(),
  checkout: createCheckoutTour()
};

Такой подход позволяет:

  • Легко запускать туры из разных частей приложения
  • Хранить ссылки на все туры в одном месте
  • Управлять состоянием (запущен ли тур, завершён и т.д.)

Организация шагов внутри тура

При большом количестве шагов важно избегать перегруженности.

Логическое группирование

Если тур содержит 10+ шагов, полезно разбить их по смыслу:

function addAuthSteps(tour) {
  tour.addStep({ id: 'login', ... });
  tour.addStep({ id: 'password', ... });
}

function addProfileSteps(tour) {
  tour.addStep({ id: 'avatar', ... });
  tour.addStep({ id: 'bio', ... });
}

Использование:

addAuthSteps(tour);
addProfileSteps(tour);

Преимущества:

  • Повышение читаемости
  • Повторное использование блоков
  • Упрощение поддержки

Конфигурационные массивы

Альтернативный подход — хранить шаги в виде массива:

const steps = [
  {
    id: 'step-1',
    text: 'Шаг 1',
    attachTo: { element: '#el1', on: 'bottom' }
  },
  {
    id: 'step-2',
    text: 'Шаг 2',
    attachTo: { element: '#el2', on: 'right' }
  }
];

steps.forEach(step => tour.addStep(step));

Плюсы:

  • Декларативный стиль
  • Удобство динамической генерации
  • Простота сериализации (например, из API)

Именование CSS-классов и селекторов

Shepherd активно использует DOM-селекторы (attachTo), поэтому важно соблюдать единый стиль.

Рекомендации:

  • Использовать BEM или аналогичную методологию:

    • .header__menu
    • .profile__avatar
  • Избегать привязки к нестабильным селекторам:

    • .nth-child
    • .dynamic-class-123

Плохо:

attachTo: { element: '.btn', on: 'top' }

Хорошо:

attachTo: { element: '.checkout__submit-button', on: 'top' }

Конвенции для событий и действий

Shepherd позволяет добавлять действия через buttons:

buttons: [
  {
    text: 'Далее',
    action: tour.next
  }
]

При сложной логике:

buttons: [
  {
    text: 'Сохранить',
    action: () => handleSaveAndNext()
  }
]

Рекомендации по именованию функций:

  • handleNextStep
  • handleFormSubmit
  • goToDashboard

Избегать:

  • doStuff
  • clickHandler1

Масштабируемая архитектура

Использование фабрик

Фабричный подход позволяет создавать туры с параметрами:

export function createFeatureTour(featureName) {
  const tour = new Shepherd.Tour();

  tour.addStep({
    id: `${featureName}-intro`,
    text: `Обзор функции ${featureName}`
  });

  return tour;
}

Интеграция с состоянием приложения

При использовании фреймворков (React, Vue):

  • Хранить текущий тур в состоянии
  • Использовать контекст или store (например, Redux)

Пример:

const [currentTour, setCurrentTour] = useState(null);

Документирование туров

Каждый тур — часть UX, поэтому важно документировать:

  • Назначение тура
  • Где и когда он запускается
  • Какие элементы задействованы

Пример комментария:

// Тур для новых пользователей.
// Запускается после регистрации.
// Охватывает основные функции панели управления.

Типичные ошибки в организации

1. Смешивание логики и UI

Плохо:

tour.addStep({
  text: getDynamicTextFromAPI()
});

Лучше:

  • Подготовить данные заранее
  • Передавать в тур уже готовые значения

2. Дублирование шагов

Повторяющиеся шаги стоит выносить в функции или конфигурации.

3. Жёсткая привязка к DOM

Если элемент может отсутствовать:

if (document.querySelector('#optional')) {
  tour.addStep({...});
}

Рекомендованная структура проекта

/src
  /tours
    /steps
      authSteps.js
      profileSteps.js
    onboardingTour.js
    dashboardTour.js
  /services
    tourManager.js

tourManager.js:

class TourManager {
  constructor() {
    this.tours = {};
  }

  register(name, tour) {
    this.tours[name] = tour;
  }

  start(name) {
    this.tours[name]?.start();
  }
}

export const tourManager = new TourManager();

Ключевые принципы

  • Имена должны отражать смысл, а не реализацию
  • Структура должна позволять рост без хаоса
  • Логика туров должна быть изолирована
  • Повторяющиеся элементы — выносить и переиспользовать
  • DOM-селекторы — стабильные и предсказуемые

Такая организация позволяет масштабировать Shepherd.js от простых onboarding-сценариев до сложных интерактивных систем обучения внутри приложения.