3 горизонтальные линии, бургер
3 горизонтальные линии, бургер
3 горизонтальные линии, бургер
3 горизонтальные линии, бургер

3 горизонтальные линии, бургер
Удалить все
ЗАГРУЗКА ...

Содержание



    Как добавить интерактивный туториал на сайт используя React

    Часы
    29.08.2024
    /
    Часы
    02.10.2025
    /
    Часы
    3 минуты
    Глазик
    494
    Сердечки
    0
    Соединённые точки
    0
    Соединённые точки
    0
    Соединённые точки
    0

    Вступление или для чего вообще нужен интерактивный туториал

    Привет, опять. В этой статье ты узнаешь как быстро и просто создать интерактивный туториал по вашему инструменту на Реакте. И первый вопрос который бы я задал, был бы такой: а нужен ли он вообще?
    И действительно, если функционал инструмента максимально простой и интуитивно понятный, то заморачиваться над туториалом не стоит. В идеале, к этому и нужно стремиться чтобы пользователь без всякой лишней мысли приступал к использованию твоего инструмента.
    Но как это бывает в реальной жизни, не все инструменты одно кнопочные и у многих большой и сложный функционал. Взять к примеру следующие веб-инструменты:
    1. Topvisor
    2. Google Analytics
    Нельзя так просто взять и пользоваться ими. Сначала, ты должен научиться ими пользоваться. В моём случае получился не очень интуитивно понятный инструмент. По этому будем делать встроенный туториал на сайт.

    Создание собственного ивента для запуска туториала

    У нас уже есть кнопка для вызова цепочки подсказок
    Нужно теперь её настроить так, чтобы эта цепочка работала. Сделаем мы это через создание собственного события. В файле Header.js замени:

    До

    export default function Header() { const header_buttons = document.getElementById('meta-header') const btns = [] for ( const btn of header_buttons.children){ const ref = btn.firstElementChild.getAttribute('href') if (btn.dataset.type == 'inner-link'){ btns.push(<Button color='primary'><a href={ref}>{btn.innerText}</a><LinkIcon className='mb-2' fontSize='small' /></Button>) } else if (btn.dataset.type == 'tutorial' ){ btns.push(<Button color='primary'><a href={ref}>{btn.innerText}</a><QuestionMarkIcon className='mb-2' fontSize='small'/></Button>) }

    После

    export default function Header() { const header_buttons = document.getElementById('meta-header') const btns = [] for ( const btn of header_buttons.children){ const ref = btn.firstElementChild.getAttribute('href') if (btn.dataset.type == 'inner-link'){ btns.push(<Button color='primary'><a href={ref}>{btn.innerText}</a><LinkIcon className='mb-2' fontSize='small' /></Button>) } else if (btn.dataset.type == 'tutorial' ){ btns.push(<Button id='onTutorial' onClick={(e)=>{ const onTutorialEvent = new Event('onTutorial') e.currentTarget.dispatchEvent(onTutorialEvent) }} color='primary'><a href={ref}>{btn.innerText}</a><QuestionMarkIcon className='mb-2' fontSize='small'/></Button>) }
    Мы добавили к кнопке айдишник + собственное событие на клик.

    Создание нового компонента Tutorial.js

    Теперь создадим новый компонент. В директории src/components создай файл Tutorial.js. После чего добавь туда следующий код:
    import * as React from 'react'; import { createRoot } from 'react-dom/client'; import Popover from '@mui/material/Popover'; export default function Hint({children, anchor}) { const [anchorEl, setAnchorEl] = React.useState(null); const [counter, setCounter] = React.useState(1); const [message, setMessage] = React.useState(''); const clearAllHints = () => { const hints = document.querySelectorAll('.tutorial_hint') hints.forEach( (hint_target) => { hint_target.style = '' if (hint_target.classList.contains('to_be_hidden')){ hint_target.classList.add('hidden') hint_target.classList.remove('to_be_hidden') } }) } const displayNextHint = () => { const hints = document.querySelectorAll('.tutorial_hint') hints.forEach( (hint_target) => { if (parseInt(hint_target.dataset.queue) == counter){ hint_target.style = 'background-color: orange' if (hint_target.classList.contains('hidden')){ hint_target.classList.remove('hidden') hint_target.classList.add('to_be_hidden') } var hint_msg = document.getElementById(`tutorial_hint_${hint_target.dataset.queue}`) setMessage(hint_msg.innerText) setAnchorEl(hint_target) } }) setCounter(counter + 1) } const handleClick = (event) => { clearAllHints() displayNextHint() }; let tutorial_button = document.getElementById('onTutorial') tutorial_button.addEventListener('onTutorial', handleClick, {once: true}) const handleClose = () => { const hints = document.querySelectorAll('.tutorial_hint') if (hints.length < counter){ setMessage('') setCounter(1) setAnchorEl(null); clearAllHints() } else{ clearAllHints() displayNextHint() } }; const open = Boolean(anchorEl); const id = open ? 'simple-popover' : undefined; return ( <Popover id={id} open={open} anchorEl={anchorEl} onClose={handleClose} anchorOrigin={{ vertical: 'bottom', horizontal: 'left', }} transformOrigin={{ vertical: 'top', horizontal: 'left', }} > {message} </Popover> ); } const popover = document.getElementById('popover-tutorial'); const root = createRoot(popover); root.render(<Hint></Hint>);
    Суть данного кода сводиться к тому, чтобы искать помеченные элементы с классом tutorial_hint и отрендерить подсказку рядом с этим элементом. Функцию на которую стоит обратить внимание это displayNextHint. Найдя все помеченные элементы, она находит элемент с ней связанный и берёт от туда текст подсказки. После чего выделяет элемент подсказки и показывает саму подсказку.
    Чтобы компонент смог отрендериться добавим ещё один элемент в app.html, прямо перед app_settings элементом.
    <div id="popover-tutorial"></div>
    И подключим новый компонент в index.js

    Помечаем элементы для подсказок

    Осталось только пометить необходимые элементы для подсказок.

    Файл AppActions.js

    export default function AppActions(){ const [isModal, setModal] = React.useState(false); const req = {'setModal': setModal} return ( <Box className="flex gap-1"> <div id="tutorial_hint_4" className="hidden"> Optional: Then you can save all of your configurations</div> <IconButton id='onSaveRequest' onClick={()=>{setModal(true); waitTillModalIsUp(SaveRequest, req)}} className='w-fit tutorial_hint' data-queue="4"><SaveIcon className=" border-2 rounded-md"/></IconButton> <Modal open={isModal} onClose={()=>{setModal(false)}} > <Box id='utils-modal' className="absolute top-1/2 left-1/2 -translate-x-2/4 -translate-y-2/4 shadow-md p-4 bg-white"> </Box> </Modal> <div id="tutorial_hint_3" className="hidden">Step 3: And now you can gather all informatino from SERP results</div> <IconButton id='onStartParsing' data-queue="3" onClick={()=>{StartParsingRequest(req)}} className='w-fit tutorial_hint'><NotStartedIcon className=" border-2 rounded-md"/></IconButton> </Box> ) }

    Файл AppQueries.js

    export default function AppQueries(){ … return ( <div> <Popover id={id} open={open} anchorEl={anchorEngine} onClose={handleClose} anchorOrigin={{ vertical: 'bottom', horizontal: 'left', }} > <List sx={{ width: '100%', maxWidth: 360, bgcolor: 'background.paper' }}> {engine_list} </List> </Popover> <div id="tutorial_hint_2" className="hidden">Step 2: Choose an required engine to parse. And then type your query</div> <IconButton onClick={(ev)=>handleClick(ev,engine_list)} className='w-fit tutorial_hint' data-queue="2"><AddBoxIcon/></IconButton> </div> ) }

    Файл AppSettings.js

    export default function AppSettings(){ ... return ( <div> <Paper elevation={2} className="z-10"> <div id="tutorial_hint_1" className="hidden" >Step 1: Check at least one checkbox, to choose what to save</div> <div id="settings_content" className='hidden'> <SettingsContent /> </div> </Paper> {/* Icon button is gonna be changed */} <IconButton onClick={ToggleSettings} className='w-fit tutorial_hint' data-queue="1"> { isSettings ? <CloseIcon className=" border-2 rounded-md"/> : <SettingsApplicationsIcon className=" border-2 rounded-md"/>} </IconButton> </div> ) }

    Файл AppUtils.js

    export default function AppUtils(){ ... return ( <Box className="flex flex-col"> <Box className="flex gap-1"> <div id="tutorial_hint_5" className="hidden">Here you can find the most popular and ready-to-use presets</div> <IconButton onClick={ToggleTrending(true)} className='w-fit border tutorial_hint' data-queue="5"><TrendingUpIcon className=" border-2 rounded-md"/></IconButton> <div id="tutorial_hint_6" className="hidden">Here you will find your own presets</div> <IconButton onClick={ToggleOwnSaves(true)} className='w-fit tutorial_hint' data-queue="6"><SavedSearchIcon className=" border-2 rounded-md"/></IconButton> <div id="tutorial_hint_7" className="hidden">Here you will see the actual proccess of gathering information</div> <div id="console-button" className='hidden tutorial_hint' data-queue="7"><IconButton onClick={ToggleConsole(true)} className='w-fit'><FeaturedPlayListIcon className=" border-2 rounded-md"/></IconButton></div> <div id="tutorial_hint_8" className="hidden">It is a link to a results of parsing proccess</div> <div id="results-button" className='hidden tutorial_hint' data-queue="8"><IconButton id="results-button-ref" href='#' onClick={()=>{}} className='w-fit'><DownloadIcon color='warning' className=" border-2 rounded-md"></DownloadIcon></IconButton></div> </Box> ... }

    Выводы или то как это выглядит

    По итогу всё это будет выглядеть как-то так:
    Можно и самому потыкаться на сайте.
    Если ты пропустил всё что было выше и хочешь просто получить проект в текущем состоянии, то ты можешь скачать его здесь. В следующей статье мы создадим возможность пользователям, входить и регистрироваться на нашем сайте.

    Не забудь поделиться, лайкнуть и оставить комментарий)

    Комментарии

    (0)

    captcha
    Отправить
    ЗАГРУЗКА ...
    Сейчас тут пусто. Буть первым (o゚v゚)ノ

    Другое

    Похожие статьи


    Серия статей о создании и продвижении SearchResultParser | Tim the webmaster

    Часы
    16.07.2024
    /
    Часы
    05.10.2025
    Глазик
    447
    Сердечки
    0
    Соединённые точки
    0
    Соединённые точки
    2
    Соединённые точки
    0
    Это статья-вступление и статья-навигатор по проекту/веб-инструменту SearchResultParser. Чему можно будет научиться и для кого эта серия статей

    Разработка фронтенда сайта на React с бэкендом на Django | SearchResultParser ч. 2

    Часы
    16.08.2024
    /
    Часы
    02.10.2025
    Глазик
    1337
    Сердечки
    0
    Соединённые точки
    0
    Соединённые точки
    0
    Соединённые точки
    0
    Показываю и рассказываю о том как разработать фронтен для сайта на Реакте с бэкендом на django. Использую MaterialUI и TailwindCSS, с исходным кодом и комментариями.

    Как добавить локализацию для django сайта (python, js, шаблоны и модели) ч. 5

    Часы
    06.02.2025
    /
    Часы
    02.10.2025
    Глазик
    5072
    Сердечки
    0
    Соединённые точки
    0
    Соединённые точки
    0
    Соединённые точки
    0
    В этой статье я покажу, как можно добавить локализацию и переводы для django сайта(i18n). Мы будем переводить Python, JS код, а так же шаблоны и django-модели. Плюс, переводы используя i18next …

    Использованные термины


    Релевантные вопросы