Недавно я искал библиотеки пользовательского интерфейса 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 📬 Всегда интересно услышать, что вы скажете!

Канпай 🍻
Рё