Недавно я искал библиотеки пользовательского интерфейса React, которые легко и расширяемо управляют темами. После небольшого сравнения Grommet в конечном итоге использовал одну из самых идеальных архитектур для стилей и тем компонентов. Grommet использует стилизованные компоненты (вместо объектно-ориентированного синтаксиса, такого как JSS), он правильно обрабатывает темы (маркеры дизайна + минимальные токены/стили компонентов) и имеет множество базовых компонентов, которые вам нужны (от кнопок до элементов ввода).
Я начал этот проект, потому что хотел использовать Grommet в качестве основы для системы дизайна и внести свой вклад в библиотеку. Однако документации для Grommet немного не хватает. И изучив исходный код, я не был доволен тем, как на самом деле код был задокументирован. Поэтому я решил создать более надежный веб-сайт с документацией, который был бы более инклюзивным для комментариев, содержал бы больше контента и работал бы в целом быстрее.
Текущее состояние документации Grommet
Документы содержат документацию только для каждого компонента в библиотеке. Есть также несколько других страниц, посвященных началу работы с Grommet или использованию браузера. Эти дополнительные страницы можно найти только на главной странице, и для доступа к ним нет навигации. Такие страницы, как темы, относятся к устаревшим вики-страницам на Github. Даже официальный пост Grommet на сайте HPE (Hewlett-Packert Enterprise) признает отсутствие документов:
Одним из недостатков является отсутствие обширной документации; в некоторых случаях он довольно легкий. — Джон Пол Мюллер, Блог HPE
Как в настоящее время работают документы Grommet?
Библиотека люверсов
- У каждого компонента есть файл
doc.js
, содержащий документацию в виде пользовательской библиотеки под названием react-desc, созданной Grommet. В этом файле компонент описан как стандартный тест Jest, с описанием и фрагментом кода/примером его использования. Затем также определяется каждый тип пропса (аналогично тому, как вы это делаете в обычном файле компонента), но они также переплетают дополнительные метаданные (например, описание и значение по умолчанию) с использованием библиотекиreact-desc
. - Инструментальный скрипт под названием
tools/generate-readme.js
сканирует все исходные файлы компонента иdoc.js
файлы. Он передал компоненты функцииdocs.js
. Результат функции docs преобразуется в Markdown, а затем передается в файл README, расположенный рядом с компонентом. Это создает файл Markdown с именем компонента, описанием, одним примером и таблицей реквизитов.
Документы по втулке
Это версия документации, которую можно увидеть на официальном веб-сайте v2 Grommet.
- Контент веб-сайта документации генерируется из репозитория
grommet-site
. Содержимое документации извлекается как зависимость отdoc.js
файлов (совмещенных с компонентами). - Файл
doc.js
содержит задокументированные значения реквизита компонента и реквизита темы (с описаниями и значениями по умолчанию):
import { describe, PropTypes } from 'react-desc';
import { colorPropType, genericProps, getAvailableAtBadge, hoverIndicatorPropType, themeDocUtils, } from '../../utils';
export const doc = Button => { const DocumentedButton = describe(Button) .availableAt(getAvailableAtBadge('Button')) .description('A button.') .details( `You can provide a single function child that will be called with 'hover' and 'focus' keys. This allows you to customize the rendering of the Button in those cases.`, ) .usage( `import { Button } from 'grommet'; <Button primary label='Label' />`, ) .intrinsicElement('button');
DocumentedButton.propTypes = { ...genericProps, active: PropTypes.bool .description('Whether the button is active.') .defaultValue(false), color: colorPropType.description( 'Fill color for primary, label color for plain, border color otherwise.', ), disabled: PropTypes.bool .description('Whether the button is disabled.') .defaultValue(false), fill: PropTypes.oneOfType([ PropTypes.oneOf(['horizontal', 'vertical']), PropTypes.bool, ]) .description( `Whether the button expands to fill all of the available width and/or height.`, ) .defaultValue(false), // ... Props Continued };
return DocumentedButton; };
export const themeDoc = { 'global.active.background.color': { description: 'The background color when using active prop.', type: 'string | { dark: string, light: string }', defaultValue: 'active', }, 'button.border.color': { description: `The color of the border.`, type: 'string | { dark: string, light: string }', }, // ... Theme Docs Continued ...themeDocUtils.focusStyle, ...themeDocUtils.disabledStyle, };
- Сайт документации построен с использованием пользовательской конфигурации Webpack.
- Страница документации каждого компонента создается вручную с помощью файла
<Router>
, и они перечислены в файлеsrc/screens/Components/index.js
. Это извлекает документацию по каждому отдельному компоненту (src/screens/Button.js
). - Компонент
<Doc>
используется для составления каждой страницы. Он принимает реквизиты, такие как имя, пример кода и т. д.
import React from 'react'; import { Box } from 'grommet'; import { doc, themeDoc } from 'grommet/components/Box/doc'; import Page from '../components/Page'; import Doc from '../components/Doc'; import { genericSyntaxes } from '../utils/props'; import Item from './Components/Item';
const desc = doc(Box).toJSON();
export default () => ( <Page> <Doc name="Box" desc={desc} code={`<Box direction="row" border={{ color: 'brand', size: 'large' }} pad="medium" > <Box pad="small" background="dark-3" /> <Box pad="medium" background="light-3" /> </Box>`} example={ <Box flex border={{ color: 'brand', size: 'large' }} pad="medium" /> } /> </Page> )
Проблемы с документами (и решения)
- Недостаточно примеров. Есть примеры, изложенные в документах компонентов (в репозитории
grommet-site
), но они фактически нигде не используются во внешнем интерфейсе. - Новый сайт документации будет включать больше примеров.
- MDX позволит писать примеры в более изолированном месте (вдали от логики страницы). И для описания примеров можно включить дополнительный текст.
- Очень явно. У каждого компонента должна быть своя созданная вручную страница в документах, он должен быть добавлен в список маршрутов, конфигурацию списка компонентов и страницу списка компонентов — есть всего несколько ручных точек, которые можно заменены автоматизированными или динамическими методами.
- Gatsby заменяет это на GraphQL и
gatsby-node
, которые могут генерировать страницы/маршруты из контента, который он импортирует в GraphQL. - Содержимое документации дополняется пользовательской библиотекой. Вместо того, чтобы соответствовать стандартному соглашению, такому как JSDoc, библиотека Grommet документирует исходный код, используя
react-desc
, пользовательскую интеграцию для Prop Types. Это сделало документацию реквизитов недоступной, если вы не создали адаптер или пользовательский импорт для специального синтаксиса. - Преобразование в формат JSDoc упрощает интеграцию с фреймворками (такими как Gatsby или Docz), поскольку JSDoc обычно интегрируется в фреймворки документации.
- Документы также написаны на JSX. Любая документация, написанная на JSX, недоступна для большинства. Требуется знание React, Javascript и JSX. И это усложняет чтение фактического содержимого документов через исходный код, поскольку все они основаны на компонентах React и синтаксисе HTML/JSX.
- MDX решит эту проблему, позволив пользователям писать более естественно с минимальным синтаксисом, но при этом иметь возможность включать более сложные элементы с использованием HTML или JSX.
- Разделить данные. Если мне нужно отредактировать документы для свойств компонента или значений темы, мне нужно перейти к исходному коду и отредактировать там файл
docs.js
. Или, если я хочу, мне нужно отредактировать соответствующий файл в репозиторииgrommet-site
. В идеале я должен иметь возможность хранить все документы вместе или извлекать их из одного источника. Будь то блоки JSDoc, PropTypes или MDX — при составлении документов должно быть меньше переключений контекста. Сайт документации должен просто извлекать все данные из исходного кода и содержать только логику для самих документов (макет, специфичные для документа компоненты, такие как блоки кода и т. д.). - Файлы MDX можно разместить вместе с исходным кодом компонента, что позволит изолировать все содержимое документации (реквизиты, значения темы, примеры и т. д.) в одном репозитории.
- Это позволяет вам включать другие библиотеки, такие как Storybook, например, которые могут использовать MDX через Storybook Docs. Вместо того, чтобы обращаться к веб-сайту документации, разработчики Storybook могут перейти к тому же содержимому README.
- Текущая навигация позволяет искать только то, что доступно. Он физически не позволит вам набирать буквы, если нет соответствующего ему компонента. Он кажется сломанным и отталкивает пользователя больше, чем обучает его. Также включает категории, которые ведут вас на страницу списка компонентов? Смущает вас относительно того, что является компонентом, а что нет.
- Версия 1 документации Grommet включала боковую панель навигации, которая была скрыта (с переключателем) на мобильных устройствах. Не уверен, почему от этого отказались в версии v2.
- Желателен счастливый брак этих двоих. Боковая панель для быстрого доступа на рабочем столе или удобного просмотра на мобильных устройствах, а также поиск для мобильных и опытных пользователей. Не уверен, включен ли он в настоящее время, но сочетание клавиш для доступа к поиску было бы фантастическим и сократило бы время навигации по сайту.
- Размер шрифта слишком велик. В частности, на рабочем столе он выглядит слишком большим. Дает главной странице эффект отсутствия текста контента, только заголовки. Страницы компонентов продолжаются бесконечно из-за размера табличного шрифта (используемого для заголовков, описаний и даже примеров кода).
- Нужно получить откат 10–20%.
- Версию документации версии 1 было труднее читать, шрифт был меньше и светлее, а цветовой контраст был плохим. Определенно шаг вперед с точки зрения разборчивости. Просто слишком большой.
Идеальная установка
- Описания PropTypes взяты из блоков комментариев.
Button.propTypes = { ...genericProps, /** * Whether the button is active. */ active: PropTypes.bool,
/** * Fill color for primary, label color for plain, border color otherwise. */ color: colorPropType,
/** Rest of props**/ } Button.defaultProps = { active: false, as: 'button', /** rest of props **/ }
- Примеры и любая дополнительная копия документации записываются в файлы MDX.
--- title: "Button" date: '2019-10-14' section: component ---
import { Button } from 'grommet';
You can provide a single function child that will be called with 'hover' and 'focus' keys. This allows you to customize the rendering of the Button in those cases.
```
jsx live <Button primary label="Label" />
```
## Active:
```
jsx live <Button active label="Submit" onClick={() => {}} />
```
- Токены дизайна, утилиты и т. д. отделены от определений компонентов. Прямо сейчас основная навигация по сайту — это просто список компонентов + маркеры дизайна (цвет, интервал и т. д.). Создается впечатление, что Color — это компонент, когда это просто страница документа для определения токенов дизайна.
- Здесь не нужно ничего менять. Гэтсби составляет список компонентов, читая папку
/src/js/components/
для любых файлов MDX. Поскольку компонента<Color>
нет, для него не будет создана страница в стиле «компонент», и он не будет сгруппирован аналогичным образом в списках. Вместо этого в документахdocs/src/pages/
создается новая «страница» MDX, содержащая документацию по токену цвета.
Честно говоря, это не слишком отличается от текущей настройки. Просто больше примеров, больше документации (по темам, общему использованию, архитектуре и семантике) и меньше кода — все это находится в лучшем стеке, который обеспечивает лучший UX на стороне интерфейса и вклада.
Как выполнить
- Удалите
react-desc
и замените на JSDocs. - Потому что
react-docgen
(новая библиотека синтаксического анализа документов) не собирает отдельные файлы с определениями PropType. Нужно объединить их обратно в основной файл. Переместите реквизит изdoc.js
в файл компонента под компонентом. - Скопируйте все описания и значения по умолчанию из формата
doc.js
иreact-desc
в блоки JSDoc с классическими типами реквизита. - Создайте файлы MDX в исходной папке каждого компонента с описанием, примерами и любой другой необходимой документацией.
- Создавайте файлы MDX для других тем документации, таких как темы, начало работы и т. д., в репозитории документации.
- Создайте навигацию, содержащую все страницы документа, от компонентов до отдельных страниц (например, начало работы).
- Отдельные компоненты в отдельный раздел?
- Новый дизайн документа с новым навигационным меню
- Удалите все автоматически сгенерированные файлы MD (см. ниже)
- Используйте эту настройку GatsbyJS для создания веб-сайта документации на основе репозитория пользовательского интерфейса Grommet.
- Эта установка настроена для вложения в папку Grommet для разработки. Вместо этого для производства Gatsby может извлекать содержимое из папки Grommet в модулях узлов.
Другие проблемы
После небольшого первоначального исследования и экспериментов с использованием Gatsby и Docz (шаблон документации, основанный на Gatsby), я начал замечать некоторые другие странные проблемы с кодовой базой.
- Получение MDX из случайных исходных файлов.
- Не позволяет Gatsby сканировать исходный код на наличие MD/MDX. Пустые/нулевые страницы вставляются в GraphQL.
- Я создал «проверку» (фильтр GraphQL в запросе) во время создания страницы Gatsby, чтобы гарантировать, что страницы не будут пустыми при создании, но он по-прежнему раздувает локальный GraphQL неточными данными, которые необходимо правильно фильтровать.
- MD неправильно сформирован
- Используя Docz, я смог обнаружить, что большая часть скомпилированного MD, сгенерированного в исходном коде Grommet, содержит ошибки. Они ссылаются на изображения или медиафайлы, которых не существует, или делают странные вещи, из-за которых парсер MD не работает.
- Пришлось сообщить об этом в репозиторий Docz.
- Не знаю, как это исправить, так как я не смог диагностировать определенные файлы, вызывающие проблему. Возможно, рукописные документы MDX (по сравнению с сгенерированными документами MD) будут лучше.
- Тест продолжает терпеть неудачу?
- Не удалось зафиксировать репозиторий, потому что более 100 тестов моментальных снимков не пройдены. Не уверен, что я делаю неправильно здесь, может быть полностью на моем конце. Ничего не менял в репозитории, кроме перемещения типов реквизита в компоненте Button, и, по-видимому, сломал несколько снапшотов других компонентов.
- Приходилось делать коммиты с включенным флагом
--no-verify
🔍 Finding changed files since git revision 28efecc43. 🎯 Found 2 changed files. ✍️ Fixing up src/js/components/Button/Button.mdx. ✅ Everything is awesome! FAIL docs/.cache/__tests__/loader.js ● Test suite failed to run /Users/ryo/Development/References/grommet/docs/.cache/__tests__/loader.js:3 import mock from "xhr-mock"; ^^^^^^ SyntaxError: Cannot use import statement outside a module at ScriptTransformer._transformAndBuildScript (node_modules/@jest/transform/build/ScriptTransformer.js:537:17) at ScriptTransformer.transform (node_modules/@jest/transform/build/ScriptTransformer.js:579:25) FAIL docs/.cache/__tests__/dev-loader.js ● Test suite failed to run /Users/ryo/Development/References/grommet/docs/.cache/__tests__/dev-loader.js:9 import mock from "xhr-mock"; ^^^^^^ SyntaxError: Cannot use import statement outside a module at ScriptTransformer._transformAndBuildScript (node_modules/@jest/transform/build/ScriptTransformer.js:537:17) at ScriptTransformer.transform (node_modules/@jest/transform/build/ScriptTransformer.js:579:25) FAIL src/js/components/Select/__tests__/Select-test.js (16.197s) ● Console console.error node_modules/prop-types/checkPropTypes.js:20 Warning: Failed prop type: Button: prop type `a11yTitle` is invalid; it must be a function, usually from the `prop-types` package, but received `object`. in Button (created by DropButton) in DropButton (created by DropButton) in DropButton (created by Context.Consumer) in StyledComponent (created by Select__StyledSelectDropButton) in Select__StyledSelectDropButton (created by Select) in Keyboard (created by Select) in Select (created by Select) in Select (created by Context.Consumer) in WithTheme(Select) console.error node_modules/prop-types/checkPropTypes.js:20 Warning: Failed prop type: Button: prop type `alignSelf` is invalid; it must be a function, usually from the `prop-types` package, but received `object`. in Button (created by DropButton) in DropButton (created by DropButton) in DropButton (created by Context.Consumer) in StyledComponent (created by Select__StyledSelectDropButton) in Select__StyledSelectDropButton (created by Select) in Keyboard (created by Select) in Select (created by Select) in Select (created by Context.Consumer) in WithTheme(Select) console.error node_modules/prop-types/checkPropTypes.js:20 Warning: Failed prop type: Button: prop type `gridArea` is invalid; it must be a function, usually from the `prop-types` package, but received `object`. in Button (created by DropButton) in DropButton (created by DropButton) in DropButton (created by Context.Consumer) in StyledComponent (created by Select__StyledSelectDropButton) in Select__StyledSelectDropButton (created by Select) in Keyboard (created by Select) in Select (created by Select) in Select (created by Context.Consumer) in WithTheme(Select) console.error node_modules/prop-types/checkPropTypes.js:20 Warning: Failed prop type: Button: prop type `margin` is invalid; it must be a function, usually from the `prop-types` package, but received `object`. in Button (created by DropButton) in DropButton (created by DropButton) in DropButton (created by Context.Consumer) in StyledComponent (created by Select__StyledSelectDropButton) in Select__StyledSelectDropButton (created by Select) in Keyboard (created by Select) in Select (created by Select) in Select (created by Context.Consumer) in WithTheme(Select) console.error node_modules/prop-types/checkPropTypes.js:20 Warning: Failed prop type: Button: prop type `color` is invalid; it must be a function, usually from the `prop-types` package, but received `object`. in Button (created by DropButton) in DropButton (created by DropButton) in DropButton (created by Context.Consumer) in StyledComponent (created by Select__StyledSelectDropButton) in Select__StyledSelectDropButton (created by Select) in Keyboard (created by Select) in Select (created by Select) in Select (created by Context.Consumer) in WithTheme(Select) console.error node_modules/prop-types/checkPropTypes.js:20 Warning: Failed prop type: Button: prop type `hoverIndicator` is invalid; it must be a function, usually from the `prop-types` package, but received `object`. in Button (created by DropButton) in DropButton (created by DropButton) in DropButton (created by Context.Consumer) in StyledComponent (created by Select__StyledSelectDropButton) in Select__StyledSelectDropButton (created by Select) in Keyboard (created by Select) in Select (created by Select) in Select (created by Context.Consumer) in WithTheme(Select) console.error node_modules/prop-types/checkPropTypes.js:20 Warning: Failed prop type: Invalid prop `target` of value `self` supplied to `Button`, expected one of ["_self","_blank","_parent","_top"]. in Button (created by DropButton) in DropButton (created by DropButton) in DropButton (created by Context.Consumer) in StyledComponent (created by Select__StyledSelectDropButton) in Select__StyledSelectDropButton (created by Select) in Keyboard (created by Select) in Select (created by Select) in Select (created by Context.Consumer) in WithTheme(Select) console.error node_modules/prop-types/checkPropTypes.js:20 Warning: Failed prop type: Invalid prop `icon` of type `array` supplied to `Button`, expected a single ReactElement. in Button (created by DropButton) in DropButton (created by DropButton) in DropButton (created by Context.Consumer) in StyledComponent (created by Select__StyledSelectDropButton) in Select__StyledSelectDropButton (created by Select) in Keyboard (created by Select) in Select (created by Select) in Select (created by Context.Consumer) in WithTheme(Select) console.warn src/js/components/Button/Button.js:59 Button should not have children if icon or label is provided console.warn src/js/components/Button/Button.js:59 Button should not have children if icon or label is provided console.warn src/js/components/Button/Button.js:59 Button should not have children if icon or label is provided console.warn src/js/components/Button/Button.js:59 Button should not have children if icon or label is provided console.warn src/js/components/Button/Button.js:59 Button should not have children if icon or label is provided console.warn src/js/components/Button/Button.js:59 Button should not have children if icon or label is provided console.warn src/js/components/Button/Button.js:59 Button should not have children if icon or label is provided console.warn src/js/components/Button/Button.js:59 Button should not have children if icon or label is provided console.warn src/js/components/Button/Button.js:59 Button should not have children if icon or label is provided console.warn src/js/components/Button/Button.js:59 Button should not have children if icon or label is provided console.warn src/js/components/Button/Button.js:59 Button should not have children if icon or label is provided console.warn src/js/components/Button/Button.js:59 Button should not have children if icon or label is provided console.warn src/js/components/Button/Button.js:59 Button should not have children if icon or label is provided console.warn src/js/components/Button/Button.js:59 Button should not have children if icon or label is provided console.warn src/js/components/Button/Button.js:59 Button should not have children if icon or label is provided console.warn src/js/components/Button/Button.js:59 Button should not have children if icon or label is provided console.warn src/js/components/Button/Button.js:59 Button should not have children if icon or label is provided console.warn src/js/components/Button/Button.js:59 Button should not have children if icon or label is provided console.warn src/js/components/Button/Button.js:59 Button should not have children if icon or label is provided console.warn src/js/components/Button/Button.js:59 Button should not have children if icon or label is provided console.warn src/js/components/Button/Button.js:59 Button should not have children if icon or label is provided console.warn src/js/components/Button/Button.js:59 Button should not have children if icon or label is provided console.warn src/js/components/Button/Button.js:59 Button should not have children if icon or label is provided console.warn src/js/components/Button/Button.js:59 Button should not have children if icon or label is provided console.warn src/js/components/Button/Button.js:59 Button should not have children if icon or label is provided console.warn src/js/components/Button/Button.js:59 Button should not have children if icon or label is provided console.warn src/js/components/Button/Button.js:59 Button should not have children if icon or label is provided console.warn src/js/components/Button/Button.js:59 Button should not have children if icon or label is provided console.warn src/js/components/Button/Button.js:59 Button should not have children if icon or label is provided console.warn src/js/components/Button/Button.js:59 Button should not have children if icon or label is provided console.warn src/js/components/Button/Button.js:59 Button should not have children if icon or label is provided console.warn src/js/components/Button/Button.js:59 Button should not have children if icon or label is provided console.warn src/js/components/Button/Button.js:59 Button should not have children if icon or label is provided console.warn src/js/components/Button/Button.js:59 Button should not have children if icon or label is provided console.warn src/js/components/Button/Button.js:59 Button should not have children if icon or label is provided console.warn src/js/components/Button/Button.js:59 Button should not have children if icon or label is provided console.warn src/js/components/Button/Button.js:59 Button should not have children if icon or label is provided ● Select › basic expect(received).toMatchSnapshot() Snapshot name: `Select basic 1` - Snapshot + Received @@ -1,39 +1,5 @@ - .c8 { - display: inline-block; - -webkit-flex: 0 0 auto; - -ms-flex: 0 0 auto; - flex: 0 0 auto; - width: 24px; - height: 24px; - fill: #7D4CDB; - stroke: #7D4CDB; - } - - .c8 g { - fill: inherit; - stroke: inherit; - } - - .c8 *:not([stroke])[fill="none"] { - stroke-width: 0; - } - - .c8 *[stroke*="#"], - .c8 *[STROKE*="#"] { - stroke: inherit; - fill: none; - } - - .c8 *[fill-rule], - .c8 *[FILL-RULE], - .c8 *[fill*="#"], - .c8 *[FILL*="#"] { - fill: inherit; - stroke: none; - } - .c2 { display: -webkit-box; display: -webkit-flex; display: -ms-flexbox; display: flex; @@ -47,56 +13,16 @@ min-width: 0; min-height: 0; -webkit-flex-direction: row; -ms-flex-direction: row; flex-direction: row; - -webkit-box-pack: justify; - -webkit-justify-content: space-between; - -ms-flex-pack: justify; - justify-content: space-between; - } - - .c3 { - display: -webkit-box; - display: -webkit-flex; - display: -ms-flexbox; - display: flex; - box-sizing: border-box; - outline: none; - min-width: 0; - min-height: 0; - -webkit-flex-direction: row; - -ms-flex-direction: row; - flex-direction: row; - -webkit-flex: 1 1; - -ms-flex: 1 1; - flex: 1 1; - -webkit-flex-basis: auto; - -ms-flex-preferred-size: auto; - flex-basis: auto; + -webkit-box-pack: center; + -webkit-justify-content: center; + -ms-flex-pack: center; + justify-content: center; } - .c7 { - display: -webkit-box; - display: -webkit-flex; - display: -ms-flexbox; - display: flex; - box-sizing: border-box; - outline: none; - max-width: 100%; - margin-left: 12px; - margin-right: 12px; - min-width: 0; - min-height: 0; - -webkit-flex-direction: column; - -ms-flex-direction: column; - flex-direction: column; - -webkit-flex: 0 0 auto; - -ms-flex: 0 0 auto; - flex: 0 0 auto; - } - .c0 { display: inline-block; box-sizing: border-box; cursor: pointer; outline: none; @@ -105,130 +31,48 @@ text-decoration: none; margin: 0; background: transparent; overflow: visible; text-transform: none; - color: inherit; - border: none; - padding: 0; - text-align: inherit; + border: 2px solid #7D4CDB; + border-radius: 18px; + color: #444444; + padding: 4px 22px; + font-size: 18px; + line-height: 24px; + -webkit-transition-property: color, background-color, border-color, box-shadow; + transition-property: color, background-color, border-color, box-shadow; + -webkit-transition-duration: 0.1s; + transition-duration: 0.1s; + -webkit-transition-timing-function: ease-in-out; + transition-timing-function: ease-in-out; } - .c5 { - box-sizing: border-box; - font-size: inherit; - font-family: inherit; - border: none; - -webkit-appearance: none; - padding: 11px; - outline: none; - background: transparent; - color: inherit; - font-weight: 600; - margin: 0; - border: 1px solid rgba(0,0,0,0.33); - border-radius: 4px; - width: 100%; - border: none; + .c0:hover { + box-shadow: 0px 0px 0px 2px #7D4CDB; } - .c5::-webkit-search-decoration { - -webkit-appearance: none; - } - - .c5::-webkit-input-placeholder { - color: #AAAAAA; - } - - .c5::-moz-placeholder { - color: #AAAAAA; - } - - .c5:-ms-input-placeholder { - color: #AAAAAA; - } - - .c5::-moz-focus-inner { - border: none; - outline: none; - } - - .c4 { - position: relative; - width: 100%; - } - - .c6 { - cursor: pointer; - } - .c1 { border: 1px solid rgba(0,0,0,0.33); border-radius: 4px; } - @media only screen and (max-width:768px) { - .c7 { - margin-left: 6px; - margin-right: 6px; - } - } - <button aria-label="Open Drop" className="c0 c1" + disabled={false} + href="#" id="test-select" onBlur={[Function]} onClick={[Function]} onFocus={[Function]} onKeyDown={[Function]} onMouseOut={[Function]} onMouseOver={[Function]} - type="button" + target="self" > <div className="c2" > - <div - className="c3" - > - <div - className="c4" - > - <input - autoComplete="off" - className="c5 c6" - id="test-select__input" - onBlur={[Function]} - onChange={[Function]} - onClick={[Function]} - onFocus={[Function]} - onKeyDown={[Function]} - readOnly={true} - tabIndex="-1" - type="text" - /> - </div> - </div> - <div - className="c7" - style={ - Object { - "minWidth": "auto", - } - } - > - <svg - aria-label="FormDown" - className="c8" - viewBox="0 0 24 24" - > - <polyline - fill="none" - points="18 9 12 15 6 9" - stroke="#000" - strokeWidth="2" - /> - </svg> - </div> + Text </div> </button> 19 | <Select id="test-select" options={['one', 'two']} />, 20 | ); > 21 | expect(component.toJSON()).toMatchSnapshot(); | ^ 22 | }); 23 | 24 | test('opens', done => { at Object.<anonymous> (src/js/components/Select/__tests__/Select-test.js:21:32) ```
- Проблемы анализа.
- После неудачных тестов у меня возникли проблемы с правильным форматированием кода в соответствии со стандартами linting. Это приведет к сбою таких правил, как экспорт по умолчанию (
export Button from "./Button"
против импорта, а затем экспорта), несмотря на то, что они включены в конфигурации репо? Может быть, моя собственная конфигурация линтинга в VSCode переопределяет конфигурацию по умолчанию? - Не проблема, но попытался использовать typedoc для генерации JSON для свойств компонентов (а не JSDocs с типами свойств). Вы можете использовать JSON, сгенерированный typedoc в Gatsby, для отображения таблицы свойств для компонента.
- Работает, но требует, чтобы typedoc был установлен в том же репозитории, что и Grommet, поскольку для этого требуются все зависимости, которые использует Grommet.
- Также требуется ручная сортировка/фильтрация через JSON. Лучше использовать исходный плагин, который адаптирует данные к GraphQL для лучшего запроса/фильтрации (или потребует от меня адаптации JSON к узлам, больше работы).
Нерешенные функции
- Реквизиты значения темы. В настоящее время они находятся в файле
doc.js
как экспортируемая переменная в формате объекта. Они содержат не только значения темы для конкретного компонента, но и то, какие глобальные значения темы применяются к компоненту. Это очень помогает при создании тем. - Решение?: Измените
doc.js
наtheme-docs.json
. Содержит только значения темы, больше никаких типов реквизита, поскольку они расположены вместе с кодом компонента. Импортируйте JSON в Gatsby с помощью подключаемого модуля преобразования JSON. Затем на страницах компонентов запросите JSON через GraphQL и отобразите в формате таблицы. - Решение?: Используйте определение Typescript структуры темы для создания страницы с «тематической переменной». Он будет содержать все значения тем, их типы и любые описания из JSDocs/блоков комментариев. Не решает проблему отображения того, какие компоненты используют глобальные значения (и какие).
- Функции поиска
- Мы можем искать такие компоненты, как текущий веб-сайт, но поиск по всему контенту обычно требует интеграции с Algolia.
- Интернационализация? Как осуществляется перевод, особенно если документы JSDoc тесно связаны с исходным кодом?
- Посмотрите, как React обрабатывает международные документы.
Результат
После дня возни или около того я набросал это доказательство концепции т, используя Gatsby, MDX, React-Docgen для создания документации на основе слегка модифицированной библиотеки Grommet. Я преобразовал один компонент Grommet (<Button>
) из использования react-desc
в JSDocs. Все остальное происходит внутри Гэтсби.
В нем не представлены все страницы компонентов или новый контент (например, тематические документы). Это требует немного больше времени или, в идеале, определяется/обсуждается сообществом. Однако я подумал, что этот прототип проиллюстрирует изменения архитектуры, которые я предлагаю.
Документы по сборке Гэтсби
Когда вы запускаете процесс сборки Gatsby, его «исходные» плагины проверяют каталог Grommet /src/js/
на наличие любых компонентов React и файлов MDX. Затем Gatsby запускает любые подключаемые модули «преобразователя», которые считывают любые импортированные данные (JS и MDX) и анализируют их в узлах и конечных точках GraphQL. Затем создаются страницы — некоторые из них статичны, а другие создаются динамически в gatsby-node.js
с использованием запросов GraphQL. Большинство страниц являются динамическими, как компоненты и содержимое MDX, которые запускаются через шаблоны страниц в /docs/src/templates/
. Эти шаблоны страниц используют GraphQL для запроса своих данных и передачи их соответствующим компонентам.
Конечным продуктом является статический PWA, который предлагает такие функции, как предварительная загрузка страниц, автономная поддержка и оптимизация изображений. Я также добавил поддержку живого кодирования в другие примеры. Таким образом, пользователям не нужно загружать новую CodeSandbox для каждого эксперимента, который они хотят протестировать (что может быть трудоемким и ненужным, если вы уже сохранили документы в автономном режиме).
Развертывание документации в рабочей среде также потребует производственного развертывания документации по исходному коду библиотеки пользовательского интерфейса (поскольку в документах Gatsby используется Grommet в качестве зависимости от NPM и извлекается производственный контент оттуда). Вот почему живая демонстрация развертывается на Netlify с использованием статического процесса сборки, который загружается вручную, а не развертывается через git commit.
Сравнение производительности
Давайте сравним эти документы Gatsby с текущей итерацией документации Grommet v2. Эти метрики были созданы Lighthouse, работающим на MacBook Pro, с настройками Mobile и Simulated 4G с 4-кратным замедлением процессора. Я почти уверен, что оба сайта размещены на Netlify, что делает его отличным базовым CDN.
Загрузка страницы
Документы Втулка v2:
- Первая содержательная отрисовка: 3,1 с.
- Первая осмысленная краска: 10,7 с.
- Время до интерактивности 10,9 с
Документы прототипа Гэтсби:
- Первая содержательная отрисовка: 0,9 с.
- Первая осмысленная краска: 0,9 с.
- Время до интерактивности 0,9 с
Имейте в виду, что документы Gatsby не поддерживают автономный режим и не содержат такого же количества страниц, как сайт Grommet.
В заключение: пост Гэтсби 🐇💨
Очень здорово видеть видимую разницу между двумя сайтами документации. Это действительно показывает, что вы можете собрать свою собственную конфигурацию Webpack (и это всегда впечатляет), но иногда лучше оставить такую архитектуру для таких фреймворков, как Gatsby. Он поставляется с предварительно подготовленными отличными и разумными настройками по умолчанию, которые обеспечивают невероятно быструю работу в Интернете.
Я открыл выпуск Github в репозитории Grommet здесь, в котором обсуждаются эти изменения. Если вы заинтересованы в том, чтобы внести свой вклад в это (или помочь с моими проблемами перед фиксацией/анализом), проверьте это здесь.
А если у вас есть какие-либо вопросы, комментарии или отзывы по этой теме — пишите мне в Twitter 📬 Всегда интересно услышать, что вы скажете!
Канпай 🍻
Рё