{"id":21611709,"url":"https://github.com/ktsstudio/frontend-hw-2","last_synced_at":"2026-02-10T23:32:35.787Z","repository":{"id":86391399,"uuid":"600710825","full_name":"ktsstudio/frontend-hw-2","owner":"ktsstudio","description":null,"archived":false,"fork":false,"pushed_at":"2024-10-25T15:33:55.000Z","size":5237,"stargazers_count":2,"open_issues_count":0,"forks_count":1,"subscribers_count":6,"default_branch":"main","last_synced_at":"2025-08-03T15:38:41.439Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ktsstudio.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2023-02-12T10:57:57.000Z","updated_at":"2025-05-06T07:47:56.000Z","dependencies_parsed_at":null,"dependency_job_id":"83b7a7fb-a64e-43a0-a426-dc4925c37c96","html_url":"https://github.com/ktsstudio/frontend-hw-2","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/ktsstudio/frontend-hw-2","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ktsstudio%2Ffrontend-hw-2","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ktsstudio%2Ffrontend-hw-2/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ktsstudio%2Ffrontend-hw-2/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ktsstudio%2Ffrontend-hw-2/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ktsstudio","download_url":"https://codeload.github.com/ktsstudio/frontend-hw-2/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ktsstudio%2Ffrontend-hw-2/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29321368,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-10T20:44:44.282Z","status":"ssl_error","status_checked_at":"2026-02-10T20:44:43.393Z","response_time":65,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-24T21:13:38.695Z","updated_at":"2026-02-10T23:32:35.748Z","avatar_url":"https://github.com/ktsstudio.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Начинающий React разработчик. Домашнее задание №2\n\nВ данном домашнем задании Вам необходимо реализовать React-компоненты для вашего будущего проекта.\n\n[Макеты](https://www.figma.com/file/x10xReXUxSisyzDMjC0NPL/%D0%9F%D1%80%D0%BE%D0%B5%D0%BA%D1%82%D1%8B-UIKit?type=design\u0026node-id=0%3A1\u0026mode=design\u0026t=gXpqhNmAZawkzsB5-1)\n\n**Требования:**\n\n1. Все цвета и отступы должны быть заданы в переменных (указаны в макете)\n   ![Переменные](./public//styles.png)\n\n## Запуск тестов\n\nДля запуска **всех тестов** выполняйте команду:\n\n```\nyarn test\n```\n\nДля запуска теста **отдельного компонента**:\n\n```\nyarn test:single \u003cназвание компонента\u003e\n```\n\nНапример:\n\n```\nyarn test:single Card\n```\n\n### Как запустить скриншотный тест для отдельного компонента\n\n- В WebStorm есть опция запуска теста внутри файла теста (иконка play слева от кода).\n- В VSCode можно установить расширение Jest, которое позволяет запускать отдельные скриншотные тесты из меню расширения Jest. После установки зависимостей перезапустите VSCode, чтобы расширение проиндексировало файлы тестов.\n\n\u003e При этом необходимо на время закомментировать те тест-кейсы, которые не требуется прогонять (вызовы функции `screenshotTesting`). **Важно всё раскомментировать перед финальной общей проверкой и пушем в репозиторий.**\n\n## 1. Loader\n\nРеализуйте компонент Лоадер\n\n```typescript\ntype LoaderProps = {\n  /** Размер */\n  size?: 's' | 'm' | 'l';\n  /** Дополнительный класс */\n  className?: string;\n};\n```\n\n**Примеры использования:**\n\n```typescript\n\u003cLoader /\u003e  // стандартный лоадер\n\n\u003cLoader size=\"l\" /\u003e  // лоадер размера L\n```\n\n## 2. Text\n\nРеализуйте компонент Text\n\n**Требования:**\n\n1. По умолчанию должен иметь цвет родителя\n1. Пропс `weight` имеет больший приоритет чем `view`\n1. При указании `tag` рендерится соответствующий тег, по умолчанию `p`\n\n```typescript\ntype TextProps = {\n  /** Дополнительный класс */\n  className?: string;\n  /** Стиль отображения */\n  view?: 'title' | 'button' | 'p-20' | 'p-18' | 'p-16' | 'p-14';\n  /** Html-тег */\n  tag?: 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6' | 'div' | 'p' | 'span';\n  /** Начертание шрифта */\n  weight?: 'normal' | 'medium' | 'bold';\n  /** Контент */\n  children: React.ReactNode;\n  /** Цвет */\n  color?: 'primary' | 'secondary' | 'accent';\n  /** Максимальное кол-во строк */\n  maxLines?: number;\n};\n```\n\n**Примеры использования:**\n\n```typescript\n\u003cText tag=\"p\" weight=\"medium\"\u003eЖирный текст\u003c/Text\u003e\n\u003cText tag=\"h1\" view=\"title\" maxLines={2}\u003eЗаголовок\u003c/Text\u003e\n```\n\n## 3. Icons\n\nРеализуйте компоненты иконок. (Назвать их CheckIcon и ArrowDownIcon)\n\n1. Все компоненты иконок лежат в директории `components/icons/` (Пример импорта: `import CheckIcon from 'components/icons/CheckIcon'`)\n1. Все компоненты иконок имеют одинаковый базовый набор пропсов `IconProps`. (Лучше всего сделать компонент-обертку `Icon` и использовать в компонентах иконок, но это не обязательно).\n1. По умолчанию имеют цвет родителя а при указании `color`, красятся в указанный стиль цвета.\n1. Можно указать ширину и высоту иконки (по умолчанию 24px)\n\n```typescript\ntype IconProps = React.SVGAttributes\u003cSVGElement\u003e \u0026 {\n  className?: string;\n  color?: 'primary' | 'secondary' | 'accent';\n};\n```\n\n**Примеры использования:**\n\n```typescript\n\u003cCheckIcon width={40} height={40} /\u003e\n\u003cArrowDownIcon color=\"accent\" /\u003e\n```\n\n## 4. Button\n\nРеализуйте компонент Кнопка\n\n**Требования:**\n\n1. Кнопка использует html-тег button и принимает все его пропсы\n1. Кнопка принимает пропсы ButtonProps и удовлетворяет их требованиям, описанным ниже\n1. Текст кнопки/дочерний элемент передается в качестве `children`\n1. При передаче дополнительного `className` не должны сбрасываться внутренние (описанные вами в стилях) классы кнопки\n1. Компонент должен быть реактивным, то есть реагировать на изменение любых пропсов\n1. Для управления классами необходимо использовать библиотеку `classnames`\n1. При loading=true, на кнопке должен появляться атрибут disabled\n\n```typescript\ntype ButtonProps = React.ButtonHTMLAttributes\u003cHTMLButtonElement\u003e \u0026 {\n  /** Состояние загрузки */\n  loading?: boolean;\n  /** Текст кнопки */\n  children: React.ReactNode;\n};\n```\n\n**Примеры использования:**\n\n```typescript\n// Кнопка с текстом \"Отправить\", логирующая в консоль \"Письмо отправлено\" при клике\n\u003cButton onClick={() =\u003e console.log('Письмо отправлено')}\u003e\n  Отправить\n\u003c/Button\u003e\n\n// Кнопка, отображающая компонент Loader при загрузке каких-то данных\n\u003cButton\n  loading={isLoading}\n\u003e\n  Отправить\n\u003c/Button\u003e\n\n// Кнопка с элементом в качестве содержимого\n\u003cButton\u003e\n  \u003cspan\u003eМодная кнопка\u003c/span\u003e\n\u003c/Button\u003e\n\n// Заблокированная кнопка с дополнительным классом\n\u003cButton className=\"some-outer-class\" disabled\u003e\n  Отправить\n\u003c/Button\u003e\n\n// Кнопка с пропсом нативной кнопки\n\u003cButton onMouseOver={() =\u003e console.log('Убери от меня курсор!')}\u003e\n  Отправить\n\u003c/Button\u003e\n```\n\n## 5. Card\n\nРеализуйте компонент Карточка (Элемент списка)\n\n**Требования:**\n\n1. Для изображения используется html-тег `img`\n1. В заголовке может быть максимум 2 строки\n1. В описании может быть максимум 3 строки\n1. Контент над заголовком необязательный (кол-во строк не ограничено)\n1. При клике на карточку должен выполняться `onClick`\n1. Для текстов используется компонент `Text`\n1. При расширении/сужении карточки, изображение должно сохранять пропорции\n\n```typescript\ntype CardProps = {\n  /** Дополнительный classname */\n  className?: string;\n  /** URL изображения */\n  image: string;\n  /** Слот над заголовком */\n  captionSlot?: React.ReactNode;\n  /** Заголовок карточки */\n  title: React.ReactNode;\n  /** Описание карточки */\n  subtitle: React.ReactNode;\n  /** Содержимое карточки (футер/боковая часть), может быть пустым */\n  contentSlot?: React.ReactNode;\n  /** Клик на карточку */\n  onClick?: React.MouseEventHandler;\n  /** Слот для действия */\n  actionSlot?: React.ReactNode;\n};\n```\n\n**Примеры использования:**\n\n```typescript\n\u003cCard\n    image=\"https://interactive-examples.mdn.mozilla.net/media/cc0-images/grapefruit-slice-332-332.jpg\"\n    title=\"Мандарин\"\n    subtitle=\"Марокко\"\n    onClick={() =\u003e console.log('Мандарин куплен!')}\n/\u003e\n\n\u003cCard\n    image=\"https://interactive-examples.mdn.mozilla.net/media/cc0-images/grapefruit-slice-332-332.jpg\"\n    title=\"Мандарин\"\n    subtitle={\u003ca href=\"/morocco\"\u003eМарокко\u003c/a\u003e}\n    contentSlot={\u003c\u003e299р\u003c/\u003e}\n    actionSlot={\u003cButton\u003eВ корзину\u003c/Button\u003e}\n/\u003e\n```\n\n## 7. Input\n\nРеализуйте компонент Поле ввода\n\n**Требования:**\n\n1. Необходимо использовать html-тег `input`\n2. Должен быть слот для иконки справа\n\n```typescript\ntype InputProps = Omit\u003c\n  React.InputHTMLAttributes\u003cHTMLInputElement\u003e,\n  'onChange' | 'value'\n\u003e \u0026 {\n  /** Значение поля */\n  value: string;\n  /** Callback, вызываемый при вводе данных в поле */\n  onChange: (value: string) =\u003e void;\n  /** Слот для иконки справа */\n  afterSlot?: React.ReactNode;\n};\n```\n\n**Примеры использования:**\n\n```typescript\n// Простое поле\n\u003cInput\n    value=\"Кто такой биткоин?\"\n    onChange={(value: string) =\u003e console.log(value)}\n/\u003e\n\n// Заблокированное поле с плейсхолдером\n\u003cInput\n    value=\"\"\n    onChange={(value: string) =\u003e console.log(value)}\n    placeholder=\"Начните набирать свой вопрос\"\n    disabled\n/\u003e\n\n// Поле с иконкой\n\u003cInput\n  value=\"Кто такой биткоин?\"\n  onChange={(value: string) =\u003e console.log(value)}\n  afterSlot={\u003cArrowDownIcon color=\"secondary\" /\u003e\n/\u003e\n```\n\n## 8. MultiDropdown\n\nРеализуйте компонент Выпадающий список с множественным выбором (Фильтр).\n\n**Требования:**\n\n1. Должен использовать компонент `Input`\n1. При вводе в поле, опции должны фильтроваться\n1. Опции должны пропадать из DOM-дерева при клике вне поля\n\n```typescript\ntype Option = {\n  /** Ключ варианта, используется для отправки на бек/использования в коде */\n  key: string;\n  /** Значение варианта, отображается пользователю */\n  value: string;\n};\n\n/** Пропсы, которые принимает компонент Dropdown */\ntype MultiDropdownProps = {\n  className?: string;\n  /** Массив возможных вариантов для выбора */\n  options: Option[];\n  /** Текущие выбранные значения поля, может быть пустым */\n  value: Option[];\n  /** Callback, вызываемый при выборе варианта */\n  onChange: (value: Option[]) =\u003e void;\n  /** Заблокирован ли дропдаун */\n  disabled?: boolean;\n  /** Возвращает строку которая будет выводится в инпуте. В случае если опции не выбраны, строка должна отображаться как placeholder. */\n  getTitle: (value: Option[]) =\u003e string;\n};\n```\n\n**Примеры использования:**\n\n```typescript\n// Простой фильтр\n\u003cMultiDropdown\n    options={[\n        { key: 'msk', value: 'Москва' },\n        { key: 'spb', value: 'Санкт-Петербург' },\n        { key: 'ekb', value: 'Екатеринбург' }\n    ]}\n    value={[{ key: 'msk', value: 'Москва' }]}\n    onChange={({ key, value }: Option) =\u003e console.log('Выбрано:', key, value)}\n    getTitle={() =\u003e ''}\n/\u003e\n\n// Заблокированный фильтр\n\u003cMultiDropdown\n    disabled\n    options={someOptions}\n    value={currentValue}\n    onChange={onChange}\n    getTitle={(values: Option[]) =\u003e values.length === 0 ? 'Выберите город' : `Выбрано: ${values.length}`}\n/\u003e\n\n// Фильтр, отображающий количество выбранных вариантов\n\u003cMultiDropdown\n    options={someOptions}\n    value={currentValue}\n    onChange={onChange}\n    getTitle={(values: Option[]) =\u003e `Выбрано: ${values.length}`}\n/\u003e\n```\n\n## 9. CheckBox\n\nРеализуйте компонент Чекбокс\n\n**Требования:**\n\n1. Необходимо использовать html-тег `input` с типом \"чекбокс\"\n\n```typescript\ntype CheckBoxProps = Omit\u003c\n  React.InputHTMLAttributes\u003cHTMLInputElement\u003e,\n  'onChange'\n\u003e \u0026 {\n  /** Вызывается при клике на чекбокс */\n  onChange: (checked: boolean) =\u003e void;\n};\n```\n\n**Примеры использования:**\n\n```typescript\n// Простой чекбокс\n\u003cCheckBox\n  checked={checked}\n  onChange={setChecked}\n/\u003e\n\n// Заблокированный чекбокс\n\u003cCheckBox\n  disabled\n  checked={checked}\n  onChange={setChecked}\n/\u003e\n```\n\n## Перед отправкой ДЗ на проверку\n\n1. Укажите личный ключ `user_token` в файле `config.yml`.\n   Пример `config.yml`:\n\n```\nuser_token: e3631261-c636-42458-ab0b-g8e534e984ee\n```\n\n2. Выполните команду запуска тестов\n\n```\nyarn test\n```\n\n3. Если не прошел визуальный тест `screenshot.test.ts`\n\n- Обновите Google Chrome до последне стабильной версии (\u003e=110.0.5478.0)\n- Убедитесь что не меняли файлы \\*.stories.tsx\n- Посмотрите различия в папке `src/__test__/__image_snapshots__/\u003cНАЗВАНИЕ КОМПОНЕНТА\u003e/__diff_output__` и исправьте их\n\n4. При успешном прохождении тестов, отправьте изменения в свой репозиторий\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fktsstudio%2Ffrontend-hw-2","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fktsstudio%2Ffrontend-hw-2","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fktsstudio%2Ffrontend-hw-2/lists"}