Showing preview only (2,604K chars total). Download the full file or copy to clipboard to get everything.
Repository: freee/vibes
Branch: main
Commit: 94af08a3974e
Files: 594
Total size: 2.2 MB
Directory structure:
gitextract_1r4gz165/
├── .babelrc
├── .circleci/
│ └── config.yml
├── .dockerignore
├── .editorconfig
├── .eslintignore
├── .eslintrc.js
├── .flowconfig
├── .github/
│ ├── PULL_REQUEST_TEMPLATE.md
│ └── workflows/
│ ├── publish_package_to_npmjs.yml
│ ├── publish_storybook.yml
│ └── pull_request_test_and_lint.yml
├── .gitignore
├── .husky/
│ └── pre-commit
├── .jest/
│ ├── Mock.js
│ └── setup.js
├── .node-version
├── .npmrc
├── .prettierignore
├── .storybook/
│ ├── main.ts
│ ├── manager.js
│ └── preview.tsx
├── Dockerfile
├── LICENSE.txt
├── Makefile
├── README.md
├── __tests__/
│ └── types/
│ └── flow/
│ ├── .flowconfig
│ ├── lv1.js
│ ├── lv2.js
│ └── utilities.js
├── docker/
│ └── git-credential-github-token
├── docs/
│ ├── Colors.stories.mdx
│ ├── Contribution.stories.mdx
│ ├── Design/
│ │ ├── Layout/
│ │ │ └── Layout.stories.mdx
│ │ └── Readme.stories.mdx
│ ├── Readme.stories.mdx
│ ├── Storybook.stories.mdx
│ ├── Stylesheets.stories.mdx
│ └── TypeScript.stories.mdx
├── examples/
│ ├── Collection.mdx
│ ├── Collection.stories.tsx
│ ├── Forms.mdx
│ ├── Forms.stories.tsx
│ ├── ImportWizard.mdx
│ ├── ImportWizard.stories.tsx
│ ├── Pages.stories.tsx
│ ├── ResponsiveLayout.mdx
│ ├── ResponsiveLayout.stories.tsx
│ ├── ThroughCommonProps.mdx
│ └── ThroughCommonProps.stories.tsx
├── index.d.ts
├── index.js
├── index.js.flow
├── injectFileName.js
├── jest.config.js
├── lv1.js.flow
├── lv2.js.flow
├── package.json
├── scripts/
│ └── release.js
├── src/
│ ├── @types/
│ │ ├── image.d.ts
│ │ ├── mdx.d.ts
│ │ └── redux.d.ts
│ ├── constants/
│ │ ├── Color.ts
│ │ ├── Font.ts
│ │ ├── Size.ts
│ │ ├── ZIndex.ts
│ │ └── index.ts
│ ├── hooks/
│ │ └── useUniqueId.ts
│ ├── index.ts
│ ├── internal/
│ │ └── CommonStyle.ts
│ ├── lv1/
│ │ ├── InlineSpinner.stories.tsx
│ │ ├── InlineSpinner.tsx
│ │ ├── Loading/
│ │ │ ├── loading-parts.json
│ │ │ └── loading-whole-2021.json
│ │ ├── Loading.stories.tsx
│ │ ├── Loading.tsx
│ │ ├── a11y/
│ │ │ ├── FocusHighlight.stories.tsx
│ │ │ ├── FocusHighlight.tsx
│ │ │ ├── FocusTrap.stories.tsx
│ │ │ ├── FocusTrap.tsx
│ │ │ ├── VisuallyHidden.stories.tsx
│ │ │ └── VisuallyHidden.tsx
│ │ ├── bases/
│ │ │ ├── Balloon.stories.tsx
│ │ │ ├── Balloon.tsx
│ │ │ ├── CardBase.stories.tsx
│ │ │ ├── CardBase.tsx
│ │ │ ├── ColumnBase.stories.tsx
│ │ │ ├── ColumnBase.tsx
│ │ │ ├── Container.stories.tsx
│ │ │ ├── Container.tsx
│ │ │ ├── ContentsBase.stories.tsx
│ │ │ ├── ContentsBase.tsx
│ │ │ ├── DialogBase.stories.tsx
│ │ │ ├── DialogBase.tsx
│ │ │ ├── FloatingBase.stories.tsx
│ │ │ ├── FloatingBase.tsx
│ │ │ ├── MarginBase.stories.tsx
│ │ │ ├── MarginBase.tsx
│ │ │ ├── NegativeContentsBase.stories.tsx
│ │ │ ├── NegativeContentsBase.tsx
│ │ │ ├── NegativeMarginBase.stories.tsx
│ │ │ ├── NegativeMarginBase.tsx
│ │ │ ├── PopupBase.stories.tsx
│ │ │ ├── PopupBase.tsx
│ │ │ ├── ScrimBase.stories.tsx
│ │ │ ├── ScrimBase.tsx
│ │ │ ├── ScrollableBase.stories.tsx
│ │ │ ├── ScrollableBase.tsx
│ │ │ ├── ZebraBase.stories.tsx
│ │ │ ├── ZebraBase.tsx
│ │ │ └── types.ts
│ │ ├── buttons/
│ │ │ ├── BackwardButton.stories.tsx
│ │ │ ├── BackwardButton.tsx
│ │ │ ├── Button.stories.tsx
│ │ │ ├── Button.tsx
│ │ │ ├── GlobalNaviButton.stories.tsx
│ │ │ ├── GlobalNaviButton.tsx
│ │ │ ├── IconOnlyBackwardButton.stories.tsx
│ │ │ ├── IconOnlyBackwardButton.tsx
│ │ │ ├── IconOnlyButton.stories.tsx
│ │ │ ├── IconOnlyButton.tsx
│ │ │ ├── IconOnlyJumpButton.stories.tsx
│ │ │ ├── IconOnlyJumpButton.tsx
│ │ │ ├── InlineLink.stories.tsx
│ │ │ ├── InlineLink.tsx
│ │ │ ├── JumpButton.stories.tsx
│ │ │ ├── JumpButton.tsx
│ │ │ ├── LeftIconButton.stories.tsx
│ │ │ ├── LeftIconButton.tsx
│ │ │ ├── ListButton.stories.tsx
│ │ │ ├── ListButton.tsx
│ │ │ ├── PagerButton.stories.tsx
│ │ │ ├── PagerButton.tsx
│ │ │ ├── RightIconButton.stories.tsx
│ │ │ ├── RightIconButton.tsx
│ │ │ ├── TabButton.stories.tsx
│ │ │ ├── TabButton.tsx
│ │ │ ├── TextButton.stories.tsx
│ │ │ └── TextButton.tsx
│ │ ├── calendar/
│ │ │ ├── CalendarDate.stories.tsx
│ │ │ ├── CalendarDate.tsx
│ │ │ ├── CalendarHead.stories.tsx
│ │ │ └── CalendarHead.tsx
│ │ ├── forms/
│ │ │ ├── CheckBox.stories.tsx
│ │ │ ├── CheckBox.tsx
│ │ │ ├── FormControlLabel.stories.tsx
│ │ │ ├── FormControlLabel.tsx
│ │ │ ├── NumeralField.stories.tsx
│ │ │ ├── NumeralField.tsx
│ │ │ ├── OptionButton.stories.tsx
│ │ │ ├── OptionButton.tsx
│ │ │ ├── RadioButton.stories.tsx
│ │ │ ├── RadioButton.tsx
│ │ │ ├── ReadOnlyField.stories.tsx
│ │ │ ├── ReadOnlyField.tsx
│ │ │ ├── SearchField.stories.tsx
│ │ │ ├── SearchField.tsx
│ │ │ ├── SelectBox.stories.tsx
│ │ │ ├── SelectBox.tsx
│ │ │ ├── TextArea.stories.tsx
│ │ │ ├── TextArea.tsx
│ │ │ ├── TextField.stories.tsx
│ │ │ ├── TextField.tsx
│ │ │ ├── ToggleButton.stories.tsx
│ │ │ ├── ToggleButton.tsx
│ │ │ └── types.ts
│ │ ├── grids/
│ │ │ ├── GridBlock.tsx
│ │ │ ├── GridWrapper.tsx
│ │ │ └── Grids.stories.tsx
│ │ ├── icons/
│ │ │ ├── Avatar.stories.tsx
│ │ │ ├── Avatar.tsx
│ │ │ ├── MaterialIcon.stories.tsx
│ │ │ ├── MaterialIcon.tsx
│ │ │ ├── RequiredIcon.stories.tsx
│ │ │ ├── RequiredIcon.tsx
│ │ │ ├── StatusIcon.stories.tsx
│ │ │ └── StatusIcon.tsx
│ │ ├── images/
│ │ │ ├── AlertSwallow.stories.tsx
│ │ │ ├── AlertSwallow.tsx
│ │ │ ├── AppStoreBadge.stories.tsx
│ │ │ ├── AppStoreBadge.tsx
│ │ │ ├── CloudSkeletonIllust.stories.tsx
│ │ │ ├── CloudSkeletonIllust.tsx
│ │ │ ├── CloudUploadIllust.stories.tsx
│ │ │ ├── CloudUploadIllust.tsx
│ │ │ ├── CsvUploadIllust.stories.tsx
│ │ │ ├── CsvUploadIllust.tsx
│ │ │ ├── DiscoveryIllust.stories.tsx
│ │ │ ├── DiscoveryIllust.tsx
│ │ │ ├── FileUploadIllust.stories.tsx
│ │ │ ├── FileUploadIllust.tsx
│ │ │ ├── FinishTaskIllust.stories.tsx
│ │ │ ├── FinishTaskIllust.tsx
│ │ │ ├── GooglePlayBadge.stories.tsx
│ │ │ ├── GooglePlayBadge.tsx
│ │ │ ├── ImageUploadIllust.stories.tsx
│ │ │ ├── ImageUploadIllust.tsx
│ │ │ ├── NoDataIllust.stories.tsx
│ │ │ ├── NoDataIllust.tsx
│ │ │ ├── NoSearchResultsIllust.stories.tsx
│ │ │ ├── NoSearchResultsIllust.tsx
│ │ │ ├── NotFoundSwallow.stories.tsx
│ │ │ ├── NotFoundSwallow.tsx
│ │ │ ├── SwallowContainer.tsx
│ │ │ ├── discovery-illust.json
│ │ │ └── finish-task-illust.json
│ │ ├── index.ts
│ │ ├── interactiveParts/
│ │ │ ├── SegmentControlButton.stories.tsx
│ │ │ ├── SegmentControlButton.tsx
│ │ │ ├── StepBlock.stories.tsx
│ │ │ ├── StepBlock.tsx
│ │ │ ├── StepBorder.stories.tsx
│ │ │ ├── StepBorder.tsx
│ │ │ ├── StepNumber.stories.tsx
│ │ │ ├── StepNumber.tsx
│ │ │ ├── Tab.stories.tsx
│ │ │ └── Tab.tsx
│ │ ├── layout/
│ │ │ ├── HStack.stories.tsx
│ │ │ ├── HStack.tsx
│ │ │ ├── Stack.stories.tsx
│ │ │ ├── Stack.tsx
│ │ │ ├── VStack.stories.tsx
│ │ │ ├── VStack.tsx
│ │ │ ├── WithDescriptionContent.stories.tsx
│ │ │ ├── WithDescriptionContent.tsx
│ │ │ ├── WithSideContent.stories.tsx
│ │ │ └── WithSideContent.tsx
│ │ ├── lists/
│ │ │ ├── BorderTableListCell.stories.tsx
│ │ │ ├── BorderTableListCell.tsx
│ │ │ ├── CheckBoxCell.stories.tsx
│ │ │ ├── CheckBoxCell.tsx
│ │ │ ├── DescriptionListCell.stories.tsx
│ │ │ ├── DescriptionListCell.tsx
│ │ │ ├── DescriptionListHeadCell.stories.tsx
│ │ │ ├── DescriptionListHeadCell.tsx
│ │ │ ├── TableListCell.stories.tsx
│ │ │ ├── TableListCell.tsx
│ │ │ ├── TableListHead.stories.tsx
│ │ │ ├── TableListHead.tsx
│ │ │ ├── TableListHeadCell.stories.tsx
│ │ │ ├── TableListHeadCell.tsx
│ │ │ ├── TableListRow.stories.tsx
│ │ │ ├── TableListRow.tsx
│ │ │ ├── TreeFoldingButtonCell.stories.tsx
│ │ │ └── TreeFoldingButtonCell.tsx
│ │ ├── messages/
│ │ │ ├── Message.stories.tsx
│ │ │ └── Message.tsx
│ │ ├── progress/
│ │ │ ├── ProgressBar.stories.tsx
│ │ │ └── ProgressBar.tsx
│ │ ├── skeleton/
│ │ │ └── SkeletonBase.tsx
│ │ └── typography/
│ │ ├── InternalHeadline.tsx
│ │ ├── Note.stories.tsx
│ │ ├── Note.tsx
│ │ ├── PageTitle.stories.tsx
│ │ ├── PageTitle.tsx
│ │ ├── Paragraph.stories.tsx
│ │ ├── Paragraph.tsx
│ │ ├── SectionTitle.stories.tsx
│ │ ├── SectionTitle.tsx
│ │ ├── SubSectionTitle.stories.tsx
│ │ ├── SubSectionTitle.tsx
│ │ ├── Text.stories.tsx
│ │ ├── Text.tsx
│ │ └── TypographyStyle.ts
│ ├── lv2/
│ │ ├── accordionPanel/
│ │ │ ├── AccordionPanel.stories.tsx
│ │ │ └── AccordionPanel.tsx
│ │ ├── basicTable/
│ │ │ ├── BasicTable.stories.tsx
│ │ │ └── BasicTable.tsx
│ │ ├── breadcrumbs/
│ │ │ ├── Breadcrumbs.stories.tsx
│ │ │ └── Breadcrumbs.tsx
│ │ ├── bulletedList/
│ │ │ ├── BulletedList.stories.tsx
│ │ │ └── BulletedList.tsx
│ │ ├── buttonGroup/
│ │ │ ├── ButtonGroup.stories.tsx
│ │ │ └── ButtonGroup.tsx
│ │ ├── calendar/
│ │ │ ├── Calendar.stories.tsx
│ │ │ ├── Calendar.tsx
│ │ │ ├── DatePicker.stories.tsx
│ │ │ ├── DatePicker.tsx
│ │ │ ├── Week.tsx
│ │ │ └── Weeks.tsx
│ │ ├── cardNavigation/
│ │ │ ├── CardNavigation.stories.tsx
│ │ │ └── CardNavigation.tsx
│ │ ├── combobox/
│ │ │ ├── ApiComboBox.stories.tsx
│ │ │ ├── ApiComboBox.tsx
│ │ │ ├── ApiMultiComboBox.stories.tsx
│ │ │ ├── ApiMultiComboBox.tsx
│ │ │ ├── CreateNewItem.tsx
│ │ │ ├── ItemLabel.tsx
│ │ │ ├── LoadMoreItem.tsx
│ │ │ ├── MultiComboBox.stories.tsx
│ │ │ ├── MultiComboBox.tsx
│ │ │ ├── MultiComboBoxField.tsx
│ │ │ ├── SingleComboBox.stories.tsx
│ │ │ ├── SingleComboBox.tsx
│ │ │ └── hooks/
│ │ │ ├── apiComboBox.ts
│ │ │ ├── apiMultiComboBox.ts
│ │ │ ├── index.ts
│ │ │ └── singleComboBox.ts
│ │ ├── descriptionList/
│ │ │ ├── DescriptionList.stories.tsx
│ │ │ └── DescriptionList.tsx
│ │ ├── dialogs/
│ │ │ ├── GuideDialog.stories.tsx
│ │ │ ├── GuideDialog.tsx
│ │ │ ├── MessageDialog.stories.tsx
│ │ │ ├── MessageDialog.tsx
│ │ │ ├── MessageDialogConfirm.stories.tsx
│ │ │ ├── MessageDialogConfirm.tsx
│ │ │ ├── TaskDialog.stories.tsx
│ │ │ ├── TaskDialog.tsx
│ │ │ ├── ToggleDialog.tsx
│ │ │ └── parts/
│ │ │ ├── DialogFooter.stories.tsx
│ │ │ ├── DialogFooter.tsx
│ │ │ ├── GuideStepCount.tsx
│ │ │ └── walkthroughImage.ts
│ │ ├── dropdown/
│ │ │ ├── Dropdown.stories.tsx
│ │ │ ├── Dropdown.tsx
│ │ │ ├── DropdownMenuContent.stories.tsx
│ │ │ ├── DropdownMenuContent.tsx
│ │ │ ├── Item.tsx
│ │ │ └── types.ts
│ │ ├── dropdownButton/
│ │ │ ├── DropdownButton.stories.tsx
│ │ │ └── DropdownButton.tsx
│ │ ├── emptyStates/
│ │ │ ├── NoDataCreated.stories.tsx
│ │ │ ├── NoDataCreated.tsx
│ │ │ ├── NoSearchResults.stories.tsx
│ │ │ └── NoSearchResults.tsx
│ │ ├── fileUploader/
│ │ │ ├── FileDropArea.stories.tsx
│ │ │ ├── FileDropArea.tsx
│ │ │ ├── FileUploader.stories.tsx
│ │ │ ├── FileUploader.tsx
│ │ │ ├── hooks.ts
│ │ │ └── types.ts
│ │ ├── filterTag/
│ │ │ ├── FilterTag.stories.tsx
│ │ │ └── FilterTag.tsx
│ │ ├── filterableDropdownButton/
│ │ │ ├── FilterableDropdownButton.stories.tsx
│ │ │ └── FilterableDropdownButton.tsx
│ │ ├── footer/
│ │ │ ├── Footer.stories.tsx
│ │ │ └── Footer.tsx
│ │ ├── formBlock/
│ │ │ ├── DateField.stories.tsx
│ │ │ ├── DateField.tsx
│ │ │ ├── FormActions.stories.tsx
│ │ │ ├── FormActions.tsx
│ │ │ ├── NameField.stories.tsx
│ │ │ ├── NameField.tsx
│ │ │ ├── PhoneNumberField.stories.tsx
│ │ │ └── PhoneNumberField.tsx
│ │ ├── formControl/
│ │ │ ├── FormControl.stories.tsx
│ │ │ ├── FormControl.tsx
│ │ │ ├── FormControlGroup.stories.tsx
│ │ │ └── FormControlGroup.tsx
│ │ ├── formFields/
│ │ │ ├── AmountRangeField.stories.tsx
│ │ │ ├── AmountRangeField.tsx
│ │ │ ├── DateDurationField.stories.tsx
│ │ │ ├── DateDurationField.tsx
│ │ │ ├── DateInput.stories.tsx
│ │ │ ├── DateInput.tsx
│ │ │ ├── DecimalInput.stories.tsx
│ │ │ ├── DecimalInput.tsx
│ │ │ ├── DigitsInput.stories.tsx
│ │ │ ├── DigitsInput.tsx
│ │ │ ├── FormattedTextField.stories.tsx
│ │ │ ├── FormattedTextField.tsx
│ │ │ ├── NumeralCodeInput.stories.tsx
│ │ │ ├── NumeralCodeInput.tsx
│ │ │ ├── PasswordField.stories.tsx
│ │ │ ├── PasswordField.tsx
│ │ │ ├── TimeInput.stories.tsx
│ │ │ ├── TimeInput.tsx
│ │ │ ├── TimeLengthInput.stories.tsx
│ │ │ └── TimeLengthInput.tsx
│ │ ├── globalNavi/
│ │ │ ├── GlobalNavi.stories.tsx
│ │ │ └── GlobalNavi.tsx
│ │ ├── guidanceMessage/
│ │ │ ├── GuidanceMessage.stories.tsx
│ │ │ └── GuidanceMessage.tsx
│ │ ├── guidedContent/
│ │ │ ├── GuidedContent.stories.tsx
│ │ │ └── GuidedContent.tsx
│ │ ├── header/
│ │ │ ├── Header.stories.tsx
│ │ │ ├── Header.tsx
│ │ │ ├── HeaderSectionContent.tsx
│ │ │ └── types.ts
│ │ ├── headlineArea/
│ │ │ ├── HeadlineArea.stories.tsx
│ │ │ └── HeadlineArea.tsx
│ │ ├── hierarchicalTable/
│ │ │ ├── HierarchicalTable.stories.tsx
│ │ │ ├── HierarchicalTable.tsx
│ │ │ ├── HierarchicalTableRowHeaderCell.tsx
│ │ │ └── hooks/
│ │ │ └── useHierarchicalTable.ts
│ │ ├── index.ts
│ │ ├── indexSearchField/
│ │ │ ├── IndexSearchField.stories.tsx
│ │ │ └── IndexSearchField.tsx
│ │ ├── lineSeparatedList/
│ │ │ ├── LineSeparatedList.stories.tsx
│ │ │ └── LineSeparatedList.tsx
│ │ ├── listButtonSelector/
│ │ │ ├── ListButtonSelector.stories.tsx
│ │ │ └── ListButtonSelector.tsx
│ │ ├── listButtons/
│ │ │ ├── ListButtons.stories.tsx
│ │ │ └── ListButtons.tsx
│ │ ├── listCard/
│ │ │ ├── ListCard.stories.tsx
│ │ │ └── ListCard.tsx
│ │ ├── listTable/
│ │ │ ├── GroupedListTable.stories.tsx
│ │ │ ├── GroupedListTable.tsx
│ │ │ ├── ListTable.stories.tsx
│ │ │ └── ListTable.tsx
│ │ ├── messageBlock/
│ │ │ ├── FloatingMessageBlock.stories.tsx
│ │ │ ├── FloatingMessageBlock.tsx
│ │ │ ├── MessageBlock.stories.tsx
│ │ │ └── MessageBlock.tsx
│ │ ├── messageIcon/
│ │ │ ├── MessageIcon.stories.tsx
│ │ │ └── MessageIcon.tsx
│ │ ├── modals/
│ │ │ ├── FullScreenModal.stories.tsx
│ │ │ └── FullScreenModal.tsx
│ │ ├── numericTable/
│ │ │ ├── NumericTable.stories.tsx
│ │ │ └── NumericTable.tsx
│ │ ├── pageSelector/
│ │ │ ├── PageSelector.stories.tsx
│ │ │ └── PageSelector.tsx
│ │ ├── pager/
│ │ │ ├── Pager.stories.tsx
│ │ │ ├── Pager.tsx
│ │ │ ├── PagerBreak.tsx
│ │ │ ├── Pagination.stories.tsx
│ │ │ └── Pagination.tsx
│ │ ├── personTag/
│ │ │ ├── PersonTag.stories.tsx
│ │ │ └── PersonTag.tsx
│ │ ├── popupProgressBar/
│ │ │ ├── PopupProgressBar.stories.tsx
│ │ │ └── PopupProgressBar.tsx
│ │ ├── popupProgressBarPortal/
│ │ │ ├── PopupProgressBarPortal.stories.tsx
│ │ │ └── PopupProgressBarPortal.tsx
│ │ ├── propsListForm/
│ │ │ ├── PropsListForm.stories.tsx
│ │ │ └── PropsListForm.tsx
│ │ ├── scrimCoveredContent/
│ │ │ ├── ScrimCoveredContent.stories.tsx
│ │ │ └── ScrimCoveredContent.tsx
│ │ ├── selectableButton/
│ │ │ ├── SelectableButton.stories.tsx
│ │ │ └── SelectableButton.tsx
│ │ ├── skeleton/
│ │ │ ├── SkeletonBlock.stories.tsx
│ │ │ ├── SkeletonBlock.tsx
│ │ │ ├── SkeletonCircle.stories.tsx
│ │ │ ├── SkeletonCircle.tsx
│ │ │ ├── SkeletonDescriptionList.stories.tsx
│ │ │ ├── SkeletonDescriptionList.tsx
│ │ │ ├── SkeletonIcon.stories.tsx
│ │ │ ├── SkeletonIcon.tsx
│ │ │ ├── SkeletonInput.stories.tsx
│ │ │ ├── SkeletonInput.tsx
│ │ │ ├── SkeletonListTable.stories.tsx
│ │ │ ├── SkeletonListTable.tsx
│ │ │ ├── SkeletonPageTitle.stories.tsx
│ │ │ ├── SkeletonPageTitle.tsx
│ │ │ ├── SkeletonParagraph.stories.tsx
│ │ │ ├── SkeletonParagraph.tsx
│ │ │ ├── SkeletonRectangle.stories.tsx
│ │ │ ├── SkeletonRectangle.tsx
│ │ │ ├── SkeletonSectionTitle.stories.tsx
│ │ │ ├── SkeletonSectionTitle.tsx
│ │ │ ├── SkeletonStackedBarChart.stories.tsx
│ │ │ └── SkeletonStackedBarChart.tsx
│ │ ├── stackedBarChart/
│ │ │ ├── StackedBarChart.stories.tsx
│ │ │ └── StackedBarChart.tsx
│ │ ├── statusSelector/
│ │ │ ├── StatusSelector.stories.tsx
│ │ │ └── StatusSelector.tsx
│ │ ├── stepper/
│ │ │ ├── Stepper.stories.tsx
│ │ │ ├── Stepper.tsx
│ │ │ ├── VerticalSteps.stories.tsx
│ │ │ └── VerticalSteps.tsx
│ │ ├── tabBar/
│ │ │ ├── TabBar.stories.tsx
│ │ │ └── TabBar.tsx
│ │ ├── tagBox/
│ │ │ ├── MiniTag.stories.tsx
│ │ │ ├── MiniTag.tsx
│ │ │ ├── TagBox.stories.tsx
│ │ │ └── TagBox.tsx
│ │ ├── withAccordionContent/
│ │ │ ├── WithAccordionContent.stories.tsx
│ │ │ └── WithAccordionContent.tsx
│ │ ├── withBalloon/
│ │ │ ├── WithBalloon.stories.tsx
│ │ │ ├── WithBalloon.tsx
│ │ │ └── useBalloon.ts
│ │ ├── withDropdown/
│ │ │ ├── WithDropdown.stories.tsx
│ │ │ └── WithDropdown.tsx
│ │ ├── withFilterableDropdown/
│ │ │ ├── WithFilterableDropdown.stories.tsx
│ │ │ └── WithFilterableDropdown.tsx
│ │ ├── withPopup/
│ │ │ ├── WithPopup.stories.tsx
│ │ │ └── WithPopup.tsx
│ │ └── withTOC/
│ │ ├── WithTOC.stories.tsx
│ │ └── WithTOC.tsx
│ └── utilities/
│ ├── AriaProps.ts
│ ├── Ascii.test.ts
│ ├── Ascii.ts
│ ├── DOMUtil.ts
│ ├── Dialog.tsx
│ ├── Digits.test.ts
│ ├── Digits.ts
│ ├── FixedPortal.tsx
│ ├── FocusableEelements.ts
│ ├── Mins.ts
│ ├── ScrollPortal.tsx
│ ├── TimeString.ts
│ ├── VibesContext.ts
│ ├── VibesProvider.tsx
│ ├── browsers.ts
│ ├── commonProps.test.ts
│ ├── commonProps.ts
│ ├── convertRemToPixel.ts
│ ├── date.test.ts
│ ├── date.ts
│ ├── functionalMarginClasses.test.ts
│ ├── functionalMarginClasses.ts
│ ├── index.ts
│ ├── keyboard.ts
│ ├── marginClasses.ts
│ ├── selfWindowNavigator.ts
│ ├── useMedia.ts
│ └── vbClassNames.ts
├── stories/
│ ├── commonKnobs.ts
│ └── index.ts
├── stylelint.config.js
├── stylesheets/
│ ├── _container_query.scss
│ ├── _lv1.scss
│ ├── _lv2.scss
│ ├── freee.scss
│ ├── lv0/
│ │ ├── _colors.scss
│ │ ├── _focus.scss
│ │ ├── _fonts.scss
│ │ └── _size.scss
│ ├── lv1/
│ │ ├── InlineSpinner.scss
│ │ ├── Loading.scss
│ │ ├── base.scss
│ │ ├── button.scss
│ │ ├── calendar.scss
│ │ ├── content.scss
│ │ ├── focusHighlight.scss
│ │ ├── focusTrap.scss
│ │ ├── form.scss
│ │ ├── grid.scss
│ │ ├── headline.scss
│ │ ├── icon.scss
│ │ ├── image.scss
│ │ ├── interactive-parts.scss
│ │ ├── layout.scss
│ │ ├── list.scss
│ │ ├── margin-option.scss
│ │ ├── message.scss
│ │ └── progress.scss
│ ├── lv2/
│ │ ├── accordionPanel.scss
│ │ ├── breadcrumbs.scss
│ │ ├── bulletedList.scss
│ │ ├── buttonGroup.scss
│ │ ├── calendar.scss
│ │ ├── cardNavigation.scss
│ │ ├── comboBox.scss
│ │ ├── dateInput.scss
│ │ ├── descriptionList.scss
│ │ ├── dialog.scss
│ │ ├── dialogFooter.scss
│ │ ├── dropdown.scss
│ │ ├── dropdownButton.scss
│ │ ├── emptyStates.scss
│ │ ├── fileDropArea.scss
│ │ ├── fileUploader.scss
│ │ ├── filterTag.scss
│ │ ├── footer.scss
│ │ ├── formBlock.scss
│ │ ├── formField.scss
│ │ ├── formGroup.scss
│ │ ├── globalNavi.scss
│ │ ├── guidanceMessage.scss
│ │ ├── guidedContent.scss
│ │ ├── header.scss
│ │ ├── headlineArea.scss
│ │ ├── hierarchicalTable.scss
│ │ ├── indexSearchField.scss
│ │ ├── lineSeparatedList.scss
│ │ ├── listButtonSelector.scss
│ │ ├── listCard.scss
│ │ ├── listTable.scss
│ │ ├── messageBlock.scss
│ │ ├── messageIcon.scss
│ │ ├── modal.scss
│ │ ├── numericTable.scss
│ │ ├── pageSelector.scss
│ │ ├── pager.scss
│ │ ├── personTag.scss
│ │ ├── popupProgressBar.scss
│ │ ├── popupProgressBarPortal.scss
│ │ ├── propListForm.scss
│ │ ├── selectableButton.scss
│ │ ├── skeleton.scss
│ │ ├── stackedBarChart.scss
│ │ ├── statusSelector.scss
│ │ ├── stepper.scss
│ │ ├── tabbar.scss
│ │ ├── tagBox.scss
│ │ ├── withAccordionContent.scss
│ │ ├── withBalloon.scss
│ │ ├── withFilterableDropdown.scss
│ │ ├── withPopup.scss
│ │ └── withTOC.scss
│ └── vibes_2021.scss
├── tsconfig.build.json
├── tsconfig.json
├── utilities.js.flow
└── vibes_2021.css
================================================
FILE CONTENTS
================================================
================================================
FILE: .babelrc
================================================
{
"presets": [
[
"@babel/preset-env",
{
"targets": {
"browsers": [
"last 2 Chrome versions",
"last 2 Firefox versions",
"Firefox ESR",
"last 3 Safari versions",
"last 2 Edge versions",
"last 3 iOS versions",
"last 2 ChromeAndroid versions",
"last 2 FirefoxAndroid versions"
]
}
}
],
"@babel/preset-flow",
"@babel/preset-react",
"@babel/preset-typescript"
],
"plugins": [
"@babel/plugin-proposal-class-properties",
"@babel/plugin-transform-flow-strip-types"
]
}
================================================
FILE: .circleci/config.yml
================================================
version: 2.1
executors:
node:
docker:
- image: cimg/node:18.16.1
defaults: &defaults
working_directory: ~/repo
parallelism: 1
executor:
name: node
aliases:
- &restore_npm_cache
restore_cache:
keys:
- npm-v7-dependency-{{ checksum "package-lock.json" }}
- npm-v7-dependency-
jobs:
build:
<<: *defaults
steps:
- checkout
- persist_to_workspace:
root: ~/
paths:
- repo
- *restore_npm_cache
- run:
name: install node dependencies
command: |
npm install
- run:
name: build package
command: |
npm run build
- save_cache:
paths:
- ./node_modules
key: npm-v7-dependency-{{ checksum "package-lock.json" }}
eslint_flow:
<<: *defaults
steps:
- attach_workspace:
at: ~/
- *restore_npm_cache
- run:
name: lint check for javascript
command: |
npm run lint --max-warnings=0
- store_test_results:
path: ./tmp/eslint
stylelint:
<<: *defaults
steps:
- attach_workspace:
at: ~/
- *restore_npm_cache
- run:
name: stylelint
command: |
npm run stylelint
- store_test_results:
path: ./tmp/stylelint
build_storybook:
<<: *defaults
resource_class: medium+
steps:
- attach_workspace:
at: ~/
- *restore_npm_cache
- run:
name: build
command: |
mkdir -p /tmp/storybook
npm run build-storybook -- -o /tmp/storybook
- store_artifacts:
path: /tmp/storybook
- run:
name: comment to pr
command: |
# PR トリガーではない場合はスキップ
if [ -z "$CIRCLE_PR_NUMBER" ]; then exit 0; fi
# https://support.circleci.com/hc/en-us/articles/5034956515355-How-to-Programmatically-Construct-the-URLs-for-Artifacts
index_html_url="https://output.circle-artifacts.com/output/job/${CIRCLE_WORKFLOW_JOB_ID}/artifacts/${CIRCLE_NODE_INDEX}/tmp/storybook/index.html"
# https://docs.github.com/en/rest/issues/comments?apiVersion=2022-11-28#create-an-issue-comment
curl \
-X POST \
-H "Accept: application/vnd.github+json" \
-H "Authorization: Bearer $GITHUB_TOKEN" \
-H "X-GitHub-Api-Version: 2022-11-28" \
https://api.github.com/repos/${CIRCLE_PROJECT_USERNAME}/${CIRCLE_PROJECT_REPONAME}/issues/${CIRCLE_PR_NUMBER}/comments \
-d '{"body":"### Storybook\n'${index_html_url}'"}'
test:
<<: *defaults
steps:
- attach_workspace:
at: ~/
- *restore_npm_cache
- run:
name: test
command: npm test
- store_test_results:
path: ./tmp/test
workflows:
version: 2
integration:
jobs:
- build
- eslint_flow:
requires:
- build
- stylelint:
requires:
- build
- build_storybook:
context:
- github_token_sushi_bot
requires:
- build
- test:
requires:
- build
================================================
FILE: .dockerignore
================================================
node_modules/
================================================
FILE: .editorconfig
================================================
root = true
[*]
charset = utf-8
end_of_line = lf
indent_style = space
indent_size = 2
trim_trailing_whitespace = true
insert_final_newline = true
[Makefile]
indent_style = tab
================================================
FILE: .eslintignore
================================================
/dist/
================================================
FILE: .eslintrc.js
================================================
module.exports = {
parser: '@babel/eslint-parser',
env: {
browser: true,
jest: true,
node: true
},
plugins: ['react-hooks'],
globals: {},
extends: ['eslint:recommended', 'plugin:compat/recommended', 'plugin:import/recommended', 'plugin:jsx-a11y/recommended', 'plugin:react/recommended', 'plugin:storybook/recommended'],
settings: {
react: {
version: 'detect'
},
'import/resolver': {
node: {
extensions: ['.js', '.ts', '.tsx']
}
},
// TODO: browserslist を設定するか各 polyfill に対応
polyfills: ['window.scrollX', 'window.scrollY', 'Array.from']
},
rules: {
'no-unused-vars': ['error', {
vars: 'all',
args: 'all',
argsIgnorePattern: '^_'
}],
// TODO: ログインなどの要素に使えるようオフにしているが、要検討
'jsx-a11y/no-autofocus': 'off',
'react-hooks/rules-of-hooks': 'error',
'react-hooks/exhaustive-deps': 'error'
},
overrides: [{
plugins: ['ft-flow'],
files: ['*.js', '*.js.flow'],
extends: ['plugin:ft-flow/recommended'],
rules: {
'ft-flow/generic-spacing': 'off',
'ft-flow/space-after-type-colon': 'off'
}
}, {
files: ['*.ts', '*.tsx'],
extends: ['eslint-config-freee-typescript'],
rules: {
'@typescript-eslint/no-explicit-any': 'off',
'jsx-a11y/no-autofocus': 'off',
'@typescript-eslint/naming-convention': ['error', {
selector: 'default',
format: ['camelCase', 'PascalCase']
}, {
selector: 'variable',
format: ['camelCase', 'UPPER_CASE', 'PascalCase']
}, {
selector: 'parameter',
format: ['camelCase', 'snake_case', 'UPPER_CASE', 'PascalCase'],
leadingUnderscore: 'allow'
}, {
selector: 'property',
format: null
}, {
selector: 'memberLike',
modifiers: ['private'],
format: ['camelCase'],
leadingUnderscore: 'require'
}, {
selector: 'typeLike',
format: ['PascalCase']
}]
},
settings: {
'import/resolver': {
node: {
extensions: ['.js', '.ts', '.tsx']
}
}
}
}]
};
================================================
FILE: .flowconfig
================================================
[ignore]
.*/__tests__/types/.*
lv1/.*
lv2/.*
utilities/.*
hooks/.*
[include]
[libs]
[lints]
[options]
server.max_workers=4
module.name_mapper='^vibes\(.*\)$' -> '<PROJECT_ROOT>\1'
sharedmemory.hash_table_pow=21
[strict]
================================================
FILE: .github/PULL_REQUEST_TEMPLATE.md
================================================
<!--
**注意**
これは public repositoryです。
ここに書いた情報は、全世界に公開されます。
社内の機密情報、特にリリース前のプロダクトや機能に関する情報を記載しないでください!
-->
## :memo: 概要
<!-- 変更内容を明記しよう -->
## :stuck_out_tongue: やってないこと
<!-- この PR ではやってないことなどを明記しよう -->
## :heavy_check_mark: 動作確認
<!-- 実装した個々の機能の再現方法を明記し、開発者・レビュワー共に確認しよう -->
- [ ] Storybook で 〇〇 が XX できるのを確認
================================================
FILE: .github/workflows/publish_package_to_npmjs.yml
================================================
name: Publish Package to npmjs
on:
release:
types: [published]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v3
with:
node-version-file: .node-version
cache: 'npm'
registry-url: 'https://registry.npmjs.org'
- name: Install dependencies
run: npm ci
- run: npm publish --access public
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
================================================
FILE: .github/workflows/publish_storybook.yml
================================================
name: publish Storybook
on:
push:
branches:
- 'main'
jobs:
publish:
if: github.repository == 'freee/vibes'
runs-on: ubuntu-latest
permissions:
id-token: write
contents: read
steps:
- uses: actions/checkout@v4
- uses: aws-actions/configure-aws-credentials@v4
with:
aws-region: 'ap-northeast-1'
role-to-assume: ${{ secrets.PROD_AWS_ROLE_TO_ASSUME }}
- uses: actions/setup-node@v3
with:
node-version-file: .node-version
cache: 'npm'
- name: Install Dependencies
run: npm install
- name: Build Storybook
run: npm run build-storybook
- name: Publish to S3
env:
AWS_BUCKET: ${{ secrets.PROD_AWS_S3_BUCKET }}
run: aws s3 sync --delete ${GITHUB_WORKSPACE}/storybook-static/ s3://${AWS_BUCKET}/ --quiet
================================================
FILE: .github/workflows/pull_request_test_and_lint.yml
================================================
name: Pull Request Test and Lint
on:
pull_request:
branches:
- main
jobs:
test_and_lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v3
with:
node-version-file: .node-version
cache: 'npm'
- name: Install dependencies
run: npm ci
- run: npm run lint
- run: npm run test
================================================
FILE: .gitignore
================================================
node_modules
*.log
.DS_Store
.idea/
.cache
.vscode
storybook-static/
dist/
# docker
docker/.build_timestamp
# direnv / dotenv
.envrc
.env
# eslint cache
.eslintcache
================================================
FILE: .husky/pre-commit
================================================
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"
npx lint-staged
================================================
FILE: .jest/Mock.js
================================================
================================================
FILE: .jest/setup.js
================================================
================================================
FILE: .node-version
================================================
18.16.1
================================================
FILE: .npmrc
================================================
save-exact = true
================================================
FILE: .prettierignore
================================================
/dist/
================================================
FILE: .storybook/main.ts
================================================
import type { StorybookConfig } from '@storybook/react-webpack5';
const config: StorybookConfig = {
addons: [
'@storybook/addon-knobs',
'@storybook/addon-essentials',
'@storybook/addon-links',
'@storybook/addon-a11y',
'@storybook/addon-interactions',
'@storybook/addon-storysource',
'storybook-addon-pseudo-states',
'@kemuridama/storybook-addon-github',
'@storybook/addon-styling-webpack',
{
name: '@storybook/addon-styling-webpack',
options: {
rules: [
// Replaces any existing Sass rules with given rules
{
test: /\.s[ac]ss$/i,
use: [
'style-loader',
'css-loader',
{
loader: 'sass-loader',
options: { implementation: require.resolve('sass') },
},
],
},
],
},
},
],
stories: [
'../docs/**/*.stories.mdx',
'../src/**/*.stories.tsx',
'../examples/**/*.stories.tsx',
],
typescript: {
check: true,
},
webpackFinal: (config) => ({
...config,
module: {
...config.module,
rules: [
...(config.module?.rules ?? []),
{
test: /\.stories\.tsx?$/,
use: [require.resolve('@storybook/source-loader')],
enforce: 'pre',
},
],
},
}),
framework: {
name: '@storybook/react-webpack5',
options: {},
},
docs: {
autodocs: true,
},
};
export default config;
================================================
FILE: .storybook/manager.js
================================================
import { addons } from '@storybook/addons';
import { create } from '@storybook/theming';
import logo from './logo-vibes.svg';
addons.setConfig({
theme: create({
base: 'light',
brandTitle: 'vibes',
brandImage: logo,
}),
});
================================================
FILE: .storybook/preview.tsx
================================================
import * as React from 'react';
import ReactDOM from 'react-dom';
import { DocsContainer } from '@storybook/addon-docs';
import { Decorator, Parameters } from '@storybook/react';
import { VibesProvider, useLang } from '../src/utilities/VibesProvider';
import '../stylesheets/vibes_2021.scss';
const LanguagePortal = React.forwardRef<HTMLDivElement, { lang: string }>(
({ lang }, ref) =>
ReactDOM.createPortal(<div lang={lang} ref={ref}></div>, document.body)
);
export const decorators: Decorator[] = [
(Story, context) => {
const portalParentRef = React.useRef(null);
return (
<VibesProvider
fixedLayout={!context.globals.responsive}
lang={context.globals.lang}
portalParentRef={portalParentRef}
>
<div lang={useLang()}>
<Story />
</div>
<LanguagePortal lang={useLang()} ref={portalParentRef} />
</VibesProvider>
);
},
];
export const parameters: Parameters = {
viewMode: 'docs',
docs: {
container: ({ children, context }) => (
<div lang={useLang()}>
<DocsContainer context={context}>{children}</DocsContainer>
</div>
),
},
options: {
storySort: {
order: ['doc', ['Readme'], 'examples', 'lv2', 'lv1', 'deprecated'],
},
},
github: {
repository: 'freee/vibes',
branch: 'main',
},
};
export const globalTypes = {
responsive: {
description: 'Responsive',
defaultValue: 'Off',
toolbar: {
title: 'Responsive',
icon: 'circlehollow',
items: [
{ value: true, title: 'On' },
{ value: false, title: 'Off' },
],
},
},
lang: {
description: 'Language',
defaultValue: 'ja',
toolbar: {
title: 'Language',
icon: 'globe',
items: [
{ value: 'ja', title: '日本語 (ja)' },
{ value: 'en', title: 'English (en)' },
],
},
},
};
================================================
FILE: Dockerfile
================================================
FROM node:14.15.4
ARG github_username
ARG github_token
ENV GITHUB_USERNAME $github_username
ENV GITHUB_TOKEN $github_token
WORKDIR /usr/src/app
COPY docker/git-credential-github-token /usr/local/bin
RUN apt-get update && apt-get install -y curl git
RUN mkdir /root/.ssh/ && \
git config --global url."https://github.com".insteadOf ssh://git@github.com && \
git config --global --add url."https://github.com".insteadOf git://git@github.com && \
git config --global --add url."https://github.com/".insteadOf git@github.com: && \
git config --global credential.helper github-token
COPY package.json ./
COPY package-lock.json ./
RUN npm install
COPY . .
EXPOSE 6006
CMD ["npm", "run", "storybook"]
================================================
FILE: LICENSE.txt
================================================
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright 2023 freee K.K.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
================================================
FILE: Makefile
================================================
APP_NAME := vibes
VERSION_TAG ?= latest
IMAGE_NAME := $(APP_NAME):$(VERSION_TAG)
.DEFAULT_GOAL = help # display usage when there is no argument.
DOCKER_DIR := docker
DOCKER_BUILD_TIMESTAMP := docker/.build_timestamp
$(DOCKER_BUILD_TIMESTAMP): $(shell find ./docker -not -name .build_timestamp)
@docker build -t $(IMAGE_NAME) --build-arg github_username=$(GITHUB_USERNAME) --build-arg github_token=$(GITHUB_TOKEN) .
touch $(DOCKER_BUILD_TIMESTAMP)
.PHONY: help
help: ## show options
@grep -E '^[a-zA-Z_-{\.}]+:.*?## .*$$' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-15s\033[0m %s\n", $$1, $$2}'
.PHONY: docker.build
docker.build: $(DOCKER_BUILD_TIMESTAMP) ## build docker image
.PHONY: docker.rebuild
docker.rebuild: ## rebuild docker image
-rm $(DOCKER_BUILD_TIMESTAMP)
make docker.build
.PHONY: docker.run
docker.run: $(DOCKER_BUILD_TIMESTAMP) ## run storybook on docker
docker run -it -p 6006:6006 --rm --name $(APP_NAME) $(IMAGE_NAME)
================================================
FILE: README.md
================================================
[世界を変えるためのデザインシステム](https://speakerdeck.com/ymrl/shi-jie-wobian-erutamefalsedezainsisutemu)です
# Getting started
## Install
TBD
## Usage
スタイルを適用するため node_modules/vibes/vibes.css を読み込んでください。
from Sass:
```
@import 'node_modules/vibes/vibes_2021';
```
from JavaScript with CSS Modules:
```js
import '@freee_jp/vibes/css';
```
スタイルを読み込んだら、vibes の React Component を次のように使うだけです。
```js
import * as React from 'react';
import { Breadcrumbs } from '@freee_jp/vibes';
import { CompanyLogoT } from 'somewhere';
export default function App() {
return (
<div className="app">
<CompanyLogoT size="fit-width" fill="default"/>
<Breadcrumbs
links={[
{ title: '取引', url: '/hub_pages/deals' },
{ title: '自動で経理', url: '/wallet_txns/stream' },
]}
/>
</div>
);
}
```
# Contribution
[こちらのガイドライン](https://github.com/freee/vibes/blob/master/docs/Contribution.stories.mdx)を参照してください。
================================================
FILE: __tests__/types/flow/.flowconfig
================================================
[ignore]
[include]
./../../../index.js
./../../../lv1.js
./../../../lv2.js
./../../../utilities.js
./../../../node_modules/lottie-web/.*
./../../../node_modules/react/.*
./../../../node_modules/react-dom/.*
./../../../node_modules/react-icons/.*
./../../../node_modules/react-modal/.*
./../../../node_modules/react-dnd/.*
./../../../node_modules/react-dnd-html5-backend/.*
[libs]
[lints]
[options]
server.max_workers=4
module.name_mapper='^vibes\(.*\)$' -> '<PROJECT_ROOT>/../../..\1'
include_warnings=true
suppress_comment= \\(.\\|\n\\)*\\$FlowExpectedError
[strict]
================================================
FILE: __tests__/types/flow/lv1.js
================================================
// @flow
import * as React from 'react';
import {
FocusHighlight,
VisuallyHidden,
Balloon,
ColumnBase,
Container,
ContentsBase,
DialogBase,
FloatingBase,
MarginBase,
NegativeMarginBase,
PopupBase,
CardBase,
ScrimBase,
ScrollableBase,
ZebraBase,
Button,
GlobalNaviButton,
IconOnlyButton,
IconOnlyJumpButton,
IconOnlyBackwardButton,
InlineLink,
JumpButton,
BackwardButton,
LeftIconButton,
ListButton,
PagerButton,
RightIconButton,
TabButton,
TextButton,
CheckBox,
RadioButton,
SearchField,
SelectBox,
TextArea,
TextField,
ToggleButton,
OptionButton,
FormControlLabel,
GridBlock,
GridWrapper,
Note,
PageTitle,
Paragraph,
SectionTitle,
SubSectionTitle,
Text,
Avatar,
MaterialIcon,
RequiredIcon,
StatusIcon,
AlertSwallow,
AppStoreBadge,
CloudSkeletonIllust,
CloudUploadIllust,
CsvUploadIllust,
FileUploadIllust,
GooglePlayBadge,
ImageUploadIllust,
NotFoundSwallow,
CalendarDate,
SegmentControlButton,
StepNumber,
StepBlock,
StepBorder,
Tab,
WithDescriptionContent,
WithSideContent,
Stack,
BorderTableListCell,
CheckBoxCell,
DescriptionListCell,
DescriptionListHeadCell,
TableListCell,
TableListHead,
TableListHeadCell,
TableListRow,
Message,
ProgressBar,
Loading,
InlineSpinner,
// eslint-disable-next-line import/no-unresolved
} from 'vibes/index';
const marginProps = {
marginLeft: true,
marginRight: true,
marginBottom: true,
marginTop: true,
marginSize: 'large',
};
const commonProps = {
'data-guide': 'guide',
'data-test': 'test',
'data-tracking': 'tracking',
'data-masking': true,
ma: 1,
mt: 1,
mb: 1,
mr: 1,
ml: 1,
};
const inputHandlers = {
onChange: (_e: SyntheticInputEvent<HTMLInputElement>) => {},
onInput: (_e: SyntheticInputEvent<HTMLInputElement>) => {},
onFocus: (_e: SyntheticFocusEvent<HTMLInputElement>) => {},
onBlur: (_e: SyntheticFocusEvent<HTMLInputElement>) => {},
onKeyDown: (_e: SyntheticKeyboardEvent<HTMLInputElement>) => {},
onKeyUp: (_e: SyntheticKeyboardEvent<HTMLInputElement>) => {},
};
const buttonAriaProps = {
'aria-expanded': true,
'aria-pressed': true,
'aria-controls': 'hoge',
'aria-owns': 'hoge',
'aria-haspopup': true,
'aria-describedby': 'hoge',
};
const linkAriaProps = {
'aria-expanded': true,
'aria-controls': 'hoge',
'aria-owns': 'hoge',
'aria-haspopup': true,
'aria-describedby': 'hoge',
};
const numberInputAriaProps = {
'aria-valuemin': 0,
'aria-valuemax': 100,
'aria-valuenow': 10,
};
const numberInputProps = {
min: 0,
max: 100,
step: 10,
};
const tableRowAriaProps = {
'aria-level': 1,
'aria-setsize': 1,
'aria-posinset': 1,
'aria-expanded': false,
};
// a11y
// $FlowExpectedError
<VisuallyHidden small="a" />;
<VisuallyHidden {...commonProps}>
<div />
</VisuallyHidden>;
<VisuallyHidden autoRead={true} assertive id="visually-hidden">
<div />
</VisuallyHidden>;
<FocusHighlight
highlightStyle="outset"
cornerStyle="round"
inline
{...commonProps}
>
<div />
</FocusHighlight>;
export default function App() {
return (
<div className="App">
{/* bases */}
<Balloon
small
border="default"
position="left"
verticalPosition="bottom"
{...marginProps}
{...commonProps}
>
balloon
</Balloon>
<ColumnBase
small
border="default"
inline
paddingSize="small"
{...marginProps}
{...commonProps}
>
<div />
</ColumnBase>
<Container {...marginProps} {...commonProps}>
<div />
<div />
</Container>
<ContentsBase paddingSize="small" {...marginProps} {...commonProps}>
<div />
<div />
</ContentsBase>
<DialogBase
small
border="default"
inline
paddingSize="small"
message
{...marginProps}
{...commonProps}
>
<div />
</DialogBase>
<FloatingBase
small
border="default"
inline
paddingSize="small"
{...commonProps}
{...marginProps}
>
<div />
</FloatingBase>
<MarginBase
marginTop
marginLeft
marginRight
marginBottom
marginSize="xxLarge"
{...commonProps}
>
<div />
</MarginBase>
<NegativeMarginBase
marginSize="small"
top
left
right
bottom
{...commonProps}
>
<div />
</NegativeMarginBase>
<PopupBase
small
border="default"
inline
paddingSize="small"
{...commonProps}
{...marginProps}
>
<div />
</PopupBase>
<CardBase
small
inline
clickable
url="test"
target="_blank"
rel="test"
onClick={(_e: SyntheticMouseEvent<>) => {}}
onSelfWindowNavigation={() => {}}
{...commonProps}
{...marginProps}
>
<div />
</CardBase>
<ScrimBase small {...commonProps} {...marginProps}>
<div />
</ScrimBase>
<ScrollableBase scrollableX scrollableY {...commonProps}></ScrollableBase>
<ZebraBase paddingSize="small" {...marginProps} {...commonProps}>
<div />
</ZebraBase>
{/* buttons */}
<Button
appearance="primary"
primary
danger
disabled
small
large
href="test"
type="button"
target="_blank"
rel="noopener noreferrer"
download="test"
ref={React.createRef<HTMLButtonElement | HTMLAnchorElement>()}
onClick={(_e: SyntheticMouseEvent<>) => {}}
onKeyDown={(
_e: SyntheticKeyboardEvent<HTMLButtonElement | HTMLAnchorElement>
) => {}}
onSelfWindowNavigation={() => {}}
iconPosition="left"
IconComponent={StatusIcon}
width="full"
{...marginProps}
{...commonProps}
{...buttonAriaProps}
{...linkAriaProps}
>
<div />
</Button>
<GlobalNaviButton
IconComponent={StatusIcon}
href="test"
current
onSelfWindowNavigation={() => {}}
{...marginProps}
{...commonProps}
>
<div />
</GlobalNaviButton>
<IconOnlyButton
IconComponent={StatusIcon}
primary
danger
disabled
small
large
label="test"
type="button"
href="test"
target="test"
rel="test"
onSelfWindowNavigation={() => {}}
onClick={(
_e: SyntheticEvent<HTMLButtonElement | HTMLAnchorElement>
) => {}}
onKeyDown={(
_e: SyntheticKeyboardEvent<HTMLButtonElement | HTMLAnchorElement>
) => {}}
{...marginProps}
{...commonProps}
{...buttonAriaProps}
{...linkAriaProps}
/>
<IconOnlyJumpButton
buttonLabel="test"
url="test"
disabled
small
large
appearance="primary"
danger
target="test"
type="button"
onClick={(_e: SyntheticEvent<HTMLButtonElement>) => {}}
onSelfWindowNavigation={() => {}}
rel="test"
{...commonProps}
/>
<IconOnlyBackwardButton
buttonLabel="test"
url="test"
disabled
small
large
appearance="primary"
danger
target="test"
type="button"
onClick={(_e: SyntheticEvent<HTMLButtonElement>) => {}}
onSelfWindowNavigation={() => {}}
rel="test"
{...commonProps}
/>
<InlineLink
href="test"
target="test"
rel="test"
onSelfWindowNavigation={() => {}}
onClick={(
_e: SyntheticEvent<HTMLAnchorElement | HTMLButtonElement>
) => {}}
{...buttonAriaProps}
{...linkAriaProps}
{...commonProps}
>
<div />
</InlineLink>
<JumpButton
url="test"
disabled
small
large
appearance="primary"
danger
target="test"
type="button"
onClick={(_e: SyntheticEvent<HTMLButtonElement>) => {}}
onSelfWindowNavigation={() => {}}
rel="test"
{...marginProps}
{...commonProps}
>
<div />
</JumpButton>
<BackwardButton
url="test"
disabled
small
large
appearance="primary"
danger
type="button"
onClick={(_e: SyntheticEvent<HTMLButtonElement>) => {}}
onSelfWindowNavigation={() => {}}
rel="test"
{...marginProps}
{...commonProps}
>
<div />
</BackwardButton>
<LeftIconButton
IconComponent={StatusIcon}
primary
danger
disabled
small
type="button"
onClick={(_e: SyntheticEvent<HTMLButtonElement>) => {}}
{...marginProps}
{...commonProps}
>
<div />
</LeftIconButton>
<ListButton
selectableItemIndex={0}
selected
href="test"
target="_blank"
rel="test"
statusIconText="test"
statusIconType="done"
actionButton
LeftIconComponent={StatusIcon}
FarRightIconComponent={StatusIcon}
bgTransparent
onClick={(
_e: SyntheticEvent<HTMLButtonElement | HTMLAnchorElement>
) => {}}
onKeyDown={(
_e: SyntheticKeyboardEvent<HTMLButtonElement | HTMLAnchorElement>
) => {}}
selectableItemRef={React.createRef<
HTMLAnchorElement | HTMLButtonElement
>()}
{...marginProps}
{...commonProps}
>
<div />
</ListButton>
<PagerButton
current
disabled
onClick={(_e: SyntheticEvent<HTMLButtonElement>) => {}}
small
{...marginProps}
{...commonProps}
>
<div />
</PagerButton>
<RightIconButton
IconComponent={StatusIcon}
primary
danger
disabled
small
type="button"
onClick={(_e: SyntheticEvent<HTMLButtonElement>) => {}}
{...marginProps}
{...commonProps}
>
<div />
</RightIconButton>
<TabButton current small {...marginProps} {...commonProps}>
<div />
</TabButton>
<TextButton
IconComponent={StatusIcon}
iconPosition="left"
id="test"
url="test"
target="_blank"
rel="test"
noBorder
disabled
small
onClick={(
_e: SyntheticEvent<HTMLSpanElement | HTMLAnchorElement>
) => {}}
{...buttonAriaProps}
{...marginProps}
{...commonProps}
>
<div />
</TextButton>
{/* forms */}
<CheckBox
name="test"
value="test"
checked
required
autofocus
disabled
error
small
{...inputHandlers}
{...marginProps}
{...commonProps}
>
<div />
</CheckBox>
<RadioButton
name="test"
value="test"
checked
required
autofocus
disabled
error
small
{...inputHandlers}
{...marginProps}
{...commonProps}
>
<div />
</RadioButton>
<SearchField
label="test"
labelledby="test"
placeholder="test"
name="test"
value="test"
required
autofocus
disabled
error
small
large
{...inputHandlers}
{...marginProps}
{...commonProps}
/>
<SelectBox
label="test"
labelledby="test"
id="selectbox-test"
options={[
{ name: 'test', value: 'test' },
{ name: 'test', options: [{ name: 'test', value: 'test' }] },
]}
name="test"
required
autofocus
disabled
error
small
large
alignCenter
alignRight
width="small"
value="test"
defaultValue="test"
autoComplete="on"
onChange={(_e: SyntheticInputEvent<HTMLSelectElement>) => {}}
onInput={(_e: SyntheticInputEvent<HTMLSelectElement>) => {}}
onFocus={(_e: SyntheticFocusEvent<HTMLSelectElement>) => {}}
onBlur={(_e: SyntheticFocusEvent<HTMLSelectElement>) => {}}
onKeyDown={(_e: SyntheticKeyboardEvent<HTMLSelectElement>) => {}}
onKeyUp={(_e: SyntheticKeyboardEvent<HTMLSelectElement>) => {}}
{...marginProps}
{...commonProps}
/>
<TextArea
id="textarea-test"
label="test"
labelledby="test"
placeholder="test"
name="test"
value="test"
defaultValue="text"
required
autofocus
disabled
error
small
large
autoComplete="on"
onChange={(_e: SyntheticInputEvent<HTMLTextAreaElement>) => {}}
onInput={(_e: SyntheticInputEvent<HTMLTextAreaElement>) => {}}
onFocus={(_e: SyntheticFocusEvent<HTMLTextAreaElement>) => {}}
onBlur={(_e: SyntheticFocusEvent<HTMLTextAreaElement>) => {}}
onKeyDown={(_e: SyntheticKeyboardEvent<HTMLTextAreaElement>) => {}}
onKeyUp={(_e: SyntheticKeyboardEvent<HTMLTextAreaElement>) => {}}
{...marginProps}
{...commonProps}
/>
<TextField
id="textfield-test"
label="test"
labelledby="test"
placeholder="test"
name="test"
value="test"
required
autofocus
disabled
error
small
large
alignCenter
alignRight
maxLength={100}
width="small"
autoComplete="on"
borderless
ref={React.createRef<HTMLInputElement>()}
IconComponent={() => <div />}
onIconClick={(_e) => {}}
onIconFocus={(_e) => {}}
onIconBlur={(_e) => {}}
iconLabel="iconlabel"
iconAriaProps={buttonAriaProps}
{...inputHandlers}
{...numberInputProps}
{...numberInputAriaProps}
{...marginProps}
{...commonProps}
/>
{/* grids */}
<GridBlock size="half" {...commonProps}>
<div />
</GridBlock>
<GridWrapper {...marginProps} {...commonProps}>
<div />
</GridWrapper>
<ToggleButton
type="checkbox"
name="test"
value="test"
toggled
small
disabled
{...commonProps}
>
<div />
</ToggleButton>
<OptionButton id="test" checked size="medium">
<div />
</OptionButton>
<FormControlLabel
id="formcontrollabel-test"
htmlFor="formcontrollabel-test-for"
required
{...commonProps}
>
あああ
</FormControlLabel>
{/* headlines */}
<Note inline textAlign="left" ellipsis {...marginProps} {...commonProps}>
<div />
</Note>
<PageTitle
id="test"
inline
textAlign="left"
headlineLevel={1}
{...marginProps}
{...commonProps}
>
<div />
</PageTitle>
<Paragraph
inline
textAlign="left"
ellipsis
{...marginProps}
{...commonProps}
>
<div />
</Paragraph>
<SectionTitle
id="test"
inline
textAlign="left"
headlineLevel={1}
{...marginProps}
{...commonProps}
>
<div />
</SectionTitle>
<SubSectionTitle
id="test"
inline
textAlign="left"
headlineLevel={1}
{...marginProps}
{...commonProps}
>
<div />
</SubSectionTitle>
<Text size={0.75} weight="bold" color="P5" {...commonProps}>
<div />
</Text>
{/* icons */}
<Avatar size="small" imageUrl="hoge" {...commonProps} />
<MaterialIcon
label="hoge"
small
IconComponent={StatusIcon}
{...commonProps}
/>
<RequiredIcon {...commonProps} />
<StatusIcon type="error" {...commonProps}>
<div />
</StatusIcon>
{/* images */}
<AlertSwallow {...commonProps} {...marginProps} />
<AppStoreBadge {...commonProps} {...marginProps} />
<CloudSkeletonIllust {...commonProps} {...marginProps} />
<CloudUploadIllust {...commonProps} {...marginProps} />
<CsvUploadIllust {...commonProps} {...marginProps} />
<FileUploadIllust {...commonProps} {...marginProps} />
<GooglePlayBadge {...commonProps} {...marginProps} />
<ImageUploadIllust {...commonProps} {...marginProps} />
<NotFoundSwallow {...commonProps} {...marginProps} />
{/* interactiveParts */}
<CalendarDate
date="test"
dateLabel="test"
today
disabled
secondaryHoliday
primaryHoliday
nationalHoliday
content={{
type: 'TimeRecord',
date: 'test',
status: 'alert',
dateLabel: 'test',
openingTime: '2019-01-01',
endingTime: '2019-12-31',
}}
onClickDate={(_date: string) => {}}
selectableDateRef={(_el: HTMLDivElement) => {}}
onKeyDown={(
_e: SyntheticKeyboardEvent<HTMLDivElement>,
_date: string,
_onClickDate?: (date: string) => void | Promise<void>
) => {}}
{...commonProps}
/>
<SegmentControlButton
current
small
large
onClick={(_e: SyntheticEvent<HTMLButtonElement>) => {}}
{...commonProps}
>
<div />
</SegmentControlButton>
<StepNumber number="1" current done disabled small {...commonProps} />
<StepBlock
number="test"
current
done
disabled
small
{...marginProps}
{...commonProps}
>
<div />
</StepBlock>
<StepBorder done separator {...commonProps} />
<Tab
current
small
selected
onClick={(_e: SyntheticEvent<HTMLButtonElement>) => {}}
onBlur={(_e: SyntheticEvent<HTMLButtonElement>) => {}}
{...marginProps}
{...commonProps}
>
<div />
</Tab>
{/* layouts */}
<WithDescriptionContent
position="vertical"
renderContent={(_: string) => <div />}
renderDescriptionContent={() => <div />}
{...commonProps}
/>
<WithSideContent
sideContent={<div />}
verticalAlign="top"
{...commonProps}
>
<div />
</WithSideContent>
<Stack
direction="horizontal"
gap={1}
alignItems="start"
justifyContent="start"
wrap="nowrap"
{...commonProps}
>
<div />
</Stack>
{/* lists */}
<BorderTableListCell
small
alignCenter
alignRight
onClick={() => {}}
{...commonProps}
>
<div />
</BorderTableListCell>
<CheckBoxCell
header
name="test"
value="test"
checked
disabled
aria-label="test"
onChange={(_e: SyntheticInputEvent<HTMLInputElement>) => {}}
{...commonProps}
/>
<DescriptionListCell {...commonProps}>
<div />
</DescriptionListCell>
<DescriptionListHeadCell minWidth={1} {...commonProps}>
<div />
</DescriptionListHeadCell>
<TableListCell
small
alignCenter
alignRight
url="/hoge/fuga"
onSelfWindowNavigation={() => {}}
colSpan={2}
indentLevel={2}
{...commonProps}
>
<div />
</TableListCell>
<TableListHead fixedHeader fixedHeaderOffset={42} {...commonProps}>
<div />
</TableListHead>
<TableListHeadCell
alignCenter
alignRight
ordering="asc"
onClick={() => {}}
minWidth={1}
{...commonProps}
>
<div />
</TableListHeadCell>
<TableListRow
onClick={() => {}}
url="/hoge/fuga"
onSelfWindowNavigation={() => {}}
{...tableRowAriaProps}
{...commonProps}
>
<div />
</TableListRow>
{/* messages */}
<Message error notice success info {...marginProps} {...commonProps}>
test
</Message>
{/* ProgressBar */}
<ProgressBar
value={1}
maxValue={100}
width="full"
indeterminate
{...commonProps}
/>
{/* Loading */}
<Loading isLoading={false}>children</Loading>
<Loading isLoading={true}>children</Loading>
<Loading isLoading={true} coverAll />
<Loading isLoading={true} coverAll message="hoge" />
{/* InlineSpinner */}
<InlineSpinner isLoading={false} large={false} {...commonProps} />
</div>
);
}
================================================
FILE: __tests__/types/flow/lv2.js
================================================
// @flow
import * as React from 'react';
import {
TextField,
RadioButton,
CheckBox,
CardNavigation,
SelectBox,
TextArea,
StatusIcon,
AccordionPanel,
BasicTable,
Breadcrumbs,
BulletedList,
ButtonGroup,
Calendar,
DatePicker,
SingleComboBox,
MultiComboBox,
DescriptionList,
MessageDialog,
MessageDialogConfirm,
TaskDialog,
GuideDialog,
Dropdown,
DropdownMenuContent,
DropdownButton,
NoDataCreated,
NoSearchResults,
FileUploader,
FileDropArea,
FilterableDropdownButton,
Footer,
DateField,
FormActions,
FormControl,
FormControlGroup,
PhoneNumberField,
NameField,
DigitsInput,
DateInput,
NumeralCodeInput,
PasswordField,
GlobalNavi,
Header,
HeadlineArea,
LineSeparatedList,
ListButtons,
ListButtonSelector,
ListCard,
ListTable,
GroupedListTable,
MessageBlock,
FloatingMessageBlock,
MessageIcon,
NumericTable,
Pagination,
Pager,
PageSelector,
Paragraph,
PopupProgressBar,
StackedBarChart,
Stepper,
VerticalSteps,
TabBar,
TagBox,
MiniTag,
WithAccordionContent,
WithBalloon,
WithDropdown,
WithFilterableDropdown,
WithPopup,
SkeletonPageTitle,
SkeletonSectionTitle,
SkeletonParagraph,
SkeletonRectangle,
SkeletonBlock,
SkeletonIcon,
SkeletonCircle,
SkeletonListTable,
SkeltonPageTitle,
SkeltonSectionTitle,
SkeltonParagraph,
SkeltonRectangle,
SkeltonBlock,
SkeltonIcon,
SkeltonCircle,
SkeltonListTable,
SkeletonDescriptionList,
SkeletonStackedBarChart,
WithTOC,
// eslint-disable-next-line import/no-unresolved
} from 'vibes/index';
import type {
ComboBoxOption,
SingleComboBoxOption,
MultiComboBoxOption,
} from 'vibes/index';
const marginProps = {
marginLeft: true,
marginRight: true,
marginBottom: true,
marginTop: true,
marginSize: 'large',
};
const commonProps = {
'data-guide': 'guide',
'data-test': 'test',
'data-tracking': 'tracking',
'data-masking': true,
ma: 1,
mt: 1,
mb: 1,
mr: 1,
ml: 1,
};
const formHandlers = {
onChange: (_e: SyntheticInputEvent<*>) => {},
onInput: (_e: SyntheticInputEvent<*>) => {},
onFocus: (_e: SyntheticFocusEvent<*>) => {},
onBlur: (_e: SyntheticFocusEvent<*>) => {},
onKeyDown: (_e: SyntheticKeyboardEvent<*>) => {},
};
const comboBoxOptions: ComboBoxOption[] = [
{ label: 'hoge', id: 1, keywords: ['hoge', 'fuga'] },
{ label: 'foo', id: 2 },
];
const singleComboBoxOptions: SingleComboBoxOption[] = [
{ label: 'hoge', id: 1, keywords: ['hoge', 'fuga'], subLabel: 'bar' },
{ label: 'foo', id: 2 },
];
export default function App() {
return (
<div className="App">
{/* lv2/accordionPanel */}
<AccordionPanel
title="test"
open
small
{...marginProps}
{...commonProps}
onClick={() => {}}
>
<div />
</AccordionPanel>
{/* lv2/basicTable */}
<BasicTable
margins={marginProps}
headers={[
{
value: 'test',
alignCenter: true,
alignRight: true,
ordering: 'asc',
onClick: () => {},
minWidth: 1,
},
{ value: <a href="#test">test</a> },
]}
rows={[
{
onClick: (_rowIndex: number) => {},
onSelfWindowNavigation: () => {},
cells: [
{
value: 1,
alignCenter: true,
alignRight: true,
small: true,
},
{ value: '2' },
],
},
{
cells: [{ value: 'link' }],
url: 'http://example.com',
},
]}
{...commonProps}
/>
{/* lv2/breadcrumbs */}
<Breadcrumbs
links={[{ title: 'test', url: 'test', onClickNavigator: () => {} }]}
label="breadcrumbs"
{...marginProps}
{...commonProps}
/>
{/* lv2/bulletedList */}
<BulletedList
listContents={[
{
value: 'test',
url: 'test',
target: '_self',
rel: 'test',
},
{
value: 'test',
'data-guide': 'guide',
'data-test': 'test',
'data-tracking': 'tracking',
'data-masking': true,
},
]}
small
{...marginProps}
{...commonProps}
/>
{/* lv2/buttonGroup */}
<ButtonGroup responsive align="left" {...marginProps} {...commonProps}>
<div />
</ButtonGroup>
{/* lv2/calendar */}
<Calendar
year="2018"
month="1"
contents={[
{
type: 'timeRecord',
date: '2019-01-01',
status: 'notice',
dateLabel: 'test',
openingTime: '2019-01-01',
endingTime: '2019-12-31',
},
]}
startFromMonday
nationalHolidays={['test']}
onClickDate={(_date: string) => {}}
{...commonProps}
/>
<DatePicker
date="2019-12-01"
onDateClick={(_d: string) => {}}
minDate="2012-07-09"
maxDate="2019-12-17"
{...commonProps}
/>
{/* lv2/comboBox */}
<SingleComboBox
value={{ label: 'hoge', id: 1, keywords: ['hoge', 'fuga'] }}
options={comboBoxOptions}
id="single-combobox-test"
label="test"
labelledby="test"
placeholder="test"
name="test"
required
disabled
error
small
large
width="large"
listWidth="xSmall"
emptyMessage={<div />}
borderless
trailingItem={{
onSelect: (_fieldValue: string) => {},
isVisible: (_fieledValue: string): boolean => {
return true;
},
render: (_fieldValue: string): React.Node => <div />,
}}
{...{ ...formHandlers, onChange: (_val?: ComboBoxOption) => {} }}
{...marginProps}
{...commonProps}
/>
<SingleComboBox
value={{
label: 'hoge',
id: 1,
keywords: ['hoge', 'fuga'],
subLabel: 'bar',
}}
options={singleComboBoxOptions}
id="single-combobox-test"
label="test"
labelledby="test"
placeholder="test"
name="test"
required
disabled
error
small
large
width="large"
listWidth="xSmall"
emptyMessage={<div />}
borderless
trailingItem={{
onSelect: (_fieldValue: string) => {},
isVisible: (_fieledValue: string): boolean => {
return true;
},
render: (_fieldValue: string): React.Node => <div />,
}}
{...{ ...formHandlers, onChange: (_val?: ComboBoxOption) => {} }}
{...marginProps}
{...commonProps}
/>
<MultiComboBox
values={[
{ label: 'hoge', id: 1, keywords: ['hoge', 'fuga'], color: 'RE' },
]}
options={[
{ label: 'hoge', id: 1, keywords: ['hoge', 'fuga'], color: 'RE' },
{ label: 'foo', id: 2, color: 'YE' },
]}
id="single-combobox-test"
label="test"
labelledby="test"
placeholder="test"
name="test"
required
disabled
error
width="large"
listWidth="xSmall"
maxSelectionCount={1}
emptyMessage={<div />}
borderless
{...{ ...formHandlers, onChange: (_val: MultiComboBoxOption[]) => {} }}
{...commonProps}
/>
{/* lv2/cardNavigation */}
<CardNavigation
navigationContents={[
{
title: 'test',
text: 'test',
url: '#',
IconComponent: StatusIcon,
},
{
title: 'test',
text: 'test',
url: '#',
IconComponent: StatusIcon,
},
{
title: 'test',
text: 'test',
url: '#',
IconComponent: StatusIcon,
},
]}
{...commonProps}
/>
{/* lv2/descriptionList */}
<DescriptionList
listContents={[
{
title: <div />,
value: <div />,
},
]}
headCellMinWidth={1}
{...marginProps}
{...commonProps}
/>
{/* lv2/dialogs */}
<MessageDialog
isOpen
title="test"
onRequestClose={(_e: SyntheticEvent<*>) => {}}
closeButtonLabel="test"
id="test"
contentAlign="left"
{...commonProps}
>
<div />
</MessageDialog>
<MessageDialogConfirm
isOpen
title="test"
onConfirm={(_e: SyntheticEvent<*>) => {}}
onRequestClose={(_e: SyntheticEvent<*>) => {}}
confirmButtonLabel="test"
closeButtonLabel="test"
confirmButtonHref="https://example.com"
confirmButtonTarget="_blank"
confirmButtonIconPosition="left"
confirmButtonIconComponent={StatusIcon}
danger
disabled
suspend
id="test"
contentAlign="left"
{...commonProps}
>
<div />
</MessageDialogConfirm>
<TaskDialog
isOpen
title="test"
onPrimaryAction={(_e: SyntheticEvent<*>) => {}}
onRequestClose={(_e: SyntheticEvent<*>) => {}}
primaryButtonLabel="test"
closeButtonLabel="test"
danger
disabled
id="test"
headerSideContent={<div />}
contentLabel="test"
{...commonProps}
>
<div />
</TaskDialog>
<GuideDialog
isOpen
title="test"
type="start"
stepCount={1}
totalStepCount={100}
onRequestBackward={(_e: SyntheticMouseEvent<>) => {}}
backwardButtonLabel="test"
onRequestForward={(_e: SyntheticMouseEvent<>) => {}}
forwardButtonLabel="test"
onRequestClose={(_e: SyntheticMouseEvent<>) => {}}
closeButtonLabel="test"
image={{
src: 'test',
width: 'test',
height: 'test',
alt: 'test',
}}
id="test"
{...commonProps}
>
<div />
</GuideDialog>
{/* lv2/dropdown */}
<Dropdown
contents={[
{
type: 'rule',
},
{
type: 'textOnly',
text: 'test',
},
{
type: 'checkbox',
text: 'test',
value: 'test',
name: 'test',
onChange: (_e: SyntheticInputEvent<HTMLInputElement>) => {},
checked: true,
disabled: true,
},
{
type: 'selectable',
text: 'test',
ariaLabel: 'test',
onClick: (_e: SyntheticEvent<>) => {},
target: '_blank',
rel: 'test',
},
]}
firstSelectableItemRef={(
_el: ?React.ElementRef<'button' | 'a' | 'input'>
) => {}}
onRequestClose={() => {}}
align="right"
positionRelative
{...commonProps}
/>
<DropdownMenuContent
contents={[
{
type: 'rule',
},
{
type: 'textOnly',
text: 'test',
},
{
type: 'checkbox',
text: 'test',
value: 'test',
name: 'test',
onChange: (_e: SyntheticInputEvent<HTMLInputElement>) => {},
checked: true,
disabled: true,
},
{
type: 'selectable',
text: 'test',
ariaLabel: 'test',
onClick: (_e: SyntheticEvent<>) => {},
target: '_blank',
rel: 'test',
},
]}
firstSelectableItemRef={(
_el: ?React.ElementRef<'button' | 'a' | 'input'>
) => {}}
onRequestClose={() => {}}
onFocusOut={(_direction: 'upward' | 'downward') => {}}
{...commonProps}
/>
{/* lv2/dropdownButton */}
<DropdownButton
buttonLabel="button"
disabled
small
large
appearance="tertiary"
iconPosition="right"
IconOnlyComponent={StatusIcon}
dropdownContents={[
{
type: 'rule',
},
{
type: 'textOnly',
text: 'test',
},
{
type: 'selectable',
text: 'test',
ariaLabel: 'test',
onClick: (_e: SyntheticEvent<>) => {},
},
]}
{...marginProps}
{...commonProps}
/>
{/* lv2/emptyStates} */}
<NoDataCreated
actionWord="登録"
objectName="hoge"
image={{ src: 'aaa', width: '100', height: '100' }}
mainText="aaa"
subText="aaa"
>
<div />
</NoDataCreated>
<NoSearchResults
objectName="hoge"
image={{ src: 'aaa', width: '100', height: '100' }}
mainText="aaa"
subText="aaa"
/>
{/* lv2/fileUploader */}
<FileUploader
onFileSelect={() => {}}
acceptFileTypes={['text/csv']}
isUploading
fileLabel="test"
multiple
{...marginProps}
{...commonProps}
/>
{/* lv2/fileDropArea */}
<FileDropArea
onFileSelect={() => {}}
acceptFileTypes={['text/csv']}
isUploading
fileLabel="test"
multiple
{...commonProps}
>
<div />
</FileDropArea>
{/* lv2/filterableDropdownButton */}
<FilterableDropdownButton
buttonLabel="button"
disabled
small
large
appearance="tertiary"
dropdownContents={[
{
type: 'selectable',
text: 'test',
ariaLabel: 'test',
onClick: (_e: SyntheticEvent<>) => {},
keywords: ['aaa'],
},
{
type: 'checkbox',
text: 'test',
onChange: (_e: SyntheticEvent<>) => {},
keywords: ['aaa'],
},
]}
iconPosition="left"
IconComponent={StatusIcon}
{...commonProps}
/>
{/* lv2/footer */}
<Footer
links={[{ title: 'test', url: 'test' }]}
AppStoreUrl="test"
GooglePlayUrl="test"
disableAppStoreBadge
disableGooglePlayBadge
copyright="Copyright 2012-2019 © freee K.K."
{...commonProps}
/>
{/* lv2/formBlock */}
<DateField
selectedDate="2019-01-01"
startDate="2019-01-01"
endDate="2019-01-01"
small
disabled
error
required
autoComplete="bday"
nullable
onChange={(_date: string | null) => {}}
onInput={(_date: string | null) => {}}
onFocus={(_date: string | null) => {}}
onBlur={(_date: string | null) => {}}
onKeyDown={(_date: string | null) => {}}
{...marginProps}
{...commonProps}
/>
<FormActions responsive fixed>
<div />
</FormActions>
<FormActions responsive position="fixed">
<div />
</FormActions>
<FormControl
id="formControl1"
label="test"
required
{...marginProps}
{...commonProps}
>
<TextField />
<RadioButton />
<CheckBox />
<SelectBox />
<TextArea />
<NameField />
<PhoneNumberField />
<DateField
selectedDate="2019-01-01"
startDate="2019-01-01"
endDate="2019-01-01"
/>
</FormControl>
<FormControlGroup {...commonProps}>
<FormControl>
<TextField />
</FormControl>
</FormControlGroup>
<NameField
lastName="Doe"
firstName="John"
lastNamePlaceholder="last name"
firstNamePlaceholder="first name"
label="name"
labelledby="idref"
disabled
required
error
small
autoComplete="name"
onChange={(
_key: 'lastName' | 'firstName',
_e: SyntheticInputEvent<HTMLInputElement>
) => {}}
onInput={(
_key: 'lastName' | 'firstName',
_e: SyntheticInputEvent<HTMLInputElement>
) => {}}
onFocus={(
_key: 'lastName' | 'firstName',
_e: SyntheticFocusEvent<HTMLInputElement>
) => {}}
onBlur={(
_key: 'lastName' | 'firstName',
_e: SyntheticFocusEvent<HTMLInputElement>
) => {}}
onKeyDown={(
_key: 'lastName' | 'firstName',
_e: SyntheticKeyboardEvent<HTMLInputElement>
) => {}}
{...marginProps}
{...commonProps}
/>
<PhoneNumberField
phoneNumberA="000"
phoneNumberB="0000"
phoneNumberC="0000"
placeholderA="000"
placeholderB="0000"
placeholderC="0000"
disabled
error
small
autoComplete="tel"
onChange={(
_key: 'a' | 'b' | 'c',
_e: SyntheticInputEvent<HTMLInputElement>
) => {}}
onInput={(
_key: 'a' | 'b' | 'c',
_e: SyntheticInputEvent<HTMLInputElement>
) => {}}
onFocus={(
_key: 'a' | 'b' | 'c',
_e: SyntheticFocusEvent<HTMLInputElement>
) => {}}
onBlur={(
_key: 'a' | 'b' | 'c',
_e: SyntheticFocusEvent<HTMLInputElement>
) => {}}
onKeyDown={(
_key: 'a' | 'b' | 'c',
_e: SyntheticKeyboardEvent<HTMLInputElement>
) => {}}
{...marginProps}
{...commonProps}
/>
{/* lv2/formFields */}
{['xSmall', 'small', 'medium', 'large', 'full'].map((width) => (
<DigitsInput
key={width}
value={0}
nullable
id="id_digitsInput"
label="DigitsInput"
labelledby="label_digitsInput"
name="name_digitsInput"
required
disabled
error
small
borderless
width={width}
autoComplete="off"
onChange={(_v: number | null) => {}}
onFocus={(_e: SyntheticFocusEvent<HTMLInputElement>) => {}}
onBlur={(_e: SyntheticFocusEvent<HTMLInputElement>) => {}}
onInput={(_e: SyntheticInputEvent<HTMLInputElement>) => {}}
onKeyDown={(_e: SyntheticKeyboardEvent<HTMLInputElement>) => {}}
{...marginProps}
{...commonProps}
/>
))}
{['xSmall', 'small', 'medium', 'large', 'full'].map((width) => (
<DateInput
key={width}
value="2019-01-01"
id="dateinput-test"
label="test"
labelledby="test"
name="test"
required
disabled
error
small
minDate="2012-07-09"
maxDate="2019-12-17"
width={width}
{...{ ...formHandlers, onChange: (_d: string) => {} }}
{...marginProps}
{...commonProps}
/>
))}
<NumeralCodeInput
id="textfield-test"
label="test"
labelledby="test"
placeholder="test"
name="test"
value="test"
required
autofocus
disabled
error
small
large
alignCenter
alignRight
maxLength={100}
width="small"
autoComplete="on"
ref={React.createRef<HTMLInputElement>()}
onChange={(_v: string) => {}}
onInput={(_e: SyntheticInputEvent<HTMLInputElement>) => {}}
onFocus={(_e: SyntheticFocusEvent<HTMLInputElement>) => {}}
onBlur={(_e: SyntheticFocusEvent<HTMLInputElement>) => {}}
onKeyDown={(_e: SyntheticKeyboardEvent<HTMLInputElement>) => {}}
onKeyUp={(_e: SyntheticKeyboardEvent<HTMLInputElement>) => {}}
{...commonProps}
/>
<PasswordField
id="passwordfield-test"
label="test"
labelledby="test"
placeholder="test"
name="test"
value="test"
required
autofocus
disabled
error
small
large
alignCenter
alignRight
width="small"
autoComplete="current-password"
ref={React.createRef<HTMLInputElement>()}
onChange={(_e: SyntheticInputEvent<HTMLInputElement>) => {}}
onInput={(_e: SyntheticInputEvent<HTMLInputElement>) => {}}
onFocus={(_e: SyntheticFocusEvent<HTMLInputElement>) => {}}
onBlur={(_e: SyntheticFocusEvent<HTMLInputElement>) => {}}
onKeyDown={(_e: SyntheticKeyboardEvent<HTMLInputElement>) => {}}
onKeyUp={(_e: SyntheticKeyboardEvent<HTMLInputElement>) => {}}
{...commonProps}
/>
{/* lv2/globalNavi */}
<GlobalNavi
links={[
{
title: 'test',
url: 'test',
IconComponent: StatusIcon,
current: true,
'data-guide': 'guide',
'data-test': 'test',
'data-tracking': 'tracking',
'data-masking': true,
},
]}
searchUrl="https://support.freee.co.jp/hc/ja/search"
{...commonProps}
/>
{/* lv2/header */}
<Header
logo={<div />}
sectionDataList={[
{
type: 'plan',
text: 'test',
url: 'test',
IconComponent: StatusIcon,
iconType: 'default',
dropdownContents: [
{
type: 'selectable',
text: 'test',
ariaLabel: 'test',
onClick: (_: SyntheticEvent<>) => {},
},
],
},
{
type: 'dropdown',
text: 'test',
IconComponent: StatusIcon,
hasBadge: true,
dropdownContents: [
{
type: 'selectable',
text: <p>test</p>,
ariaLabel: 'test',
onClick: (_: SyntheticEvent<>) => {},
unread: true,
},
{
type: 'textOnly',
text: <p>test</p>,
},
],
},
]}
{...commonProps}
/>
<Header
logo={<div />}
sectionDataList={[
{
type: 'plan',
text: 'test',
url: 'test',
IconComponent: StatusIcon,
iconType: 'default',
dropdownContents: [
{
type: 'selectable',
text: 'test',
ariaLabel: 'test',
onClick: (_: SyntheticEvent<>) => {},
},
],
},
{
type: 'dropdown',
text: 'test',
IconComponent: StatusIcon,
hasBadge: true,
dropdownContents: [
{
type: 'selectable',
text: <p>test</p>,
ariaLabel: 'test',
onClick: (_: SyntheticEvent<>) => {},
unread: true,
},
{
type: 'textOnly',
text: <p>test</p>,
},
],
},
]}
{...commonProps}
/>
{/* lv2/headlineArea */}
<HeadlineArea
pageTitle="test"
statusIconType="done"
statusIconText="test"
relatedMenus={<div />}
{...commonProps}
>
<div />
</HeadlineArea>
{/* lv2/lineSeparatedList */}
<LineSeparatedList
listContents={[
{
value: 'test',
},
{
value: <Paragraph>test</Paragraph>,
},
]}
{...commonProps}
/>
{/* lv2/listButtons */}
<ListButtons
buttons={[
{
label: <div />,
selected: true,
href: 'test',
target: '_blank',
rel: 'test',
statusIconText: 'test',
statusIconType: 'success',
bgTransparent: true,
onClick: (_e: SyntheticEvent<HTMLButtonElement>) => {},
},
]}
onKeyDown={(
_e: SyntheticKeyboardEvent<HTMLButtonElement | HTMLAnchorElement>
) => {}}
selectableItemRef={React.createRef<
HTMLButtonElement | HTMLAnchorElement
>()}
{...marginProps}
{...commonProps}
/>
{/* lv2/listButtonSelector */}
<ListButtonSelector
buttons={[
{
label: <div />,
selected: true,
href: 'test',
target: '_blank',
rel: 'test',
statusIconText: 'test',
statusIconType: 'success',
bgTransparent: true,
onClick: (_e: SyntheticEvent<HTMLButtonElement>) => {},
},
]}
selectedLabel="test"
selectorLabel="test"
withActionButton
actionLabel="test"
action={() => {}}
ActionIconComponent={StatusIcon}
disabled
{...marginProps}
{...commonProps}
></ListButtonSelector>
{/* lv2/listCard */}
<ListCard
title="test"
url="test"
onClick={(_e: SyntheticEvent<HTMLButtonElement>) => {}}
actions={<div />}
thumbnail={<div />}
target="test"
rel="test"
{...commonProps}
>
<div />
</ListCard>
{/* lv2/listTable */}
<ListTable
margins={marginProps}
headers={[
{
value: 'test',
alignCenter: true,
alignRight: true,
ordering: 'asc',
onClick: () => {},
minWidth: 1,
'data-test': 'test',
},
{ value: <a href="#test">test</a> },
]}
rows={[
{
onClick: (_rowIndex: number) => {},
onSelfWindowNavigation: () => {},
onChangeCheckBox: (_e: SyntheticInputEvent<HTMLInputElement>) => {},
'data-test': 'test',
cells: [
{
value: 1,
alignCenter: true,
alignRight: true,
small: true,
colSpan: 2,
'data-masking': true,
'data-test': 'test',
},
{ value: '2' },
],
},
{
cells: [{ value: 'link' }],
url: 'http://example.com',
},
]}
onChangeHeaderCheckBox={(
_e: SyntheticInputEvent<HTMLInputElement>
) => {}}
fixedHeader
fixedHeaderOffset={1}
{...commonProps}
/>
<GroupedListTable
withCheckBox
foldable
headers={[
{
value: 'test',
alignCenter: true,
alignRight: true,
ordering: 'asc',
onClick: () => {},
minWidth: 1,
},
{ value: <a href="#test">test</a> },
]}
rowGroups={[
{
summaryCells: [
{
value: 1,
alignCenter: true,
alignRight: true,
small: true,
colSpan: 2,
'data-test': 'test',
},
{ value: '2' },
],
rows: [
{
onClick: (_rowIndex: number) => {},
onSelfWindowNavigation: () => {},
onChangeCheckBox: (
_e: SyntheticInputEvent<HTMLInputElement>
) => {},
cells: [
{
value: 1,
alignCenter: true,
alignRight: true,
small: true,
colSpan: 2,
},
{ value: '2' },
],
},
{
cells: [{ value: 'link' }],
url: 'http://example.com',
},
],
onClick: () => {},
onChangeCheckBox: (_e: SyntheticInputEvent<HTMLInputElement>) => {},
folded: true,
onToggleFolded: (_b: boolean) => {},
'data-test': 'test',
},
]}
onChangeHeaderCheckBox={(
_e: SyntheticInputEvent<HTMLInputElement>
) => {}}
{...commonProps}
/>
{/* lv2/messageBlock */}
<MessageBlock
linkTitle="test"
linkUrl="test"
linkTarget="_blank"
linkRel="test"
onRequestClose={() => {}}
message="test"
hover
error
notice
success
info
{...marginProps}
/>
<FloatingMessageBlock
message="test"
error
notice
success
info
onClose={(_autoClose: boolean) => {}}
{...commonProps}
/>
<MessageBlock
linkTitle="test"
linkUrl="test"
message="test"
hover
error
notice
success
info
{...marginProps}
/>
{/* lv2/messageIcon */}
<MessageIcon label="hoge" error success notice small {...marginProps}>
message
</MessageIcon>
{/* lv2/numericTable */}
<NumericTable
headers={[
{
value: <div />,
alignCenter: true,
alignRight: true,
ordering: 'asc',
onClick: () => {},
},
]}
rows={[
{
cells: [
{
value: <div />,
alignCenter: true,
alignRight: true,
small: true,
onClick: (_rowIndex: number) => {},
},
],
},
]}
{...marginProps}
{...commonProps}
/>
{/* lv2/pagination */}
<Pagination
rowsPerPageOptions={[{ value: '10', name: '10件' }, { value: '20' }]}
rowsPerPageValue={10}
defaultRowsPerPageValue={10}
currentPage={1}
rowCount={99}
displayRowCount="100件以上"
selectBoxWidth="small"
disabled
onChange={() => {}}
onFocus={() => {}}
onBlur={() => {}}
{...commonProps}
/>
{/* lv2/pager */}
<Pager
currentPage={1}
onPageChange={(_page: number) => {}}
pageCount={1}
pageRange={10}
sidePageRange={10}
small
/>
{/* lv2/pageSelector */}
<PageSelector
prevDisabled
nextDisabled
onClickPrev={() => {}}
onClickNext={() => {}}
onClickTitle={() => {}}
renderPopup={(requestClose, firstSelectableItemRef) => (
<button onClick={requestClose} ref={firstSelectableItemRef} />
)}
hasDropdown
isExpanded
{...marginProps}
{...commonProps}
>
<div />
</PageSelector>
{/* lv2/popupProgressBar */}
<PopupProgressBar
status="doing"
message="メッセージ"
onClose={() => {}}
{...commonProps}
/>
{/* lv2/stackedBarChart */}
<StackedBarChart
items={[
{
color: 'RE',
label: 'label',
value: 1,
valueLabel: '1',
},
{
color: 'OR',
label: 'label',
value: 2,
valueLabel: '2',
},
]}
onClickItem={() => {}}
{...commonProps}
/>
{/* lv2/stepper */}
<Stepper
steps={['test', <div key={1} />]}
currentStepIndex={1}
disabledStepIndex={[1, 2, 3, 4, 5]}
small
{...commonProps}
/>
<VerticalSteps
steps={[
{
title: 'test',
content: <div />,
contentSummary: <div />,
actions: <div />,
},
]}
currentStepIndex={0}
stepHeadlineLevel={1}
{...commonProps}
/>
{/* lv2/tabbar */}
<TabBar
tabs={['test']}
currentTabIndex={1}
small
onClickTab={(_n: number) => {}}
{...commonProps}
/>
<TabBar
tabs={[
{
name: 'test',
'data-guide': 'guide',
'data-test': 'test',
'data-tracking': 'tracking',
'data-masking': true,
},
]}
currentTabIndex={1}
small
onClickTab={(_n: number) => {}}
{...commonProps}
/>
{/* lv2/tagbox */}
<TagBox type="hoge" removable onRemove={() => {}} {...commonProps}>
hoge
</TagBox>
<MiniTag type="hoge" removable onRemove={() => {}} {...commonProps}>
hoge
</MiniTag>
{/* lv2/withAccordionContent */}
<WithAccordionContent
renderAccordionButtonArea={(AccordionButton, contentId) => (
<AccordionButton contentId={contentId} open onClick={() => {}}>
アコーディオンボタン
</AccordionButton>
)}
isOpen
{...commonProps}
>
<div />
</WithAccordionContent>
{/* lv2/withBalloon */}
<WithBalloon
border="default"
balloonContent={<div />}
{...commonProps}
balloonDisabled
>
<div />
</WithBalloon>
<WithBalloon
border="default"
renderContent={(_balloonId: string) => <div />}
renderBalloonContent={(_isVisible: boolean) => <div />}
{...commonProps}
></WithBalloon>
{/* lv2/WithDropdown */}
<WithDropdown
disabled
dropdownContents={[
{
type: 'rule',
},
{
type: 'textOnly',
text: 'test',
},
{
type: 'selectable',
text: 'test',
ariaLabel: 'test',
onClick: (_e: SyntheticEvent<>) => {},
},
]}
renderButton={(dropdownId, isOpen, buttonRef) => (
<button
aria-controls={dropdownId}
aria-expanded={isOpen}
ref={buttonRef}
>
hoge
</button>
)}
{...commonProps}
/>
<WithFilterableDropdown
disabled
render={(popupId, isOpen, controlRef, togglePopup) => (
<button
aria-controls={popupId}
aria-expanded={isOpen}
ref={controlRef}
onFocus={togglePopup(true)}
>
hoge
</button>
)}
dropdownContents={[
{
type: 'selectable',
text: 'test',
ariaLabel: 'test',
onClick: (_e: SyntheticEvent<>) => {},
keywords: ['aaa'],
},
{
type: 'checkbox',
text: 'test',
onChange: (_e: SyntheticEvent<>) => {},
keywords: ['aaa'],
},
]}
{...commonProps}
/>
<WithPopup
disabled
render={(popupId, isOpen, controlRef, togglePopup) => (
<button
aria-controls={popupId}
aria-expanded={isOpen}
ref={controlRef}
onFocus={togglePopup(true)}
>
hoge
</button>
)}
renderPopup={(requestClose, firstSelectableItemRef) => (
<div>
<button
onClick={() => requestClose()}
ref={firstSelectableItemRef}
></button>
</div>
)}
/>
{/* lv2/skeleton */}
<SkeletonPageTitle {...commonProps} />
<SkeletonSectionTitle {...commonProps} />
<SkeletonParagraph size="medium" {...commonProps} />
<SkeletonRectangle size="default" {...commonProps} />
<SkeletonBlock size="medium" {...commonProps} />
<SkeletonIcon size="medium" {...commonProps} />
<SkeletonCircle size="medium" {...commonProps} />
<SkeletonListTable
columnCount={5}
rowCount={5}
rowCells={[{ value: <SkeletonIcon /> }]}
{...commonProps}
/>
<SkeletonStackedBarChart {...commonProps} />
<SkeltonPageTitle {...commonProps} />
<SkeltonSectionTitle {...commonProps} />
<SkeltonParagraph size="medium" {...commonProps} />
<SkeltonRectangle size="default" {...commonProps} />
<SkeltonBlock size="medium" {...commonProps} />
<SkeltonIcon size="medium" {...commonProps} />
<SkeltonCircle size="medium" {...commonProps} />
<SkeltonListTable
columnCount={5}
rowCount={5}
rowCells={[{ value: <SkeletonIcon /> }]}
{...commonProps}
/>
<SkeletonDescriptionList rowCount={5} {...commonProps} />
<WithTOC
containerID="WithTOC--Example"
contents={[
{ id: 'WithTOC--Example1', label: 'Lorem Ipsum', content: <></> },
]}
offsetTop={200}
onNavigateTo={(_: string) => {}}
/>
</div>
);
}
================================================
FILE: __tests__/types/flow/utilities.js
================================================
// @flow
import {
getFocusableElements,
isFocusableElement,
// eslint-disable-next-line import/no-unresolved
} from 'vibes/index';
const focusableElement = getFocusableElements(new HTMLElement());
focusableElement.length;
focusableElement.item(0);
focusableElement.entries();
focusableElement.keys();
focusableElement.values();
// $FlowExpectedError
focusableElement.hoge();
isFocusableElement(new HTMLElement());
================================================
FILE: docker/git-credential-github-token
================================================
#!/bin/sh
echo protocol=https
echo host=github.com
echo username=$GITHUB_USERNAME
echo password=$GITHUB_TOKEN
================================================
FILE: docs/Colors.stories.mdx
================================================
import { Meta } from "@storybook/addon-docs";
<Meta title="doc/Colors" />
# 色についての方針
vibes 内で使用する色、ならびに vibes を使用するプロダクトで使用する色は、[freee のブランドカラー](https://brand.freee.co.jp/designelements/color/)から選定してください。
また、コントラスト比の確保に留意してください([コントラスト比確保の重要性](https://a11y-guidelines.freee.co.jp/explanations/contrast.html))
- テキストコンテンツに関しては 4.5:1 のコントラスト比を確保してください
- テキストコンテンツ以外に関しては、画面の情報を読み解くのに必要な部分に関しては 3:1 のコントラスト比を確保してください
# vibes 内で使用されるセマンティックカラー
特定の意味を伝えるために、いくつかの色はその意味と紐付いた **セマンティックカラー** として定義します。
これらの意味を伝える場合、または色を使用する場合は、色と意味の組み合わせに一貫性があるようにしてください。
## P5 (<span style={{background: "#2864f0", color: "#FFFFFF"}}>#2864f0</span>)
- **[DO]** freee のブランドを象徴する色で、ロゴはこの色を使用します
- **[DO]** UI では「現在いる場所」「現在表示しているもの」「(処理などの)成功」を表現するのにも使用されています
- **[DO NOT]** P7 とあわせて、リンクやボタンの色と誤認しやすいため、「非インタラクティブ要素を目立たせるため」だけの使用は避けてください l
## P7 (<span style={{background: "#285ac8", color: "#FFFFFF"}}>#285ac8</span>)
- **[DO]** リンクやボタンに使用するために、P5 よりやや暗くしたメインカラーです
- **[DO NOT]** P5 とあわせて、リンクやボタンの色と誤認しやすいため、「非インタラクティブ要素を目立たせるため」だけの使用は避けてください l
## RE5 (<span style={{background: "#dc1e32", color: "#ffffff"}}>#dc1e32</span>)
- **[DO]** エラーの表現や、削除などの致命的な動作に使用してください
## YE7, YE10 (<span style={{background:"#be8c14", color: "#ffffff"}}>#be8c14</span>, <span style={{background:"#825a0f", color: "#ffffff"}}>#825a0f</span>)
- **[DO]** 先の挙動に注意が必要な場合など、ユーザーに注意をうながす際に使用してください
- **[DO NOT]** YE7 は白 #FFFFFF に対してのコントラスト比が 4.5:1 を満たしません。テキストを書く場合は「30px = 1.875rem 以上」または「22px = 1.375rem 以上の太字」にするか、YE10 を使用してください([freee アクセシビリティー・ガイドライン: コントラスト比の確保](https://a11y-guidelines.freee.co.jp/categories/text.html#gl-text-contrast))。
## GY7 (<span style={{background: "#323232", color: "#ffffff"}}>#323232</span>)
- **[DO]** 通常の文字色に使用してください
## S9 (<span style={{background: "#6e6b6b", color: "#ffffff"}}>#6e6b6b</span>)
- **[DO]** GY7 よりも目立たせたくない場所の文字色に使用してください
## S1 (<span style={{background:"#f7f5f5"}}>#f7f5f5</span>)
- **[DO]** コラム領域などテキストを乗せる領域の地色に使用してください
# 外部への提供
Color 定数として vibes 内に定義された 16 進数カラーコードを提供します。
変数名は歴史的な経緯で名付けられているものもあり、必ずしもその色の使用目的を表現していないことに注意してください。
## Examples
### with styled-components [css prop](https://styled-components.com/docs/api#css-prop)
```typescript
import * as React from 'react';
import { VibesBackgroundColor } from '@freee_jp/vibes';
// Defined: background-color: ${VibesBackgroundColor}
const Component: React.FC = ({ children }) => {
return (
<div
css={`
background-color: ${VibesBackgroundColor};
`}
>
{children}
</div>
);
};
export default Component;
```
### Example with styled-components basic
```typescript
import * as React from 'react';
import { VibesBackgroundColor } from '@freee_jp/vibes';
import styled from 'styled-components';
// Defined: background-color: ${VibesBackgroundColor}
const StyledInner = styled.div`
background-color: ${VibesBackgroundColor};
`;
const Component: React.FC = ({ children }) => {
return <StyledInner>{children}</StyledInner>;
};
export default Component;
```
================================================
FILE: docs/Contribution.stories.mdx
================================================
import { Meta } from "@storybook/addon-docs";
<Meta title='doc/Contribution' />
# vibes の開発に参加する
このドキュメントを読んでいただいてありがとうございます。
きっとこれを読んでいるあなたはvibesの開発に参加しようと思ってくれている方かなと思います。そういう人向けの情報を書いていきます。
## Issue を立てる
TBD
## Pull Request する
vibesは誰でもPull RequestしてOKです。あなたからのPull Requestをお待ちしております。
### 開発環境の準備
開発時に必要となる依存パッケージの導入、および初期セットアップを行ってください。
```sh
$ npm install
$ npm run setup
```
### コードの修正
- JavaScript (TypeScript) の修正が必要な場合は `src/` 内のファイルを修正してください
- スタイルシート (CSS, SCSS, SASS) を修正する場合は `stylesheets/` 内のファイルを修正してください
**ルートディレクトリ内にある `vibes_2021.css` や、`dist/` ディレクトリ内のファイルはビルド生成物です**。
これらのファイルはリリース時以外には変更する必要はありません。
あなたの修正がこれらのファイルに反映されていなくても、Pull RequestしてOKです。
### 動作確認する
動作確認は主にStorybookで行います。Storybookは以下の方法で立ち上がります。
```sh
# npm で立ち上げる場合
$ npm run storybook
# access to localhost:6006
```
## コーディング規約・方針
以下のページをそれぞれ参照してください。
- [TypeScript](?path=/docs/doc-typescript--page)
- [Stylesheets](?path=/docs/doc-stylesheets--page)
## リリース
### リリース手順
TBD
#### majorバージョンに該当する変更
vibesを利用する側のプロダクトで、バージョンアップ時に確認や変更が強いられるもの(破壊的変更が含まれるもの)は `major` ラベルを付与します。
- vibesを利用する側で、コードの変更が強いられるもの
- 既存コンポーネントの削除
- 既存のpropsの意味が変わってしまうもの
- vibesを利用する側で、画面デザインの変更が強いられるもの
- コンポーネントの外形の大きさが大きく変わってしまうような変更
- vibesを利用する側で設定の変更が必要となるもの
- ビルドまわりの設定変更が必要となりそうなもの
- peerDependenciesが増えるもの・減るもの
- vibes側で大きな方針の変更をするもの
- バージョン指定基準が決まるとか
- その他、盛大に祝ったり気持ちを盛り上げ(バイブスを上げ)たりしたいもの
#### minorバージョンに該当するもの
コンポーネントの追加・改善などを含み、vibesを利用するプロダクト側で変更や確認が強いられない場合には `minor` ラベルを付与します。
- コンポーネントの追加
- コンポーネントの機能改善
- propsの追加や、取り得る値の追加
- コンポーネントのデザイン改善
- その改善が加わることによってvibesを使う側の画面のデザインを変更する必要のないもの
- その他、新機能や機能改善がありつつ、vibesを使う側ではコードや画面デザインの変更が不要な変更
#### patchバージョンに該当するもの
`major`, `minor` に該当する変更がない場合は、 `patch` ラベルを付与してください。
- APIに変化のないバグフィックス
- vibes内で使用しているnpmモジュールのバージョンアップ
- vibes内のリファクタリング
- その他、新機能や機能改善でもなく、vibes利用側でのコードや画面デザインの不要な変更
================================================
FILE: docs/Design/Layout/Layout.stories.mdx
================================================
import { Meta } from "@storybook/addon-docs";
import widthImg from './width.png';
import heightImg from './height.jpg';
<Meta title='doc/Design/Layout' />
このドキュメントでは、標準画面サイズなど画面全体のレイアウトについての方針を記載します。
# 画面サイズ
## 最小コンテンツ幅
- デスクトップWeb向けの画面では最低画面幅1152pxで表示が崩れないように設計してください。
- 1152px(72rem) = コンテンツ幅1120px(70rem) + 左右マージン 16px(1rem)
- <img src={widthImg} alt="上記計算式の解説画像" width="480" />
- 会計Webにアクセスされる端末のほとんどが幅1280px以上(2021年9月現在で約98%)であるため、それよりも小さい範囲でrem計算しやすい値としてコンテンツ幅70remになっています
- 大きな表が求められるレポート画面など、コンテンツが1120pxでは収まらない場合、適宜その部分を横スクロールで表示できるようにしてください。
- 幅1120pxよりも小さい幅でレイアウトすることも可能です。
- 「1280pxの画面解像度でアクセスしたときに画面幅が足りずに表示崩れを起こしてしまう」のを防ぐ意図なので、それより小さいコンテンツ幅のほうが使いやすいと考えられる場合はそれに合わせてもらってOKです(下記の narrow とか)
## ブラウザ幅に合わせてコンテンツ幅をどう表示するか
コンテンツをブラウザの横幅にあわせて無尽蔵に広げられると、右端に配置したボタン等がユーザーの目に入りにくくなります。そのためコンテンツの最大幅を設定しています。
現在のVibesでは Container コンポーネントのオプションによって、以下からレイアウトを選択することができます。
- narrow: 640px = 40rem固定
- ウィザードUIなど、左右幅を大きく必要としない場合に使用します
- normal: 1120px = 70rem固定
- wide: ブラウザの左右端までコンテンツ幅が広がる(最小 1120px = 70rem、 最大 1600px = 100rem)
- 画面幅が大きいときにどこまでコンテンツ幅を大きくするかはその画面における情報量を考慮して判断してください。
- コレクション画面などの一行あたりの情報量が多いことが想定される場合には wide にすることを推奨しています。
## 画面高さ
ユーザーがスクロールしたりせずに見ることができるUIの範囲の高さ(ブラウザのウィンドウの縦幅)は630pxを前提にしてください。
- ファーストビューを考慮する必要がある設計の場合は630pxを前提にしてください。
- <img src={heightImg} alt="画面高さ630pxはブラウザのコンテンツ部分の上端から下端までに収まる高さとして設定されています" width="480"/>
- 会計Webにアクセスする端末のほとんどが高さ720px以上である(2021年9月現在で99.4%)ことから、そこからブラウザのツールバーなどの分を差し引いた値として定義された数字です
- fixedの要素がある場合などは630pxで成立するように画面設計を行うか、630pxを基準にメディアクエリを追加してレイアウトを可変にできるように実装するよう仕様を作成してください。
================================================
FILE: docs/Design/Readme.stories.mdx
================================================
import { Meta } from "@storybook/addon-docs";
<Meta title='doc/Design/Readme' />
# コンポーネント設計の指針
Vibesのコンポーネントを設計するときには以下のような方針で行っています。
Vibesを使用して画面を設計する際にも意識することで、デザインの一貫性を保ち、アクセシビリティを高め、実装コストを最小限にすることができます。
- [Vibesのカラーパレット](?path=/docs/doc-colors--page) を使用し、適切なコントラスト比を持つ組み合わせにする
- CSSで `rem` によるサイズ指定を行うため、コンポーネントのサイズは16の倍数ピクセルを基準として設計する
- コンポーネントにはマージンのオプションをつけるため、コンポーネント自体には固定のマージンをつけない
- ユーザーがインタラクションできるものの輪郭は角を丸くし、そうでないものは角を丸くしない
# Vibesのコンポーネントを使った画面設計
Sketch CloudからlibraryとしてSketchファイルを提供しています。
これを使うことで、簡単にSketch内でVibesコンポーネントのシンボルを使用できます。
## Storybookを使った確認
実際のコンポーネントの挙動はStorybookで確認していくことができます。
Knobsを使うことで、使おうとしているものに近い状態のコンポーネントを確認できます。
特にサイズ変更に関してはコンポーネントごとに制約があるので、コンポーネントを使う前に必ず実際のコンポーネントを確認するようにしてください。
## マージン
ほとんどのVibesのコンポーネントにはマージンを付けるためのオプションが用意されています。
そのため、コンポーネントにマージンをつけるときにはこのオプションが使用できるサイズでマージンをつける必要があります。
- マージンの大きさは 0.5rem(8px), 1rem(16px), 1.5rem(24px), 2rem(32px), 3rem(48px) である必要があります
- 現在、マージンを付けるオプションの実装を移行しています
- 旧仕様(`marginTop` `marginBottom` `marginLeft` `marginRight`)しか使えないコンポーネントでは、各方向のマージンの大きさはすべて同じにするか、 `MarginBase` のようなコンポーネントを噛ませる必要があります
- 新仕様(`ma` `mt` `mb` `ml` `mr`)が使えるコンポーネントでは、各方向に別々のサイズのマージンを与えることができます。
コンポーネントは外側から内側に向かってマージンが小さくなるように設計されています。
多くの要素が配置されるはずのContentsBaseに1.5remのpaddingが付けられているため、画面設計では1remや0.5remの使用頻度が高くなるはずです。
================================================
FILE: docs/Readme.stories.mdx
================================================
import { Meta } from '@storybook/addon-docs';
<Meta title="doc/Readme" />
# vibes
コンポーネントベースのデザインシステムです。
## vibes の動作ブラウザ
vibes の動作想定環境は[freee の動作環境](https://support.freee.co.jp/hc/ja/articles/202848120) に準じます。
スクリーンリーダーに関しては[アクセシビリティー・ガイドラインの標準環境](https://a11y-guidelines.freee.co.jp/explanations/screen-reader-check.html#id79)に準じますが、簡便なテストのために VoiceOver を併用します。
- 以下のブラウザで必要に応じて動作確認をしています
- Google Chrome
- Microsoft Edge (Chromium ベース)
- Firefox
- Safari
- スクリーンリーダーでの挙動・バグについては Windows 上の Chrome と NVDA での動作確認に基きます
- 一部のブラウザにのみ発生する不具合に関してはバグとして対応しない判断をする場合があります
## vibes の使い方
### vibes を使った画面デザイン
コーディングをせずに画面デザインを行う場合には Sketch を使用してください。vibes の Library ファイルは Sketch Cloud で配布しています。
必要な場合は UX チームにお問い合わせください。
- vibes のコンポーネントには、Figma では表現しきれない制約が存在しています。使おうとしているコンポーネントの仕様は Storybook で確認してください
- その他の注意点などを [Design ページ](/?path=/docs/doc-design--page) に記載してあります。ご確認ください
### Storybook からコンポーネントを探す
vibes のコンポーネントは他のどのコンポーネントにも依存しない `lv1` と、他のコンポーネントを組み合わせてある `lv2` に大別されています
- コンポーネントを探す際は `lv2` から探し、作ろうとしているものが `lv2` で実現できない場合は `lv1` を組み合わせて使うことを検討してください
- Storybook に `examples` として、レイアウトのサンプルを用意しています。こちらも参考にしてください
### 作ろうとしているものが vibes だけでは実現不可能なとき
vibes に追加されているコンポーネントは、プロダクトを跨いで使用するような汎用的なものを想定しています。
そのため、プロダクト固有のドメインに強く紐付くもの、ユーザーに強くアトラクトする必要のあるものについては、vibes を使用しないほうが効果的な場合があります。
- あなた自身が画面デザインを作成している場合は、vibes のコンポーネントのみの組みあわせで目的を達成できるか検討してください
- vibes で完結する画面設計であれば、工数の削減や品質の向上が見込めます。画面デザインで期待する効果と比較したうえで意思決定してください
- vibes を使用しない場合でも、vibes や freee 全体のトーン&マナーを意識して、全体の統一感を著しく損わないよう注意してください
- デザイナーから渡されたものをコーディングしている場合は、まずデザイナーに問い合わせてください
- デザイナーがコンポーネントの技術制約を認識できていない場合も考えられます。まずはデザイナーの意思を確認してください
- 必要に応じて、vibes の仕様変更や、プロダクト側での UI コンポーネント実装を検討してください
- プロダクト側で実装する場合には、今後のアップデートに耐えられる実装を意識してください
- **vibes のコンポーネントへのスタイル上書きはしないでください** 。`.vb-` で始まるクラス名や、内部の要素セレクタへのスタイル指定を行わないでください。スタイルの上書きは影響範囲を読みづらく、メンテナンス性を下げてしまいます
- **`.vb-` で始まる className をプロダクト内で使用しないでください**。vibes コンポーネントの DOM 構造は今後変更することがあり、その場合に vibes のアップデートが困難になります
- vibes 内で使用していない `.vb-` で始まる className も、使用しないでください。
- vibes と組み合わせてスタイルシートを記述する場合は、文字サイズ変更時の整合性を保つため、 `px` ではなく `rem` を使用してください
### アクセシビリティ
vibes を使用しただけでは、必ずしもアクセシビリティの配慮が要らなくなるというものではありません。
- Storybook 内の Examples や各コンポーネントのドキュメントにはあちこちにアクセシビリティの向上に必要な情報が書かれています
- アクセシビリティ向上のために用意された `prop` には、使い方を誤るとかえってアクセシビリティを下げてしまうものもあるため、ドキュメントをよく読んで使用してください
- デザイン・コード・プロダクトを対象に必ず [アクセシビリティチェック](https://a11y-guidelines.freee.co.jp/checks/index.html) を実行してください
## プロダクトへの導入方法
### インストール
TBD
### CSS の読み込み
スタイルを適用するため `node_modules/@freee_jp/vibes/vibes_2021.css` を読み込んでください。
from Sass:
```sass
@import 'node_modules/@freee_jp/vibes/vibes_2021';
```
from JavaScript with CSS Modules:
```js
import '@freee_jp/vibes/css';
```
- CSS の読み込み方法は、プロジェクトの技術選定状況にあわせて検討してください
### vibes コンポーネントの使用
`vibes` から 個別のコンポーネントを import して使用してください
```tsx
import * as React from 'react';
import { ColumnBase, FormControlGroup, FormControl, TextField } from '@freee_jp/vibes';
const Component = () =>
<ColumnBase>
<FormControlGroup>
<FormControl label="日付" fieldId="dateField" mb={1} mr={1}>
<TextField id="dateField" name="dateField" />
</FormControl>
<FormControl label="備考" fieldId="noteField" mb={1} mr={1}>
<TextField id="noteField" name="noteField" />
</FormControl>
...
</FormControlGroup>
<Button onClick={() => doSubmit()}>送信</Button>
</ColumnBase>
}
```
- vibes では TypeScript をサポートしています。プロジェクトで使用できる場合には TypeScript を使用してください。
- Flow にも対応していますが、新規に採用するのは **非推奨** です。Flow の型定義ファイルは手作業によるメンテナンスとなっており、TypeScript との差異がある状態になっています。
- `vibes/dist` や `vibes/src` からの import は **禁止** です。
### コンポーネント共通の `props`
`commonProps` として、全コンポーネントで使用可能な `props` を定義しています
- `ma` `mt` `mb` `mr` `ml`
- マージン用の `prop` で、それぞれ全方向・上方向・下方向・右方向・左方向へのマージンを `rem` 単位で指定できます
- 指定できる値は `0.25` `0.5` `1` `1.5` `2` `3` および `-0.25` `-0.5` `-1` `-1.5` `-2` `-3` です。
- 一部のコンポーネントでは `marginTop` `marginBottom` `marginLeft` `marginRight` `marginSize` の指定も可能ですが、指定内容・利用可能コンポーネントが限定的なので、使用は推奨しません
- `data-guide`
- peacemaker 等によるユーザガイド用で使用することを想定しています。DOM に `data-guide` 属性としてレンダリングされます
- `data-test`
- Selenium や Puppeteer による E2E テストで使用することを想定しています。DOM に `data-test` 属性としてレンダリングされます
- `data-tracking`
- Google Tag Manager などによるトラッキング用途に使用することを想定しています。DOM に `data-tracking` 属性としてレンダリングされます
- `data-masking`
- 行動追跡ツールでのマスキングが必要な要素に指定することを想定しています。DOM に `data-masking` 属性としてレンダリングされます
### VibesContext
vibes コンポーネント共通の設定を、`VibesContext` から変更して使うことができます
```tsx
import { VibesProvider } from '@freee_jp/vibes';
<VibesProvider fixedLayout={true} portalParent={document.body} lang="ja">
...
</VibesProvider>;
```
設定できる項目は以下のとおりです
- `fixedLayout`: ウインドウ幅に関わらずレイアウトを固定するかどうか。
- デフォルトは `false` で、レスポンシブ対応が有効となり、一部のコンポーネントが画面幅にあわせた表示になります。
- `true` の場合は、画面幅に関わらずレイアウトが固定され、常に PC 向けの表示となります。
- `portalParent`: ポップアップやダイアログなど、通常時は body 要素の直下に挿入されるコンポーネントの挿入先を変更できます。
- `lang`: コンポーネントの文言の言語を変更できます。デフォルトは `ja` です。
================================================
FILE: docs/Storybook.stories.mdx
================================================
import { Meta } from "@storybook/addon-docs";
<Meta title='doc/Storybook' />
## Storybookの使い方
Storybookではコンポーネントの見た目や挙動や仕様を確かめることができます。
### コンポーネントの探し方
Vibesのコンポーネントは、どのコンポーネントからも独立しているLv1と、他のコンポーネントに依存するLv2があります。
車輪の再発明を防ぐため、コンポーネントはまずLv2から探すようにしてください。
また、サイドバーには検索機能があります。使いたいコンポーネントの名前がわかっている場合はこちらもご利用ください。
### Storybookの画面構成
Storybookの画面は、Sidebar, Canvas, Docs, Addonsからなります。
- Sidebarはコンポーネントが一覧になっている部分です。Sキーで表示・非表示にできます。
- Canvasはコンポーネントが表示される部分です。上部のタブでDocsと切り変えられます
- Docsはコンポーネントのコードコメントから生成されたドキュメントが表示される部分です。上部のタブでCanvasと切り変えられます。
- AddonsはCanvasのコンポーネントに対する操作ができます。Aキーで表示・非表示にできます。
### Knobs/Controlsの使用
Canvasに表示されたコンポーネントは、AddonsパネルのKnobs/Controlsタブで表示のカスタマイズをして確認することができます。
サイズに関するオプションなどを試したい場合に使用してください。
## Storybookの書き方
Storybookはコンポーネントのドキュメンテーションであると同時に、その挙動を保証するテストにもなります。その両方の意味において、書き方がある程度統一されていることが求められます。
本章では、vibesに置ける標準的なStorybookの書き方を説明します。
### 前提
2023年12月6日時点で利用している`storybook@7.6.1`における書き方になります。
### 記法
#### Component Story Format 3(CSF3)で記載する(必須)
[CSF3](https://storybook.js.org/blog/component-story-format-3-0)を利用します。現時点ではそうではない書き方が多く残っていますが、既存のものに修正を入れる場合や新しく書く場合は書き換えてください。
#### パラメータの検証はaddon-knobsではなくaddon-controlsを利用する(必須)
addon-knobsは非推奨です。現時点では利用箇所が多く残っていますが、既存のものに修正を入れる場合や新しく書く場合は[addon-controls](https://storybook.js.org/docs/essentials/controls)を利用してください。
#### 関数のmockはaddon-actionsではなくstorybook-testのfnを利用する(推奨)
addon-actionsは、後述するinteraction testでの再利用ができません。そのため、storybook-testのfnを利用してください。
#### 一番上にBasicというstoryを用意する(推奨)
addon-docsの仕様上、一番上のstoryがメインのstoryとなります。そのため、そのコンポーネントの基本的な利用の確認ができるようなstoryを書いてください。
またこのBasicは、後述するinteraction testでも再利用する想定です。
2つ目以降のstoryは、必要に応じて色々なパターンを見せる用に記載してください。
#### コンポーネントにコメントを付与する(任意)
addon-docsによってコードコメントがStorybookにも表示されるようになります。必要に応じて、コンポーネントの利用に関する注意事項を記載してください。
```jsx
// good example
import { fn } from '@storybook/test';
import { Meta, StoryObj } from '@storybook/react';
import Button from './Button';
export default {
component: Button,
} as Meta<typeof Button>;
type Story = StoryObj<typeof Button>;
export const Basic: Story = {
args: {
onClick: fn(),
onSelfWindowNavigation: fn(),
onFocus: fn(),
onBlur: fn(),
children: 'ボタン',
},
render: (args) => {
const ref = React.createRef<HTMLButtonElement | HTMLAnchorElement>();
return <Button {...args} ref={ref} />;
},
};
// bad example
import { action } from '@storybook/addon-actions';
import { text } from '@storybook/addon-knobs';
import Button from './Button';
export const ButtonComponent = () => {
const ref = React.createRef<HTMLButtonElement | HTMLAnchorElement>();
return (
<Button
onClick={action('click')}
onSelfWindowNavigation={action('self window navigation')}
onFocus={action('focus')}
onBlur={action('blur')}
ref={ref}
>
{text('Children', 'ボタン', 'Button')}
</Button>
);
};
```
### テスト
storybook を利用したテストとしては以下の2つを考えています。
- intreaction test
- visual regression test
#### interaction test
ここでいうinteraction testとは、コンポーネントの画面上での表示や操作した時の振る舞いを確認するテストです。
必要に応じてaddon-intreactionを利用し、interaction testを書いてください。Button.stories.tsxが参考になると思います。
#### visual regression test
visual regression testとは、修正が入る前後でコンポーネントがどのように表示されるかの画像比較を行うテストです。
コンポーネントのバリエーションを一覧できるようなstoryを用意してください。必要に応じてaddon-pseudo-statesを利用し、擬似クラスの表示も確認できるようにしてください。Button.stories.tsxが参考になると思います。
なお、現時点ではどのようにvisual regression testを行うかは未定です。
================================================
FILE: docs/Stylesheets.stories.mdx
================================================
import { Meta } from "@storybook/addon-docs";
<Meta title='doc/Stylesheets' />
# CSS
変更する場合は **stylesheetsディレクトリに格納されているSCSSファイル** を変更してください。
ルートディレクトリにある `vibes.css` や `vibes_hr.css` はビルド成果物です。取り違えに気をつけてください。
vibes では [BEM](https://en.bem.info/methodology/) をベースにしたコーディングルールを採用しています。
## styled-components
部分的にstyled-componentsを導入しはじめています。
styled-componentsについては、[?path=/docs/doc-typescript--page](TypeScript)のページを参照してください。
## コーディング規約
### Block
基本的には各 Lv でディレクトリごとに区切られた modules や components ごとに 1 つの Block として扱います。ただし、 `button` と `textButton`, `TextField` と `TextArea` のように振る舞いや DOM 構造が大きく異なる場合は同じ modules であっても Modifier ではなく別の Block として定義することを推奨します。
### Element
Block の構成要素で、スタイルはその Block の子要素である場合のみ有効となります。
### Modifier
Block と Element の状態やバリエーションを表します。`primary, danger, active, small` といったものが該当します。上述のように、振る舞いや DOM 構造が大きく変わる場合は別の Block や Element として扱うことを検討してください。
### 命名規則
`vb-Block__Element--Modifier`
#### 接頭辞として `vb-` をつける
```css
.vb-block
```
#### Block と Element はアンダースコア 2 つ `__` で繋ぐ
```css
.vb-block__text
.vb-block__image
```
#### Block または Element と Modifier はハイフン 2 つ `--` で繋ぐ
```css
.vb-block--active
.vb-block__button--primary
```
#### 2 単語以上を含む名前はキャメルケース `camelCase` で記述する
```css
.vb-form-block // NG
.vb-formBlock // OK
.vb-block--text-only // NG
.vb-block--textOnly // OK
```
### Element はネストさせない
並列に記述する。要素がネストしていてもクラス名には反映させない。
```css
.vb-block__text__image // NG
.vb-block__text // OK
.vb-block__image // OK
```
### 複数の Modifier の組み合わせによって状態が変化しうる場合は、 Modifier をネストさせてもよい
ただし、ネストせずに記述できそうな場合は極力避ける。
```css
.vb-block--primary--disable // OK
.vb-block--primary // better
.vb-block--disable // better
```
### マークアップ
#### Block を入れ子にしたスタイルの指定をしない。
```css
// NG
.vb-block {
display: block;
.vb-form {
line-height: 1;
}
}
// OK
.vb-block {
display: block;
}
.vb-form {
line-height: 1;
}
```
#### Modifier をつける時は必ず元の Block, Element のクラスと併記する
Modifier 単体で使えるようにしない。
```html
// NG
<div class="vb-block--active"></div>
// OK
<div class="vb-block vb-block--active"></div>
```
```css
// NG
.vb-block {
display: block;
color: #000;
}
.vb-block--active {
display: block;
color: #f00;
}
// OK
.vb-block {
display: block;
color: #f00;
&--active {
color: #f00;
}
}
```
#### 要素セレクタを使わない
必要な場合は Element として定義する。
```html
<!-- NG -->
<div class="vb-block">
<a href="/foo">foo</a>
</div>
<!-- OK -->
<div class="vb-block">
<a className="vb-block__link" href="/foo">foo</a>
</div>
```
```css
// NG
.vb-block {
a {
text-decoration: underline;
}
}
// OK
.vb-block {
&__link {
text-decoration: underline;
}
}
```
================================================
FILE: docs/TypeScript.stories.mdx
================================================
import { Meta } from "@storybook/addon-docs";
<Meta title='doc/TypeScript' />
# TypeScript (JavaScript)
変更する場合は **srcディレクトリに格納されているts(x)ファイル** を変更してください。`dist` ディレクトリ内にあるファイルはビルド成果物です。取り間違いに気をつけてください。
### コンポーネントの追加時の注意点
コンポーネントを追加するときは、同名の `stories.tsx` ファイルを作成し、Storybookで閲覧できるようにしてください。
また、追加したコンポーネントをプロダクト側から参照できるよう、 `src/lv1/index.ts` `src/lv2/index.ts` で `export` してください。
### 型定義ファイルについて
TypeScriptの型定義に加えて、Flowでの型定義を `lv1.js.flow` `lv2.js.flow` に(手作業で)記述しています。
こちらはコンポーネントを追加したり変更したりした際に必ず修正してください。
## styled-components について
部分的にstyled-componentsを採用しはじめていますが、以下をルールとします
- Vibesの外部から利用できるかたちでstyled-components由来のpropを提供しない
- 特に as をexportしない。as を提供する場合は、使用できるtagNameを限定する
- コンポーネントは `CommonStyle` (`src/internal/CommonStyle.ts`) を extendしたもの(`styled(CommonStyle)`)が最外層にあるようにする
- ここに `ma` `mt` `mb` `mr` `mr` のようなマージンを提供するpropや、共通のフォント設定などが定義されています
以下のようなかたちになります
```ts
import { CommonStyle } from '../../internal/CommonStyle';
const ComponentStyle = styled(commonStyle)`
/* ここにコンポーネントのスタイルを書く */
`
type Props = {
//...
} & CommonProps;
export const Component: React.FC<Props> = (props: Props) =>{
return (
<ComponentStyle>
...
</ComponentStyle>
);
}
```
================================================
FILE: examples/Collection.mdx
================================================
import { Story, Canvas } from '@storybook/addon-docs/blocks';
# コレクション画面
オブジェクトを一覧表示し、それぞれのオブジェクト個別の画面(シングル画面)に遷移させたり、フィルタやソートによってオブジェクトを検索させたりする画面をコレクション画面と呼びます。
<Canvas>
{/* Container を使っているとinlineでは見辛いので、inline={false} にしている */}
<Story id="examples-collection--collection-page" inline={false} height={800} />
</Canvas>
## コレクション画面の構成
コレクション画面は以下の3種類のエリアに分解されます
1. フィルターされたオブジェクトの表示エリア
2. フィルターされたオブジェクトに対する操作エリア
3. オブジェクト全体に対する操作エリア
### フィルターされたオブジェクトの表示エリア
`ListTable` や `ListCard` を用いてデータを表示します。
ユーザーが初めて機能を使う場合や、検索条件にマッチするオブジェクトが無い場合には 「検索条件に該当するオブジェクトが無いこと」を示したり、機能の説明をして使用を促したりするEmpty UIを用意してください。
{/* TODO: Empty UIのサンプルを用意する */}
#### `ListTable` を用いた表示
* 可能なかぎりすべてのカラムをソート可能にしてください
* 一括操作可能な場合には、左端にチェックボックスを表示します(`withCheckbox` propをつけるとチェックボックスが表示されます)
* ヘッダー行のチェックボックスは現在表示されている範囲のすべてのオブジェクトのチェックボックスを制御します
* データの表示条件(フィルタ、ソート、ページングなど)が変更された場合、チェックボックスを未選択状態に戻してください
* 各行をクリックすることで、それぞれのオブジェクト個別の画面(シングル画面)に遷移させるか、シングルを表現するダイアログを開くことができます
* 各オブジェクトに対して行えるアクションのショートカットボタンを行内に配置することができます。ただし、それらのアクションはシングル画面やダイアログでも実行可能にしてください。
* リストの下には `Pager` を配置して、件数が多い場合に辿れるようにしてください。
* いわゆる「無限スクロール」にはしないでください
<Canvas>
<Story id="examples-collection--list-table-with-check-box" />
</Canvas>
{/* TODO: ListTableのサンプルを用意する */}
### フィルターされたオブジェクトに対する操作エリア
検索機能によってフィルターされた範囲のデータに対する操作を行います。
* 一括操作、表示件数の変更、フィルターされた範囲のエクスポートがおもに該当します。
* フィルターされたオブジェクトの表示エリアで左端にチェックボックスを置いているため、チェックを入れたオブジェクトを対象とする一括操作は左端に配置しています
<Canvas>
<Story id="examples-collection--filtered-objects-action-area" />
</Canvas>
### オブジェクト全体に対する操作エリア
オブジェクトの新規作成やインポート、フィルターの操作をまとめるエリアです。
<Canvas>
<Story id="examples-collection--all-objects-action-area" />
</Canvas>
## コレクション画面のバリエーション
表示するオブジェクトの種類や、想定される使われ方によってコレクション表示を最適な状態にしてください。
### 詳細なフィルタをもつコレクション
詳細なフィルターによって絞り込み表示が可能となったコレクション表示です。一覧性を高めるために、フィルター部分の折り畳みを検討しても良いでしょう。
<Canvas>
{/* Container を使っているとinlineでは見辛いので、inline={false} にしている */}
<Story id="examples-collection--with-complex-filter" inline={false} height={800} />
</Canvas>
### 一括操作なしのコレクション
一括操作を可能としない場合にはチェックボックスを配置せず、フィルターされたオブジェクトに対する操作エリアを右に寄せることもできます。
<Canvas>
{/* Container を使っているとinlineでは見辛いので、inline={false} にしている */}
<Story id="examples-collection--without-checkbox" inline={false} height={800} />
</Canvas>
### emptyStates(検索結果が空の場合)
検索結果が空の場合にはemptyUIを表示します。vibesではemptyStates/ NoSearchResultsにデフォルトのemptyUIを用意しています。
<Canvas>
{/* Container を使っているとinlineでは見辛いので、inline={false} にしている */}
<Story id="examples-collection--no-search-results-found" inline={false} height={800} />
</Canvas>
### emptyStates(データがまだ作成されていない場合)
データがまだ作成されていない場合にはemptyUIを表示します。vibesではemptyStates/ NoDataCreatedにデフォルトのemptyUIを用意しています。
<Canvas>
{/* Container を使っているとinlineでは見辛いので、inline={false} にしている */}
<Story id="examples-collection--no-data-created-yet" inline={false} height={800} />
</Canvas>
================================================
FILE: examples/Collection.stories.tsx
================================================
import * as React from 'react';
import { MdAdd, MdFilterList } from 'react-icons/md';
import Collection from './Collection.mdx';
import {
Container,
ContentsBase,
Breadcrumbs,
MarginBase,
JumpButton,
Button,
SearchField,
SelectBox,
Paragraph,
WithSideContent,
ListTable,
HeadlineArea,
DropdownButton,
Pagination,
Pager,
Digits,
FormControl,
FormControlGroup,
TextField,
DigitsInput,
DateInput,
ColumnBase,
SectionTitle,
VisuallyHidden,
NoSearchResults,
NoDataCreated,
} from '../src';
import { Order } from '../src/lv1/lists/TableListHeadCell';
export default {
title: 'examples/Collection',
parameters: { docs: { page: Collection } },
};
type ListElm = {
title: string;
user: string;
amount: number;
status: string;
date: string;
};
const useData = () => {
const data: ListElm[] = [
{
title: '打ち合わせ費用',
user: 'フリー太郎',
amount: 100000,
status: '申請中',
date: '2020-10-01',
},
{
title: '書籍購入費',
user: 'user.email@example.com',
amount: 123000,
status: '申請中',
date: '2020-09-23',
},
{
title: '交通費',
user: '佐々木大輔',
amount: 2000,
status: '精算済',
date: '2020-10-11',
},
{
title: 'UFO撮影ロケ',
user: '五反田花子',
amount: 3000000,
status: '却下',
date: '2020-09-12',
},
{
title: 'ツチノコ捜索費',
user: '三田次郎',
amount: 1000000,
status: '申請中',
date: '2020-11-01',
},
{
title: 'オフィス備品',
user: 'フリー太郎',
amount: 48000,
status: '精算済',
date: '2020-09-12',
},
{
title: '書籍購入費',
user: 'user.email@example.com',
amount: 2800,
status: '申請中',
date: '2020-10-12',
},
{
title: 'ネコのエサ代',
user: '三田次郎',
amount: 3000,
status: '申請中',
date: '2020-10-27',
},
{
title: '駐車場代',
user: 'フリー太郎',
amount: 4000,
status: '申請中',
date: '2020-10-05',
},
{
title: 'PC用品',
user: '五反田花子',
amount: 800000,
status: '申請中',
date: '2020-10-21',
},
];
const [sortKey, setSortKey] = React.useState<keyof ListElm>('date');
const [sortOrder, setSortOrder] = React.useState<Order>('desc');
const [statuses, setStatuses] = React.useState<boolean[]>(
Array(data.length).fill(false)
);
const nextOrder: { [key in Order]: Order } = {
asc: 'desc',
desc: 'init',
init: 'asc',
};
const sort = (newKey: keyof ListElm) => {
if (sortKey === newKey) {
setSortOrder((prev) => nextOrder[prev]);
} else {
setSortKey(newKey);
setSortOrder('asc');
}
// ソートしたときはチェックボックスの状態をリセット
setStatuses(Array(data.length).fill(false));
};
const changeAllStatus = (newStatus: boolean) => {
setStatuses(Array(data.length).fill(newStatus));
};
const changeRowStatus = (newStatus: boolean, rowIndex: number) => {
const newStatuses = statuses.slice();
newStatuses[rowIndex] = newStatus;
setStatuses(newStatuses);
};
const sortedData =
sortOrder === 'init'
? data
: data
.slice()
.sort(
(a, b) =>
(typeof a[sortKey] === 'number' && typeof b[sortKey] === 'number'
? Number(a[sortKey]) - Number(b[sortKey])
: String(a[sortKey]).localeCompare(String(b[sortKey]))) *
(sortOrder === 'desc' ? -1 : 1)
);
const noResults: ListElm[] = [];
return {
sortKey,
sortOrder,
statuses,
sort,
sortedData,
noResults,
changeRowStatus,
changeAllStatus,
};
};
export const CollectionPage = () => {
const {
sort,
sortKey,
sortOrder,
statuses,
sortedData,
changeAllStatus,
changeRowStatus,
} = useData();
return (
<Container width="wide">
<ContentsBase>
<Breadcrumbs
mb={1}
links={[
{ title: 'ホーム', url: '/' },
{ title: '申請一覧', url: '/path/' },
]}
/>
<HeadlineArea
pageTitle="コレクション表示"
relatedMenus={
<>
<JumpButton url="/path/to/related/1" mr={1}>
関連機能1
</JumpButton>
<JumpButton url="/path/to/related/2" mr={1}>
関連機能2
</JumpButton>
<DropdownButton
dropdownContents={[
{
type: 'selectable',
url: '/path/to/service1',
text: 'サービス1',
target: '_blank',
},
{
type: 'selectable',
url: '/path/to/service2',
text: 'サービス2',
target: '_blank',
},
{ type: 'rule' },
{
type: 'selectable',
url: '/path/to/service3',
text: '関連するアプリを探す',
target: '_blank',
},
]}
buttonLabel="関連サービス・アプリ"
/>
</>
}
>
これはコレクション表示のサンプルです。ここにはこの画面の説明を書きます。説明が必要ないような画面だったら無理して書かなくてええんやで。
</HeadlineArea>
<MarginBase mb={1}>
<Button IconComponent={MdAdd} mr={1}>
新規追加
</Button>
<JumpButton url="/path/to/import">インポート</JumpButton>
</MarginBase>
<MarginBase mb={2}>
<SearchField
width="large"
placeholder="タイトル、ユーザー名、メールアドレスなどで検索"
marginRight
marginSize="small"
/>
<Button>検索</Button>
</MarginBase>
<WithSideContent
mb={1}
sideContent={
<>
<Pagination
rowsPerPageOptions={[
{ value: '10' },
{ value: '20' },
{ value: '50' },
{ value: '100' },
{ value: '200' },
]}
rowsPerPageValue={20}
currentPage={1}
rowCount={999}
mr={1}
/>
<DropdownButton
buttonLabel="エクスポート"
dropdownContents={[
{
type: 'selectable',
text: 'CSV形式でエクスポート',
},
{
type: 'selectable',
text: 'JSON形式でエクスポート',
},
{ type: 'rule' },
{
type: 'selectable',
text: 'エクスポート履歴',
},
]}
/>
</>
}
>
<DropdownButton
buttonLabel="一括操作"
dropdownContents={[
{
type: 'selectable',
text: 'ステータスを変更',
},
{ type: 'selectable', text: '削除' },
]}
mr={0.5}
/>
{statuses.filter((e) => e).length > 0 && (
<Paragraph inline>
{statuses.filter((e) => e).length}件を選択中
</Paragraph>
)}
</WithSideContent>
<ListTable
mr={-1.5}
ml={-1.5}
headers={[
{
value: 'タイトル',
minWidth: 15,
onClick: () => sort('title'),
ordering: (sortKey == 'title' && sortOrder) || 'init',
},
{
value: 'ユーザー',
minWidth: 10,
onClick: () => sort('user'),
ordering: (sortKey == 'user' && sortOrder) || 'init',
},
{
value: '金額',
minWidth: 5,
alignRight: true,
onClick: () => sort('amount'),
ordering: (sortKey == 'amount' && sortOrder) || 'init',
},
{
value: 'ステータス',
onClick: () => sort('status'),
ordering: (sortKey == 'status' && sortOrder) || 'init',
},
{
value: '作成日',
onClick: () => sort('date'),
ordering: (sortKey == 'date' && sortOrder) || 'init',
},
{ value: '操作' },
]}
onChangeHeaderCheckBox={(e) => changeAllStatus(e.target.checked)}
rows={sortedData.map((row, i) => ({
checked: statuses[i],
onChangeCheckBox: (e) => {
changeRowStatus(e.target.checked, i);
},
url: `/path/to/single/${i}`,
cells: [
{ value: row.title },
{ value: row.user, breakWord: true },
{ value: Digits.formalize(row.amount), alignRight: true },
{ value: row.status },
{ value: row.date },
{
value: (
<>
<Button mr={0.5} small appearance="tertiary">
コピー
</Button>
<Button mr={0.5} danger small appearance="tertiary">
削除
</Button>
</>
),
},
],
}))}
withCheckBox
></ListTable>
<Pager
currentPage={1}
pageCount={99}
onPageChange={() => {
/* 2ページ目以降作ってないので許して */
}}
/>
</ContentsBase>
</Container>
);
};
export const ListTableWithCheckBox = () => {
const {
sort,
sortKey,
sortOrder,
statuses,
sortedData,
changeAllStatus,
changeRowStatus,
} = useData();
return (
<>
<ListTable
mr={-1.5}
ml={-1.5}
headers={[
{
value: 'タイトル',
minWidth: 15,
onClick: () => sort('title'),
ordering: (sortKey == 'title' && sortOrder) || 'init',
},
{
value: 'ユーザー',
minWidth: 10,
onClick: () => sort('user'),
ordering: (sortKey == 'user' && sortOrder) || 'init',
},
{
value: '金額',
minWidth: 5,
alignRight: true,
onClick: () => sort('amount'),
ordering: (sortKey == 'amount' && sortOrder) || 'init',
},
{
value: 'ステータス',
onClick: () => sort('status'),
ordering: (sortKey == 'status' && sortOrder) || 'init',
},
{
value: '作成日',
onClick: () => sort('date'),
ordering: (sortKey == 'date' && sortOrder) || 'init',
},
{ value: '操作' },
]}
onChangeHeaderCheckBox={(e) => changeAllStatus(e.target.checked)}
rows={sortedData.map((row, i) => ({
checked: statuses[i],
onChangeCheckBox: (e) => {
changeRowStatus(e.target.checked, i);
},
url: `/path/to/single/${i}`,
cells: [
{ value: row.title },
{ value: row.user, breakWord: true },
{ value: Digits.formalize(row.amount), alignRight: true },
{ value: row.status },
{ value: row.date },
{
value: (
<>
<Button mr={0.5} small appearance="tertiary">
コピー
</Button>
<Button mr={0.5} danger small appearance="tertiary">
削除
</Button>
</>
),
},
],
}))}
withCheckBox
></ListTable>
<Pager
currentPage={1}
pageCount={99}
onPageChange={() => {
/* 2ページ目以降作ってないので許して */
}}
/>
</>
);
};
export const FilteredObjectsActionArea = () => (
<WithSideContent
mb={1}
sideContent={
<>
<Pagination
rowsPerPageOptions={[
{ value: '10' },
{ value: '20' },
{ value: '50' },
{ value: '100' },
{ value: '200' },
]}
rowsPerPageValue={20}
currentPage={1}
rowCount={999}
mr={1}
/>
<DropdownButton
buttonLabel="エクスポート"
dropdownContents={[
{
type: 'selectable',
text: 'CSV形式でエクスポート',
},
{
type: 'selectable',
text: 'JSON形式でエクスポート',
},
{ type: 'rule' },
{
type: 'selectable',
text: 'エクスポート履歴',
},
]}
/>
</>
}
>
<DropdownButton
buttonLabel="一括操作"
dropdownContents={[
{
type: 'selectable',
text: 'ステータスを変更',
},
{ type: 'selectable', text: '削除' },
]}
/>
</WithSideContent>
);
export const AllObjectsActionArea = () => (
<>
<MarginBase mb={1}>
<Button IconComponent={MdAdd} mr={1}>
新規追加
</Button>
<JumpButton url="/path/to/import">インポート</JumpButton>
</MarginBase>
<MarginBase mb={2}>
<SearchField
width="large"
placeholder="タイトル、ユーザー名、メールアドレスなどで検索"
marginRight
marginSize="small"
/>
<Button>検索</Button>
</MarginBase>
</>
);
export const WithComplexFilter = () => {
const {
sort,
sortKey,
sortOrder,
statuses,
sortedData,
changeAllStatus,
changeRowStatus,
} = useData();
return (
<Container width="wide">
<ContentsBase>
<Breadcrumbs
mb={1}
links={[
{ title: 'ホーム', url: '/' },
{ title: '申請一覧', url: '/path/' },
]}
/>
<HeadlineArea
pageTitle="コレクション表示"
relatedMenus={
<>
<JumpButton url="/path/to/related/1" mr={1}>
関連機能1
</JumpButton>
<JumpButton url="/path/to/related/2" mr={1}>
関連機能2
</JumpButton>
<DropdownButton
dropdownContents={[
{
type: 'selectable',
url: '/path/to/service1',
text: 'サービス1',
target: '_blank',
},
{
type: 'selectable',
url: '/path/to/service2',
text: 'サービス2',
target: '_blank',
},
{ type: 'rule' },
{
type: 'selectable',
url: '/path/to/service3',
text: '関連するアプリを探す',
target: '_blank',
},
]}
buttonLabel="関連サービス・アプリ"
/>
</>
}
>
これはコレクション表示のサンプルです。ここにはこの画面の説明を書きます。説明が必要ないような画面だったら無理して書かなくてええんやで。
</HeadlineArea>
<MarginBase mb={1}>
<Button IconComponent={MdAdd} mr={1}>
新規追加
</Button>
<JumpButton url="/path/to/import">インポート</JumpButton>
</MarginBase>
<ColumnBase paddingSize="small" mb={2}>
<WithSideContent
verticalAlign="middle"
sideContent={
<Button appearance="tertiary">検索条件をクリア</Button>
}
>
<SectionTitle>検索条件</SectionTitle>
</WithSideContent>
<FormControlGroup>
<FormControl label="タイトル" fieldId="form1-title" mr={1} mb={1}>
<TextField id="form1-title" />
</FormControl>
<FormControl label="ユーザー" fieldId="form1-user" mr={1} mb={1}>
<TextField id="form1-user" width="small" />
</FormControl>
<FormControl label="金額" mr={1} mb={1}>
<DigitsInput nullable label="金額の下限" width="small" />
〜
<DigitsInput nullable label="金額の上限" width="small" />
</FormControl>
<FormControl
label="ステータス"
fieldId="form1-status"
mr={1}
mb={1}
>
<SelectBox
options={[
{ name: '申請中' },
{ name: '精算済' },
{ name: '却下' },
]}
id="form1-status"
width="small"
/>
</FormControl>
<FormControl label="日付" mr={1} mb={1}>
<DateInput label="日付の下限" width="small" />
〜
<DateInput label="日付の上限" width="small" />
</FormControl>
</FormControlGroup>
<Button IconComponent={MdFilterList}>絞り込む</Button>
</ColumnBase>
<VisuallyHidden>
{/* 検索条件の見出しを立てているため、一覧部分にも視覚的には見えないかたちで見出しを立てる */}
<SectionTitle>申請の一覧</SectionTitle>
</VisuallyHidden>
<WithSideContent
mb={1}
sideContent={
<>
<Pagination
rowsPerPageOptions={[
{ value: '10' },
{ value: '20' },
{ value: '50' },
{ value: '100' },
{ value: '200' },
]}
rowsPerPageValue={20}
currentPage={1}
rowCount={999}
mr={1}
/>
<DropdownButton
buttonLabel="エクスポート"
dropdownContents={[
{
type: 'selectable',
text: 'CSV形式でエクスポート',
},
{
type: 'selectable',
text: 'JSON形式でエクスポート',
},
{ type: 'rule' },
{
type: 'selectable',
text: 'エクスポート履歴',
},
]}
/>
</>
}
>
<DropdownButton
buttonLabel="一括操作"
dropdownContents={[
{
type: 'selectable',
text: 'ステータスを変更',
},
{ type: 'selectable', text: '削除' },
]}
mr={0.5}
/>
{statuses.filter((e) => e).length > 0 && (
<Paragraph inline>
{statuses.filter((e) => e).length}件を選択中
</Paragraph>
)}
</WithSideContent>
<ListTable
mr={-1.5}
ml={-1.5}
headers={[
{
value: 'タイトル',
minWidth: 15,
onClick: () => sort('title'),
ordering: (sortKey == 'title' && sortOrder) || 'init',
},
{
value: 'ユーザー',
minWidth: 10,
onClick: () => sort('user'),
ordering: (sortKey == 'user' && sortOrder) || 'init',
},
{
value: '金額',
minWidth: 5,
alignRight: true,
onClick: () => sort('amount'),
ordering: (sortKey == 'amount' && sortOrder) || 'init',
},
{
value: 'ステータス',
onClick: () => sort('status'),
ordering: (sortKey == 'status' && sortOrder) || 'init',
},
{
value: '作成日',
onClick: () => sort('date'),
ordering: (sortKey == 'date' && sortOrder) || 'init',
},
{ value: '操作' },
]}
onChangeHeaderCheckBox={(e) => changeAllStatus(e.target.checked)}
rows={sortedData.map((row, i) => ({
checked: statuses[i],
onChangeCheckBox: (e) => {
changeRowStatus(e.target.checked, i);
},
url: `/path/to/single/${i}`,
cells: [
{ value: row.title },
{ value: row.user, breakWord: true },
{ value: Digits.formalize(row.amount), alignRight: true },
{ value: row.status },
{ value: row.date },
{
value: (
<>
<Button mr={0.5} small appearance="tertiary">
コピー
</Button>
<Button mr={0.5} danger small appearance="tertiary">
削除
</Button>
</>
),
},
],
}))}
withCheckBox
></ListTable>
<Pager
currentPage={1}
pageCount={99}
onPageChange={() => {
/* 2ページ目以降作ってないので許して */
}}
/>
</ContentsBase>
</Container>
);
};
export const WithoutCheckbox = () => {
const { sort, sortKey, sortOrder, sortedData } = useData();
return (
<Container width="wide">
<ContentsBase>
<Breadcrumbs
mb={1}
links={[
{ title: 'ホーム', url: '/' },
{ title: '申請一覧', url: '/path/' },
]}
/>
<HeadlineArea
pageTitle="コレクション表示"
relatedMenus={
<>
<JumpButton url="/path/to/related/1" mr={1}>
関連機能1
</JumpButton>
<JumpButton url="/path/to/related/2" mr={1}>
関連機能2
</JumpButton>
<DropdownButton
dropdownContents={[
{
type: 'selectable',
url: '/path/to/service1',
text: 'サービス1',
target: '_blank',
},
{
type: 'selectable',
url: '/path/to/service2',
text: 'サービス2',
target: '_blank',
},
{ type: 'rule' },
{
type: 'selectable',
url: '/path/to/service3',
text: '関連するアプリを探す',
target: '_blank',
},
]}
buttonLabel="関連サービス・アプリ"
/>
</>
}
>
これはコレクション表示のサンプルです。ここにはこの画面の説明を書きます。説明が必要ないような画面だったら無理して書かなくてええんやで。
</HeadlineArea>
<MarginBase mb={1}>
<Button IconComponent={MdAdd} mr={1}>
新規追加
</Button>
<JumpButton url="/path/to/import">インポート</JumpButton>
</MarginBase>
<WithSideContent
mb={1}
sideContent={
<>
<Pagination
rowsPerPageOptions={[
{ value: '10' },
{ value: '20' },
{ value: '50' },
{ value: '100' },
{ value: '200' },
]}
rowsPerPageValue={20}
currentPage={1}
rowCount={999}
mr={1}
/>
<DropdownButton
buttonLabel="エクスポート"
dropdownContents={[
{
type: 'selectable',
text: 'CSV形式でエクスポート',
},
{
type: 'selectable',
text: 'JSON形式でエクスポート',
},
{ type: 'rule' },
{
type: 'selectable',
text: 'エクスポート履歴',
},
]}
/>
</>
}
>
<SearchField
width="large"
placeholder="タイトル、ユーザー名、メールアドレスなどで検索"
marginRight
marginSize="small"
/>
<Button>検索</Button>
</WithSideContent>
<ListTable
mr={-1.5}
ml={-1.5}
headers={[
{
value: 'タイトル',
minWidth: 15,
onClick: () => sort('title'),
ordering: (sortKey == 'title' && sortOrder) || 'init',
},
{
value: 'ユーザー',
minWidth: 10,
onClick: () => sort('user'),
ordering: (sortKey == 'user' && sortOrder) || 'init',
},
{
value: '金額',
minWidth: 5,
alignRight: true,
onClick: () => sort('amount'),
ordering: (sortKey == 'amount' && sortOrder) || 'init',
},
{
value: 'ステータス',
onClick: () => sort('status'),
ordering: (sortKey == 'status' && sortOrder) || 'init',
},
{
value: '作成日',
onClick: () => sort('date'),
ordering: (sortKey == 'date' && sortOrder) || 'init',
},
{ value: '操作' },
]}
rows={sortedData.map((row, i) => ({
url: `/path/to/single/${i}`,
cells: [
{ value: row.title },
{ value: row.user, breakWord: true },
{ value: Digits.formalize(row.amount), alignRight: true },
{ value: row.status },
{ value: row.date },
{
value: (
<>
<Button mr={0.5} small appearance="tertiary">
コピー
</Button>
<Button mr={0.5} danger small appearance="tertiary">
削除
</Button>
</>
),
},
],
}))}
></ListTable>
<Pager
currentPage={1}
pageCount={99}
onPageChange={() => {
/* 2ページ目以降作ってないので許して */
}}
/>
</ContentsBase>
</Container>
);
};
export const NoSearchResultsFound = () => {
const { sort, sortKey, sortOrder, noResults } = useData();
return (
<Container width="wide">
<ContentsBase>
<Breadcrumbs
mb={1}
links={[
{ title: 'ホーム', url: '/' },
{ title: '申請一覧', url: '/path/' },
]}
/>
<HeadlineArea
pageTitle="コレクション表示"
relatedMenus={
<>
<JumpButton url="/path/to/related/1" mr={1}>
関連機能1
</JumpButton>
<JumpButton url="/path/to/related/2" mr={1}>
関連機能2
</JumpButton>
<DropdownButton
dropdownContents={[
{
type: 'selectable',
url: '/path/to/service1',
text: 'サービス1',
target: '_blank',
},
{
type: 'selectable',
url: '/path/to/service2',
text: 'サービス2',
target: '_blank',
},
{ type: 'rule' },
{
type: 'selectable',
url: '/path/to/service3',
text: '関連するアプリを探す',
target: '_blank',
},
]}
buttonLabel="関連サービス・アプリ"
/>
</>
}
>
これはコレクション表示のサンプルです。ここにはこの画面の説明を書きます。説明が必要ないような画面だったら無理して書かなくてええんやで。
</HeadlineArea>
<MarginBase mb={1}>
<Button IconComponent={MdAdd} mr={1}>
新規追加
</Button>
<JumpButton url="/path/to/import">インポート</JumpButton>
</MarginBase>
<WithSideContent
mb={1}
sideContent={
<>
<Paragraph inline mr={0.5}>
<label htmlFor="examples__collection__collectionPage__limitSelect">
表示件数:
</label>
</Paragraph>
<SelectBox
id="examples__collection__collectionPage__limitSelect"
width="xSmall"
options={[
{ name: '10件' },
{ name: '20件' },
{ name: '50件' },
{ name: '100件' },
{ name: '200件' },
]}
mr={1}
/>
<Paragraph inline mr={1}>
0 / 0
</Paragraph>
<DropdownButton
buttonLabel="エクスポート"
dropdownContents={[
{
type: 'selectable',
text: 'CSV形式でエクスポート',
},
{
type: 'selectable',
text: 'JSON形式でエクスポート',
},
{ type: 'rule' },
{
type: 'selectable',
text: 'エクスポート履歴',
},
]}
/>
</>
}
>
<SearchField
width="large"
placeholder="タイトル、ユーザー名、メールアドレスなどで検索"
marginRight
marginSize="small"
/>
<Button>検索</Button>
</WithSideContent>
{noResults.length > 0 ? (
<ListTable
mr={-1.5}
ml={-1.5}
headers={[
{
value: 'タイトル',
minWidth: 15,
onClick: () => sort('title'),
ordering: (sortKey == 'title' && sortOrder) || 'init',
},
{
value: 'ユーザー',
minWidth: 10,
onClick: () => sort('user'),
ordering: (sortKey == 'user' && sortOrder) || 'init',
},
{
value: '金額',
minWidth: 5,
alignRight: true,
onClick: () => sort('amount'),
ordering: (sortKey == 'amount' && sortOrder) || 'init',
},
{
value: 'ステータス',
onClick: () => sort('status'),
ordering: (sortKey == 'status' && sortOrder) || 'init',
},
{
value: '作成日',
onClick: () => sort('date'),
ordering: (sortKey == 'date' && sortOrder) || 'init',
},
{ value: '操作' },
]}
rows={noResults.map((row, i) => ({
url: `/path/to/single/${i}`,
cells: [
{ value: row.title },
{ value: row.user, breakWord: true },
{ value: Digits.formalize(row.amount), alignRight: true },
{ value: row.status },
{ value: row.date },
{
value: (
<>
<Button mr={0.5} small appearance="tertiary">
コピー
</Button>
<Button mr={0.5} danger small appearance="tertiary">
削除
</Button>
</>
),
},
],
}))}
></ListTable>
) : (
<NoSearchResults mt={1} />
)}
</ContentsBase>
</Container>
);
};
export const NoDataCreatedYet = () => {
const { sort, sortKey, sortOrder, noResults } = useData();
return (
<Container width="wide">
<ContentsBase>
<Breadcrumbs
mb={1}
links={[
{ title: 'ホーム', url: '/' },
{ title: '申請一覧', url: '/path/' },
]}
/>
<HeadlineArea
pageTitle="コレクション表示"
relatedMenus={
<>
<JumpButton url="/path/to/related/1" mr={1}>
関連機能1
</JumpButton>
<JumpButton url="/path/to/related/2" mr={1}>
関連機能2
</JumpButton>
<DropdownButton
dropdownContents={[
{
type: 'selectable',
url: '/path/to/service1',
text: 'サービス1',
target: '_blank',
},
{
type: 'selectable',
url: '/path/to/service2',
text: 'サービス2',
target: '_blank',
},
{ type: 'rule' },
{
type: 'selectable',
url: '/path/to/service3',
text: '関連するアプリを探す',
target: '_blank',
},
]}
buttonLabel="関連サービス・アプリ"
/>
</>
}
>
これはコレクション表示のサンプルです。ここにはこの画面の説明を書きます。説明が必要ないような画面だったら無理して書かなくてええんやで。
</HeadlineArea>
<MarginBase mb={1}>
<Button IconComponent={MdAdd} mr={1}>
新規追加
</Button>
<JumpButton url="/path/to/import">インポート</JumpButton>
</MarginBase>
<WithSideContent
mb={1}
sideContent={
<>
<Paragraph inline mr={0.5}>
<label htmlFor="examples__collection__collectionPage__limitSelect">
表示件数:
</label>
</Paragraph>
<SelectBox
id="examples__collection__collectionPage__limitSelect"
width="xSmall"
options={[
{ name: '10件' },
{ name: '20件' },
{ name: '50件' },
{ name: '100件' },
{ name: '200件' },
]}
mr={1}
/>
<Paragraph inline mr={1}>
0 / 0
</Paragraph>
<DropdownButton
buttonLabel="エクスポート"
dropdownContents={[
{
type: 'selectable',
text: 'CSV形式でエクスポート',
},
{
type: 'selectable',
text: 'JSON形式でエクスポート',
},
{ type: 'rule' },
{
type: 'selectable',
text: 'エクスポート履歴',
},
]}
/>
</>
}
>
<SearchField
width="large"
placeholder="タイトル、ユーザー名、メールアドレスなどで検索"
marginRight
marginSize="small"
/>
<Button>検索</Button>
</WithSideContent>
{noResults.length > 0 ? (
<ListTable
mr={-1.5}
ml={-1.5}
headers={[
{
value: 'タイトル',
minWidth: 15,
onClick: () => sort('title'),
ordering: (sortKey == 'title' && sortOrder) || 'init',
},
{
value: 'ユーザー',
minWidth: 10,
onClick: () => sort('user'),
ordering: (sortKey == 'user' && sortOrder) || 'init',
},
{
value: '金額',
minWidth: 5,
alignRight: true,
onClick: () => sort('amount'),
ordering: (sortKey == 'amount' && sortOrder) || 'init',
},
{
value: 'ステータス',
onClick: () => sort('status'),
ordering: (sortKey == 'status' && sortOrder) || 'init',
},
{
value: '作成日',
onClick: () => sort('date'),
ordering: (sortKey == 'date' && sortOrder) || 'init',
},
{ value: '操作' },
]}
rows={noResults.map((row, i) => ({
url: `/path/to/single/${i}`,
cells: [
{ value: row.title },
{ value: row.user, breakWord: true },
{ value: Digits.formalize(row.amount), alignRight: true },
{ value: row.status },
{ value: row.date },
{
value: (
<>
<Button mr={0.5} small appearance="tertiary">
コピー
</Button>
<Button mr={0.5} danger small appearance="tertiary">
削除
</Button>
</>
),
},
],
}))}
></ListTable>
) : (
<NoDataCreated mt={3}>
<Button appearance="primary" mt={1}>
新規作成
</Button>
</NoDataCreated>
)}
</ContentsBase>
</Container>
);
};
================================================
FILE: examples/Forms.mdx
================================================
import { Story, Canvas } from '@storybook/addon-docs/blocks';
# フォーム
vibes では3種類のフォームの組み方を提供しています
- 縦フォーム
- 横フォーム
- リストフォーム
いずれの場合にも、**アクセシビリティの確保** と **エラーの表示方法** に注意を払ってください。
## 縦フォーム
`DescriptionList` を使用して項目を縦方向に並べるフォーム形式です。
- 入力欄の説明を丁寧に表記したり、グルーピングの表現をしたりしやすい形式です
- 利用頻度が低く、ユーザーの学習効果を期待しづらい場所での使用に適しています
- 行にフィールドが1つしかない場合には `DescriptionList` の `title` = 左側カラムに `<label>` 要素を、
複数ある場合にはそれぞれのフィールドにラベルを配置し( `value` = 右側カラム内に横フォームを配置するのを推奨 )、
必ずフィールドとラベルが紐付けられている状態にしてください
- aXe または Lighthouse で紐付けが行われているか確認することを推奨します
<Canvas>
<Story id="examples-forms--vertical-form" />
</Canvas>
## 横フォーム
`FormControl` と `FormControlGroup` を使用して項目を横方向に並べるフォーム形式です
- たくさんの入力欄をコンパクトに配置する形式です。
- 利用頻度が高く、ユーザーの学習効果を期待でき、描画領域を節約したい場所に適しています
- `FormControl` 内にフィールドが1つしかない場合には、`FormControl` の `fieldId` propにフィールドの `id` を渡し、
必ずフィールドとラベルが紐付けられている状態にしてください
- `FormControl` は `<fieldset>` 要素になっているため、ラジオボタンが配置される場合や複数のフィールドが配置される場合は `fieldId` による紐付けは不要です
- aXe または Lighthouse で紐付けが行われているか確認することを推奨します
<Canvas>
<Story id="examples-forms--horizontal-form" />
</Canvas>
## リストフォーム
オブジェクトが複数持つ子オブジェクトをシングル画面で一括で編集する際などに使用するフォーム形式です。
- たくさんの小さなオブジェクトを一度に編集する場所で使用します
- 子オブジェクトの編集項目が多い場合は、別画面にしたり、`TaskDialog` に切り出すなどを検討してください
- 一度にたくさん編集することが想定されない場合も、別画面にしたり、`TaskDialog` に切り出すなどを検討してください
- ひとつの項目名に対してたくさんのフィールドが紐付くため、`<label>` 要素は使用しません
- `aria-label` にて、 `1行目の日付` のように、何行目を操作しているのかわかるラベルを指定してください
- aXe または Lighthouse で紐付けが行われているか確認することを推奨します
<Canvas>
<Story id="examples-forms--list-form" />
</Canvas>
## フォーム送信のインタラクション
ボタンのラベルが「保存」「作成」「登録」などであっても、ここでは「送信」として扱います。
- 送信中は送信ボタンやその他のアクションボタンを `disabled` にし、マウス操作だけでなくキーボード操作でもアクションできないようにしてください
- `Loading` 等で覆われる場合でも、必ず `disabled` にしてください
- 送信が成功した場合はページ遷移やモーダルの開閉、フォーカスの移動、`FloatingMessageBlock` などによってユーザーに送信が完了したフィードバックをしてください
- 送信が失敗した場合は `FloatingMessageBlock` を `error` で表示し、ユーザーに送信が失敗したフィードバックをしてください
<Canvas>
<Story id="examples-forms--submit-success-interaction" />
</Canvas>
### フォームのエラー表示
- 送信の失敗時には、 `FloatingMessageBlock` でエラーの存在をユーザーに伝え(スクリーンメッセージ)、`Message` で個別のエラーの詳細をユーザーに伝えてください(インラインメッセージ)
- `FloatingMessageBlock` を使用すると、 `aria-live` 属性によりスクリーンリーダー等の支援技術にエラーが起きていることを伝えることができます
- 複数のセクションのある長大なフォームの場合、エラーが発生しているセクションに `MessageBlock` を配置してください(セクションメッセージ)
- 多くの場合、サーバー側でエラーの検証が必須となるため、送信ボタンを押すまで入力内容がエラーとなるかどうかを予測することができません。
そのため、必ずしも「クライアント側でエラー検証を行い、エラーが無くなるまで送信ボタンを押せなくする」必要は **ありません**
- クライアント側で検証できなかったエラーがサーバー側で発見された場合、ユーザーは両方のエラーを修正する2度手間を強いられてしまいます
- もしクライアント側でエラーを修正するまで送信できなくする場合は、送信ボタンの周囲にエラーの修正が必要であることがわかるよう、メッセージを記載してください
- もし入力フィールドごとのエラーメッセージを記載できない場合は、フォーム上部に `MessageBlock` でまとめて記載しても構いません
- その場合でも、同時に `FloatingMessageBlock` を表示し、「エラーが起きていること」に気付けるようにしてください。
- 可能なかぎり、フィールドごとにエラーメッセージを記載するようにしてください
<Canvas>
<Story id="examples-forms--submit-error-interaction" />
</Canvas>
#### ダイアログ内のエラー表示
- `TaskDialog` 内にフォームを配置している場合でも、 `FloatingMessageBlock` を使用してスクリーンメッセージを表示してください
<Canvas>
<Story id="examples-forms--error-on-task-dialog" />
</Canvas>
### フロントエンドでのバリデーション
- フォームのバリデーション(入力内容の検証)はサーバーサイドで必ず行う必要があるため、フロントエンドで行う必要はありません
- フロントエンドで入力内容のバリデーションを行う場合、ユーザーが正しく入力するための補助と位置付けてください
- 入力中のユーザーにストレスを与えないよう、フロントエンでのバリデーションはエラーとは別の形で表現し、送信ボタンを押すなどの形でユーザーが入力を終えた意思が明らかになってからエラーとして表示してください
- 通常、サーバーサイドでも同じようなバリデーションを行うことになるため、エラーとしての表示はそちらの結果を表示するかたちに寄せてしまうのが良いでしょう
- `Balloon` や `Note` コンポーネントによる注釈は、スクリーンリーダーや拡大表示を使用しているユーザーは気付かない可能性があることに注意してください
- `氏名(カタカナ)` のような入力形式がわかりやすい項目名にしたり、「ハイフンなしで 1410031 のように入力してください」という注釈文を表示したりして、入力開始前に正しい記入方法がわかるようにしてください
- `DigitsInput` のように全角半角などの文字種別を自動的に修正したり、郵便番号のハイフン有り・無しどちらも許容したりするなど、入力形式のエラーが起こりにくい工夫をすることを検討してください
<Canvas>
<Story id="examples-forms--frontend-validation" />
</Canvas>
#### バリデーションエラー時の送信ボタン無効化
- サーバーサイドでバリデーションを行う前提があるため、フロントエンドでのバリデーションが通るまで送信ボタンを無効化したりする必要はありません
- もし無効化する場合はなぜ送信ボタンが無効であるのか、ユーザーが理解できるようにしてください
- ボタンの周囲に注釈テキストを表示したり、バルーンでメッセージを表示したりしてください
- `aria-describedby` によって、それらのメッセージを無効化されたボタンに紐付けてください
- どの入力項目が原因で送信できないのかがわかりにくくなるため、入力項目が多い入力フォームや、必須項目とそうでない項目が順番的に混在しているフォームでの使用は避けてください
- 必須項目とそうでない項目が存在する場合、必須項目を先に、そうでない項目を後に並べるようにしてください
- バリデーション結果をエラーとして表示する場合にはBlur時以降に表示するようにするなど、入力中・入力前のユーザーへのストレスに配慮してください
<Canvas>
<Story id="examples-forms--disable-button-if-invalid" />
</Canvas>
## フォーム作成の注意点
- ラベルとフィールドの紐付けを必ず行ってください
- freeeアクセシビリティー・ガイドライン: [フォーム・コントロールのラベル付けの必要性](https://a11y-guidelines.freee.co.jp/explanations/form-labeling.html)
- aXe または Lighthouse で紐付けが行われているか確認することを推奨します
- ユーザーが自分自身の情報を入力するフォームでは、 `autoComplete` 属性を使用して自動補完が効く状態にしてください
- ただし、自分自身 **以外** の情報を入力するフォームでは、 `autoComplete` を `off` にしてください
- `autoComplete` 属性に何を指定するべきかは、MDN: [HTML の autocomplete 属性](https://developer.mozilla.org/ja/docs/Web/HTML/Attributes/autocomplete) を参照してください
- 安易な `placeholder` の使用は推奨しません
- `placeholder` の文字列は通常のテキストよりもコントラストの低い色の組み合わせになっているため、可読性が下がります
- コントラストの高い色を使用すると、入力済み文字列との区別が付かなくなるおそれがあります
- 記入をし始めてしまうと `placeholder` の内容が確認できなくなります。可読性の問題もあるので記入に必要な情報を `placeholder` にするべきではありません
================================================
FILE: examples/Forms.stories.tsx
================================================
import * as React from 'react';
import Forms from './Forms.mdx';
import {
Button,
CheckBox,
DateField,
DateInput,
DescriptionList,
DigitsInput,
FloatingMessageBlock,
FormActions,
FormControl,
FormControlGroup,
InlineLink,
ListTable,
Loading,
Message,
MessageBlock,
MessageIcon,
NameField,
Note,
SelectBox,
TaskDialog,
TextField,
ToggleButton,
VisuallyHidden,
WithDescriptionContent,
WithBalloon,
RequiredIcon,
} from '../src';
import { MdAdd } from 'react-icons/md';
export default {
title: 'examples/Forms',
parameters: { docs: { page: Forms } },
};
export const VerticalForm = () => (
<>
<DescriptionList
mb={1}
listContents={[
{
// 単体でフィールドを置くときはlabel の htmlFor を忘れない!!!
title: (
<label htmlFor="vertical-form__employee-code">従業員番号</label>
),
value: <TextField id="vertical-form__employee-code" />,
},
// 姓名でフィールドが分かれているので、ここはlabelを使用しない
{
title: (
<>
氏名
<RequiredIcon ml={0.5} />
</>
),
value: <NameField autoComplete="name" required />,
},
{
// 姓名でフィールドが分かれているので、ここはlabelを使用しない
title: '氏名(カタカナ)',
value: (
<WithDescriptionContent
renderContent={() => (
<NameField
lastNamePlaceholder="セイ"
firstNamePlaceholder="メイ"
label="氏名(カタカナ)"
autoComplete="name"
required
/>
)}
renderDescriptionContent={() => (
<Note mt={0.5}>全角カタカナで入力してください</Note>
)}
/>
),
},
{
title: <label htmlFor="vertical-form__display-name">表示名</label>,
value: (
<WithDescriptionContent
renderDescriptionContent={() => (
<Note mt={0.5}>
空欄にした場合、氏名が表示名として使用されます
</Note>
)}
renderContent={(descId) => (
<TextField
// renderDescriptionContentの内容がフィールドの説明として渡されるよう、aria-describedbyに渡している
aria-describedby={descId}
id="vertical-form__display-name"
/>
)}
/>
),
},
{
// 「性別」はSelectBoxのラベルなので、htmlFor で紐付ける
title: <label htmlFor="vertical-form__sex">性別</label>,
value: (
<>
<SelectBox
id="vertical-form__sex"
width="small"
options={[
{ value: '0', name: '未選択' },
{ value: '1', name: '男性' },
{ value: '2', name: '女性' },
{ value: '9', name: 'その他' },
]}
/>
<CheckBox ml={1}>性別を自分以外のメンバーに公開する</CheckBox>
</>
),
},
{
// 複数のフィールドがあるので、ここはlabelを使用しない
title: '住所',
value: (
<>
<FormControlGroup>
{/* フィールドがひとつの場合はfieldIdを指定 */}
<FormControl
label="郵便番号"
fieldId="vertical-form__postal"
required
mb={1}
mr={1}
>
<TextField
id="vertical-form__postal"
width="small"
autoComplete="postal-code"
/>
</FormControl>
{/* フィールドがひとつの場合はfieldIdを指定 */}
<FormControl
label="都道府県"
fieldId="vertical-form__pref"
required
mb={1}
mr={1}
>
{/* 都道府県のSelectBoxを作るのめんどくさい(許して) */}
<TextField
id="vertical-form__pref"
width="medium"
autoComplete="address-level1"
/>
</FormControl>
{/* フィールドがひとつの場合はfieldIdを指定 */}
<FormControl
label="市区町村"
fieldId="vertical-form__city"
required
mb={1}
mr={1}
>
{/* 都道府県のSelectBoxを作るのめんどくさい(許して) */}
<TextField
id="vertical-form__city"
width="medium"
autoComplete="address-level2"
/>
</FormControl>
</FormControlGroup>
<FormControlGroup>
{/* フィールドがひとつの場合はfieldIdを指定 */}
<FormControl
label="町名・番地"
fieldId="vertical-form__address-line-1"
required
mb={1}
mr={1}
>
<TextField
id="vertical-form__address-line-1"
width="large"
autoComplete="street-address"
/>
</FormControl>
</FormControlGroup>
<FormControlGroup>
{/* フィールドがひとつの場合はfieldIdを指定 */}
<FormControl
label="建物名・部屋番号"
fieldId="vertical-form__address-line-2"
mb={1}
mr={1}
>
<TextField
id="vertical-form__address-line-2"
width="large"
autoComplete="street-address"
/>
</FormControl>
</FormControlGroup>
</>
),
},
{
// 年月日でフィールドが分かれているので、<label>を使用しない
title: '生年月日',
value: (
<DateField
selectedDate="1990-01-01"
startDate="1900-01-01"
endDate="2021-12-31"
autoComplete="bday"
/>
),
},
{
// 単体でフィールドを置くときはlabel の htmlFor を忘れない!!!
title: (
<label htmlFor="vertical-form__pension_num">基礎年金番号</label>
),
value: (
<WithDescriptionContent
renderDescriptionContent={() => (
<Note mt={0.5}>
基礎年金番号は10桁の数字で表され、4桁と6桁の組み合わせとなっています。
<InlineLink
href="https://www.nenkin.go.jp/faq/n_net/toroku/moshikomi/20150519.html"
target="_blank"
>
基礎年金番号の調べ方
</InlineLink>
</Note>
)}
renderContent={(descId) => (
<TextField
id="vertical-form__pension_num"
// renderDescriptionContentの内容がフィールドの説明として渡されるよう、aria-describedbyに渡している
aria-describedby={descId}
/>
)}
/>
),
},
]}
/>
<FormActions>
<Button appearance="primary">保存</Button>
<Button>キャンセル</Button>
</FormActions>
</>
);
export const HorizontalForm = () => (
<>
<FormControlGroup>
<FormControl mb={1} mr={1} label="種別">
<ToggleButton name="horizontal-form__type" type="radio">
収入
</ToggleButton>
<ToggleButton name="horizontal-form__type" type="radio">
支出
</ToggleButton>
</FormControl>
<FormControl
mb={1}
mr={1}
label="口座"
fieldId="horizontal-form__account"
>
<SelectBox
id="horizontal-form__account"
width="small"
options={[
{ name: '現金' },
{ name: 'freee銀行' },
{ name: '五反田銀行' },
]}
/>
</FormControl>
<FormControl
mb={1}
mr={1}
label="取引先"
required
help="入出金の相手となる会社名や個人名を入力します"
fieldId="horizontal-form__partner"
>
<TextField id="horizontal-form__partner" required />
</FormControl>
<FormControl
mb={1}
mr={1}
label="金額"
required
fieldId="horizontal-form__amount"
>
<DigitsInput id="horizontal-form__amount" />
</FormControl>
<FormControl
mb={1}
mr={1}
label="摘要"
fieldId="hofizontal-form__description"
help="入出金の目的などを記載します"
>
<TextField width="large" id="hofizontal-form__description" required />
</FormControl>
</FormControlGroup>
<FormActions>
<Button appearance="primary">登録</Button>
</FormActions>
</>
);
export const ListForm = () => {
const [values, setValues] = React.useState<
{
date: string;
type: 'income' | 'expense';
amount: number;
note: string;
}[]
>([{ date: '2021-04-01', type: 'income', amount: 0, note: '' }]);
return (
<>
<ListTable
mb={0.5}
headers={[
{ value: '日付' },
{ value: '収支' },
// 通常のListTableでは金額は右寄せで配置するが、DigitsInput内が右寄せであり、DigitsInputの左端と揃えたいため、見出しセルは左寄せで配置する
{ value: '金額' },
{
value: (
<>
備考
<MessageIcon label="ヘルプ" small>
メモとして自由に使える欄です
</MessageIcon>
</>
),
},
{ value: <VisuallyHidden>行の操作</VisuallyHidden> },
]}
rows={values.map((v, i) => ({
cells: [
{
value: (
<DateInput
value={v.date}
// 何行目の何の項目なのかわかるラベルにする
label={`${i + 1}行目の日付`}
width="small"
onChange={(d) =>
setValues([
...values.slice(0, i),
{ ...v, date: d },
...values.slice(i + 1),
])
}
/>
),
},
{
value: (
<SelectBox
value={v.type}
// 何行目の何の項目なのかわかるラベルにする
// 視覚的に見えるラベルとN:1なので、<label>要素は使用せず、aria-labelで表現する
label={`${i + 1}行目の収支`}
width="xSmall"
onChange={(e) =>
setValues([
...values.slice(0, i),
{
...v,
type:
e.target.value === 'income' ? 'income' : 'expense',
},
...values.slice(i + 1),
])
}
options={[
{ value: 'income', name: '収入' },
{ value: 'expense', name: '支出' },
]}
/>
),
},
{
value: (
<DigitsInput
value={v.amount}
// 何行目の何の項目なのかわかるラベルにする
// 視覚的に見えるラベルとN:1なので、<label>要素は使用せず、aria-labelで表現する
label={`${i + 1}行目の金額`}
onChange={(a) =>
setValues([
...values.slice(0, i),
{ ...v, amount: a || 0 },
...values.slice(i + 1),
])
}
/>
),
},
{
value: (
<TextField
value={v.note}
// 何行目の何の項目なのかわかるラベルにする
// 視覚的に見えるラベルとN:1なので、<label>要素は使用せず、aria-labelで表現する
label={`${i + 1}行目の備考`}
width="large"
onChange={(e) =>
setValues([
...values.slice(0, i),
{ ...v, note: e.target.value },
...values.slice(i + 1),
])
}
/>
),
},
{
alignRight: true,
value: (
<Button
onClick={() =>
setValues([...values.slice(0, i), ...values.slice(i + 1)])
}
appearance="tertiary"
small
disabled={values.length === 1}
>
行を削除
</Button>
),
},
],
}))}
/>
<Button
IconComponent={MdAdd}
iconPosition="left"
onClick={() =>
setValues([
...values,
{ date: '2021-04-01', amount: 0, type: 'income', note: '' },
])
}
// ListTableと左端を揃えるため、左側に1.5remのマージンを持たせている。
// 通常の使用では、ListTableの側に-1.5remのネガティブマージンを付けることも多いはず
ml={1.5}
>
行を追加
</Button>
</>
);
};
export const SubmitSuccessInteraction = () => {
const [message, setMessage] = React.useState('');
const [sending, setSending] = React.useState(false);
return (
<>
<Note mb={1}>
「保存」ボタンで送信中〜送信完了の動きを試すことができます
</Note>
<FormActions>
<Button
appearance="primary"
disabled={sending}
onClick={() => {
setMessage('');
setSending(true);
setTimeout(() => {
setMessage('保存しました');
setSending(false);
}, 600);
}}
>
保存
</Button>
<Button disabled={sending}>キャンセル</Button>
</FormActions>
<Loading coverAll isLoading={sending} />
{message && <FloatingMessageBlock success message={message} />}
</>
);
};
export const ErrorOnTaskDialog = () => {
const [message, setMessage] = React.useState('');
const [sending, setSending] = React.useState(false);
const [error, setError] = React.useState(false);
const [modalOpen, setModalOpen] = React.useState(false);
return (
<>
<Button onClick={() => setModalOpen(true)}>編集ダイアログを開く</Button>
<TaskDialog
isOpen={modalOpen}
title="編集"
primaryButtonLabel="保存"
closeButtonLabel="閉じる"
onRequestClose={() => setModalOpen(false)}
onPrimaryAction={() => {
setMessage('');
setSending(true);
setError(false);
setTimeout(() => {
setMessage(
'入力内容にエラーがあります。修正のうえ、再度お試しください'
);
setError(true);
setSending(false);
}, 600);
}}
disabled={sending}
>
{message && <MessageBlock error mb={1} message={message} />}
<DescriptionList
mr={-1.5}
ml={-1.5}
listContents={[
{
title: (
<label htmlFor="submit-error-interaction__amount">
振込金額
<RequiredIcon ml={0.5} />
</label>
),
value: (
<>
<DigitsInput
id="submit-error-interaction__amount"
required
error={error}
value={0}
/>
{error && (
<Message ml={1} error>
0より大きい数値を入力してください
</Message>
)}
</>
),
},
{
title: (
<label htmlFor="submit-error-interaction__account-from">
振込元口座
<RequiredIcon ml={0.5} />
</label>
),
value: (
<>
<SelectBox
id="submit-error-interaction__account-from"
options={[
{ name: '選択してください', value: 'default' },
{ name: 'freee銀行' },
{ name: '五反田銀行' },
{ name: '品川信用金庫' },
]}
error={error}
value="default"
required
/>
{error && (
<Message ml={1} error>
口座を選択してください
</Message>
)}
</>
),
},
{
title: (
<label htmlFor="submit-error-interaction__name-from">
振込名義
</label>
),
value: (
<>
<WithDescriptionContent
renderContent={(descId) => (
<>
<TextField
id="submit-error-interaction__name-from"
// エラーメッセージがaria-desdribedbyになっているべきか悩ましいが、
// ここでは他のフィールドと仕様をあわせやすいよう、エラーメッセージをaria-describedに入れない判断をした
aria-describedby={descId}
error={error}
value="ふりーたろう"
/>
{error && (
<Message ml={1} error>
半角カタカナで記入してください
</Message>
)}
</>
)}
renderDescriptionContent={() => (
<Note mt={0.5}>
振込先に通知される名義を半角カタカナで記入してください。空欄にした場合は口座名義がそのまま使用されます。
</Note>
)}
/>
</>
),
},
{
title: '振込先口座',
value: (
<>
<FormControlGroup>
<FormControl
label="銀行コード"
fieldId="submit-error-interaction__bank_to"
help="銀行ごとに指定されている4桁の数字です"
required
mr={1.5}
mb={1}
>
<TextField
id="submit-error-interaction__bank_to"
width="small"
required
value={'123'}
error={error}
/>
{error && (
<MessageIcon error label="エラー" ml={0.5}>
4桁の半角数字で記入してください
</MessageIcon>
)}
</FormControl>
<FormControl
label="支店コード"
fieldId="submit-error-interaction__branch_to"
help="支店ごとに指定されている3桁の数字です"
required
mr={1.5}
mb={1}
>
<TextField
id="submit-error-interaction__branch_to"
width="xSmall"
required
value=""
error={error}
/>
{error && (
<MessageIcon error label="エラー" ml={0.5}>
3桁の半角数字で記入してください
</MessageIcon>
)}
</FormControl>
</FormControlGroup>
<FormControlGroup>
<FormControl
// ここだけエラー内容が思いつかんかった
label="口座種別"
fieldId="submit-error-interaction__account_type_to"
required
mr={1.5}
>
<SelectBox
id="submit-error-interaction__account_type_to"
options={[
{ name: '普通' },
{ name: '当座' },
{ name: '定期' },
]}
width="xSmall"
required
/>
</FormControl>
<FormControl
label="口座番号"
fieldId="submit-error-interaction__account_number_to"
required
mr={1.5}
mb={1}
>
<TextField
id="submit-error-interaction__account_number_to"
width="medium"
required
value=""
error={error}
/>
{error && (
<MessageIcon error label="エラー" ml={0.5}>
7桁の半角数字で記入してください
</MessageIcon>
)}
</FormControl>
<FormControl
label="口座名義"
fieldId="submit-error-interaction__account_name_to"
required
mr={1.5}
mb={1}
>
<TextField
id="submit-error-interaction__account_name_to"
width="medium"
required
value="ふりーはなこ"
error={error}
/>
{error && (
<MessageIcon error label="エラー" ml={0.5}>
半角カタカナで記入してください
</MessageIcon>
)}
</FormControl>
</FormControlGroup>
</>
),
},
]}
/>
<Loading coverAll isLoading={sending} />
{message && <FloatingMessageBlock error message={message} />}
</TaskDialog>
</>
);
};
export const SubmitErrorInteraction = () => {
const [message, setMessage] = React.useState('');
const [sending, setSending] = React.useState(false);
const [error, setError] = React.useState(false);
return (
<>
<Note mb={1}>
「保存」ボタンで送信中〜エラー時の動きを試すことができます
</Note>
{message && <MessageBlock error mb={1} message={message} />}
<DescriptionList
mb={1}
listContents={[
{
title: (
<label htmlFor="submit-error-interaction__amount">
振込金額
<RequiredIcon ml={0.5} />
</label>
),
value: (
<>
<DigitsInput
id="submit-error-interaction__amount"
required
error={error}
value={0}
/>
{error && (
<Message ml={1} error>
0より大きい数値を入力してください
</Message>
)}
</>
),
},
{
title: (
<label htmlFor="submit-error-interaction__account-from">
振込元口座
<RequiredIcon ml={0.5} />
</label>
),
value: (
<>
<SelectBox
id="submit-error-interaction__account-from"
options={[
{ name: '選択してください', value: 'default' },
{ name: 'freee銀行' },
{ name: '五反田銀行' },
{ name: '品川信用金庫' },
]}
error={error}
value="default"
required
/>
{error && (
<Message ml={1} error>
口座を選択してください
</Message>
)}
</>
),
},
{
title: (
<label htmlFor="submit-error-interaction__name-from">
振込名義
</label>
),
value: (
<>
<WithDescriptionContent
renderContent={(descId) => (
<>
<TextField
id="submit-error-interaction__name-from"
// エラーメッセージがaria-desdribedbyになっているべきか悩ましいが、
// ここでは他のフィールドと仕様をあわせやすいよう、エラーメッセージをaria-describedに入れない判断をした
aria-describedby={descId}
error={error}
value="ふりーたろう"
/>
{error && (
<Message ml={1} error>
半角カタカナで記入してください
</Message>
)}
</>
)}
renderDescriptionContent={() => (
<Note mt={0.5}>
振込先に通知される名義を半角カタカナで記入してください。空欄にした場合は口座名義がそのまま使用されます。
</Note>
)}
/>
</>
),
},
{
title: '振込先口座',
value: (
<>
<FormControlGroup>
<FormControl
label="銀行コード"
fieldId="submit-error-interaction__bank_to"
help="銀行ごとに指定されている4桁の数字です"
required
mr={1.5}
mb={1}
>
<TextField
id="submit-error-interaction__bank_to"
width="small"
required
value={'123'}
error={error}
/>
{error && (
<MessageIcon error label="エラー" ml={0.5}>
4桁の半角数字で記入してください
</MessageIcon>
)}
</FormControl>
<FormControl
label="支店コード"
fieldId="submit-error-interaction__branch_to"
help="支店ごとに指定されている3桁の数字です"
required
mr={1.5}
mb={1}
>
<TextField
id="submit-error-interaction__branch_to"
width="xSmall"
required
value=""
error={error}
/>
{error && (
<MessageIcon error label="エラー" ml={0.5}>
3桁の半角数字で記入してください
</MessageIcon>
)}
</FormControl>
</FormControlGroup>
<FormControlGroup>
<FormControl
// ここだけエラー内容が思いつかんかった
label="口座種別"
fieldId="submit-error-interaction__account_type_to"
required
mr={1.5}
>
<SelectBox
id="submit-error-interaction__account_type_to"
options={[
{ name: '普通' },
{ name: '当座' },
{ name: '定期' },
]}
width="xSmall"
required
/>
</FormControl>
<FormControl
label="口座番号"
fieldId="submit-error-interaction__account_number_to"
required
mr={1.5}
mb={1}
>
<TextField
id="submit-error-interaction__account_number_to"
width="medium"
required
value=""
error={error}
/>
{error && (
<MessageIcon error label="エラー" ml={0.5}>
7桁の半角数字で記入してください
</MessageIcon>
)}
</FormControl>
<FormControl
label="口座名義"
fieldId="submit-error-interaction__account_name_to"
required
mr={1.5}
mb={1}
>
<TextField
id="submit-error-interaction__account_name_to"
width="medium"
required
value="ふりーはなこ"
error={error}
/>
{error && (
<MessageIcon error label="エラー" ml={0.5}>
半角カタカナで記入してください
</MessageIcon>
)}
</FormControl>
</FormControlGroup>
</>
),
},
]}
/>
<FormActions>
<Button
appearance="primary"
disabled={sending}
onClick={() => {
setMessage('');
setSending(true);
setError(false);
setTimeout(() => {
setMessage(
'入力内容にエラーがあります。修正のうえ、再度お試しください'
);
setError(true);
setSending(false);
}, 600);
}}
>
保存
</Button>
<Button disabled={sending}>キャンセル</Button>
</FormActions>
<Loading coverAll isLoading={sending} />
{message && <FloatingMessageBlock error message={message} />}
</>
);
};
export const FrontendValidation = () => {
const [value, setValue] = React.useState('');
const validationMessage = !value.match(/^[ァ-ヾ]*$/)
? '全角カタカナで入力してください'
: '';
const [error, setError] = React.useState(false);
const [statusMessage, setStatusMessage] = React.useState('');
const [serverValidationMessage, setServerValidationMessage] =
React.useState('');
const [sending, setSending] = React.useState(false);
return (
<>
<FormControl
fieldId="frontend-validation__corporate-name-kana"
label="事業所名(カタカナ)"
mb={1}
>
<WithBalloon
border="notice"
balloonDisabled={!validationMessage}
renderBalloonContent={() => validationMessage}
renderContent={() => (
<TextField
id="frontend-validation__corporate-name-kana"
value={value}
onChange={(e) => setValue(e.target.value)}
error={error}
/>
)}
/>
<VisuallyHidden autoRead>
{/* スクリーンリーダー向けに、フロントエンドバリデーションのメッセージをVisuallyHiddenで配置しています
`autoRead` を渡すことで、 `aria-live="polite"` になっているが、これが表示されているタイミングは入力中であることが多いため、自動的に読み上げられる可能性は低い。
(サーバーサイドのものでいいので)バリデーション結果が正しく伝わるよう、代替手段を提供する必要がある。
この例では保存ボタン押下後にFloatingMessageBlockで通知されるため、気付くことができる。
*/}
{validationMessage}
</VisuallyHidden>
{serverValidationMessage && (
<Message ml={1} error>
{serverValidationMessage}
</Message>
)}
</FormControl>
<Button
appearance="primary"
disabled={sending}
onClick={() => {
setStatusMessage('');
setServerValidationMessage('');
setError(false);
setSending(true);
setTimeout(() => {
setSending(false);
if (!value.match(/^[ァ-ヾ]*$/)) {
setError(true);
setServerValidationMessage('全角カタカナで入力してください');
setStatusMessage(
'入力内容にエラーがあります。修正のうえ、再度お試しください'
);
} else {
setStatusMessage('保存しました');
}
}, 600);
}}
>
保存
</Button>
<Loading coverAll isLoading={sending} />
{statusMessage && (
<FloatingMessageBlock
error={error}
success={!error}
message={statusMessage}
/>
)}
</>
);
};
export const DisableButtonIfInvalid = () => {
const [values, setValues] = React.useState({
companyName: '',
companyNameKana: '',
notes: '',
});
const [errorMessages, setErrorMessages] = React.useState({
companyName: '',
companyNameKana: '',
notes: '',
});
const validatedAsRequired =
values.companyName.length > 0 && values.companyNameKana.length > 0;
const validatedAsKatakana = !!values.companyNameKana.match(/^[ァ-ヾ]*$/);
const [statusMessage, setStatusMessage] = React.useState('');
const [sending, setSending] = React.useState(false);
return (
<>
<DescriptionList
mb={1}
listContents={[
{
title: (
<label htmlFor="disable-button-if-invalid__corporate-name">
事業所名
<RequiredIcon ml={0.5} />
</label>
),
value: (
<>
<TextField
id="disable-button-if-invalid__corporate-name"
value={values.companyName}
onChange={(e) =>
setValues({
...values,
companyName: e.target.value,
})
}
onBlur={() =>
setErrorMessages({
...errorMessages,
companyName:
values.companyName.length > 0
? ''
: '事業所名は必須です',
})
}
required
error={!!errorMessages.companyName}
/>
{errorMessages.companyName && (
<Message error ml={1}>
{errorMessages.companyName}
</Message>
)}
</>
),
},
{
title: (
<label htmlFor="disable-button-if-invalid__corporate-name-kana">
事業所名(カタカナ)
<RequiredIcon ml={0.5} />
</label>
),
value: (
<>
<WithBalloon
border="notice"
balloonDisabled={validatedAsKatakana}
renderBalloonContent={() =>
!validatedAsKatakana ? '全角カタカナで入力してください' : ''
}
renderContent={() => (
<TextField
id="disable-button-if-invalid__corporate-name-kana"
value={values.companyNameKana}
onChange={(e) =>
setValues({
...values,
companyNameKana: e.target.value,
})
}
onBlur={() =>
setErrorMessages({
...errorMessages,
companyNameKana: !validatedAsKatakana
? '全角カタカナで入力してください'
: values.companyNameKana.length == 0
? '事業所名(カタカナ)は必須です'
: '',
})
}
required
error={!!errorMessages.companyNameKana}
/>
)}
/>
<VisuallyHidden autoRead>
{/*
スクリーンリーダー向けに、フロントエンドバリデーションのメッセージをVisuallyHiddenで配置しています
`autoRead` を渡すことで、 `aria-live="polite"` になっているが、これが表示されているタイミングは入力中であることが多いため、自動的に読み上げられる可能性は低い。
(サーバーサイドのものでいいので)バリデーション結果が正しく伝わるよう、代替手段を提供する必要がある。
この例では保存ボタン押下後にFloatingMessageBlockで通知されるため、気付くことができる。
*/}
{!validatedAsKatakana ? '全角カタカナで入力してください' : ''}
</VisuallyHidden>
{errorMessages.companyNameKana && (
<Message error ml={1}>
{errorMessages.companyNameKana}
</Message>
)}
</>
),
},
{
title: (
<label htmlFor="disable-button-if-invalid__notes">備考</label>
),
value: (
<TextField
id="disable-button-if-invalid__notes"
value={values.notes}
onChange={(e) =>
setValues({ ...values, notes: e.target.value })
}
width="large"
/>
),
},
]}
/>
<WithDescriptionContent
position="horizontal"
renderContent={(descriptionId) => (
<Button
appearance="primary"
disabled={sending || !(validatedAsKatakana && validatedAsRequired)}
aria-describedby={descriptionId}
onClick={() => {
setStatusMessage('');
setSending(true);
setTimeout(() => {
setSending(false);
setStatusMessage('保存しました');
}, 600);
}}
>
保存
</Button>
)}
renderDescriptionContent={() =>
!validatedAsRequired ? (
<Note ml={0.5}>必須項目をすべて入力してください</Note>
) : !validatedAsKatakana ? (
<Note ml={0.5}>入力内容に誤りがあります。ご確認ください</Note>
) : undefined
}
/>
<Loading coverAll isLoading={sending} />
{statusMessage && (
<FloatingMessageBlock success message={statusMessage} />
)}
</>
);
};
================================================
FILE: examples/ImportWizard.mdx
================================================
import { Story, Canvas } from '@storybook/addon-docs/blocks';
# インポートウィザード
インポートによってオブジェクトの新規追加を行う画面の例です。
- スクリーンリーダーのユーザーがページが切り変わったことに気付けるよう、ステップを移動するたびPageTitleにフォーカスが移動するようになっています。
- ウィザードとして実装してあり、グローバルナビ等を配置しない想定になっています。
<Canvas>
{/* Container を使っているとinlineでは見辛いので、inline={false} にしている */}
<Story id="examples-importwizard--import-walkthrough" inline={false} height={800} />
</Canvas>
## ファイルの準備ステップ
テンプレートファイルのダウンロード導線と、アップロードのUIを配置します。アップロードすることで次のステップに進めます。
<Canvas>
<Story id="examples-importwizard--upload-step" />
</Canvas>
## 内容確認ステップ
アップロードされたファイルの内容から、インポート後の状態を表示しています。エラーがある場合には MessageIcon で表現してください。
<Canvas>
<Story id="examples-importwizard--confirm-step" />
</Canvas>
## 実行中ステップ
インポート作業を実行中のステップです。前のステップには戻れなくなっています。ポーリング等を使って現在の状態を取得し、完了していたら次のステップに遷移する想定です。
実行中・完了済みのインポートがいつでも確認できるようになっていれば、ここからウィザードを抜ける導線を用意しても問題ありません。
<Canvas>
<Story id="examples-importwizard--execution-step" />
</Canvas>
## 完了ステップ
インポートによって作成されたデータを示し、一覧または再実行への導線を示します。
<Canvas>
<Story id="examples-importwizard--completed-step" />
</Canvas>
================================================
FILE: examples/ImportWizard.stories.tsx
================================================
import * as React from 'react';
import { MdFileDownload } from 'react-icons/md';
import ImportWizard from './ImportWizard.mdx';
import {
Container,
ContentsBase,
Breadcrumbs,
PageTitle,
Paragraph,
Button,
FileUploader,
FileTypes,
Stepper,
SectionTitle,
JumpButton,
FormActions,
NumericTable,
Loading,
Digits,
MarginBase,
} from '../src';
export default {
title: 'examples/ImportWizard',
parameters: { docs: { page: ImportWizard } },
};
const requestMock = (wait = 700) => {
// eslint-disable-next-line compat/compat
return new Promise<void>((resolve) => setTimeout(() => resolve(), wait));
};
export const ImportWalkthrough = () => {
const [isRequesting, setIsRequesting] = React.useState(false);
const [currentStep, setCurrentStep] = React.useState(0);
const headRef = React.useRef<HTMLHeadingElement>(null);
// 実行中ステップに入ったときだけ、3秒で次のステップへ
React.useEffect(() => {
if (currentStep === 2) {
setTimeout(() => setCurrentStep(3), 3000);
}
// stepが変わったら見出しにフォーカス
headRef.current?.focus();
}, [currentStep]);
return (
<Container width="narrow">
<ContentsBase>
<Breadcrumbs
mb={1}
links={[
{ title: 'ホーム', url: '/' },
{ title: '申請一覧', url: '/path/' },
{ title: 'インポート' },
]}
/>
<Stepper
currentStepIndex={currentStep}
steps={['ファイルの準備', '内容の確認', '実行', '完了']}
disabledStepIndex={[]}
/>
{currentStep === 0 ? (
<UploadStep
isRequesting={isRequesting}
headRef={headRef}
onFileSelect={async () => {
setIsRequesting(true);
await requestMock();
setCurrentStep(currentStep + 1);
setIsRequesting(false);
}}
/>
) : currentStep === 1 ? (
<ConfirmStep
isRequesting={isRequesting}
headRef={headRef}
onBack={() => setCurrentStep(currentStep - 1)}
onConfirm={async () => {
setIsRequesting(true);
await requestMock(1000);
setCurrentStep(currentStep + 1);
setIsRequesting(false);
}}
/>
) : currentStep === 2 ? (
<ExecutionStep headRef={headRef} />
) : (
<CompletedStep headRef={headRef} onRetry={() => setCurrentStep(0)} />
)}
</ContentsBase>
</Container>
);
};
export const UploadStep = ({
headRef = React.createRef<HTMLHeadingElement>(),
onFileSelect = () => {
return;
},
isRequesting = false,
}: {
headRef: React.Ref<HTMLHeadingElement>;
onFileSelect: () => any;
isRequesting: boolean;
}) => (
<>
<PageTitle textAlign="center" mb={1} ref={headRef}>
申請のインポート
</PageTitle>
<Paragraph mb={1} textAlign="center">
複数の申請を一括して追加することができます。
<br />
テンプレートのCSVファイルをMicrosoft
Excel等で編集して、アップロードしてください。
</Paragraph>
<Paragraph mb={2} textAlign="center">
<Button IconComponent={MdFileDownload}>
テンプレートCSVファイルのダウンロード
</Button>
</Paragraph>
<SectionTitle mb={1} textAlign="center">
ファイルをアップロードして次のステップへ
</SectionTitle>
{/* TODO: こういうの↓を提供してくれるコンポーネントほしいね */}
<MarginBase fitContent mr="auto" ml="auto">
<FileUploader
fileLabel="CSVファイル"
isUploading={isRequesting}
acceptFileTypes={[FileTypes.CSV]}
onFileSelect={onFileSelect}
/>
</MarginBase>
</>
);
const ConfirmTable = () => (
<NumericTable
ml={-1.5}
mr={-1.5}
mb={1}
headers={[
{ value: 'タイトル' },
{ value: '申請者' },
{ value: '日付' },
{ value: '金額', alignRight: true },
]}
rows={[
{
cells: [
{ value: '打ち合わせ費用' },
{ value: 'フリー太郎' },
{ value: '2020-10-01' },
{ value: Digits.formalize(10000), alignRight: true },
],
},
{
cells: [
{ value: '打ち合わせ費用' },
{ value: 'フリー太郎' },
{ value: '2020-10-01' },
{ value: Digits.formalize(10000), alignRight: true },
],
},
{
cells: [
{ value: '打ち合わせ費用' },
{ value: 'フリー太郎' },
{ value: '2020-10-01' },
{ value: Digits.formalize(10000), alignRight: true },
],
},
{
cells: [
{ value: '打ち合わせ費用' },
gitextract_1r4gz165/ ├── .babelrc ├── .circleci/ │ └── config.yml ├── .dockerignore ├── .editorconfig ├── .eslintignore ├── .eslintrc.js ├── .flowconfig ├── .github/ │ ├── PULL_REQUEST_TEMPLATE.md │ └── workflows/ │ ├── publish_package_to_npmjs.yml │ ├── publish_storybook.yml │ └── pull_request_test_and_lint.yml ├── .gitignore ├── .husky/ │ └── pre-commit ├── .jest/ │ ├── Mock.js │ └── setup.js ├── .node-version ├── .npmrc ├── .prettierignore ├── .storybook/ │ ├── main.ts │ ├── manager.js │ └── preview.tsx ├── Dockerfile ├── LICENSE.txt ├── Makefile ├── README.md ├── __tests__/ │ └── types/ │ └── flow/ │ ├── .flowconfig │ ├── lv1.js │ ├── lv2.js │ └── utilities.js ├── docker/ │ └── git-credential-github-token ├── docs/ │ ├── Colors.stories.mdx │ ├── Contribution.stories.mdx │ ├── Design/ │ │ ├── Layout/ │ │ │ └── Layout.stories.mdx │ │ └── Readme.stories.mdx │ ├── Readme.stories.mdx │ ├── Storybook.stories.mdx │ ├── Stylesheets.stories.mdx │ └── TypeScript.stories.mdx ├── examples/ │ ├── Collection.mdx │ ├── Collection.stories.tsx │ ├── Forms.mdx │ ├── Forms.stories.tsx │ ├── ImportWizard.mdx │ ├── ImportWizard.stories.tsx │ ├── Pages.stories.tsx │ ├── ResponsiveLayout.mdx │ ├── ResponsiveLayout.stories.tsx │ ├── ThroughCommonProps.mdx │ └── ThroughCommonProps.stories.tsx ├── index.d.ts ├── index.js ├── index.js.flow ├── injectFileName.js ├── jest.config.js ├── lv1.js.flow ├── lv2.js.flow ├── package.json ├── scripts/ │ └── release.js ├── src/ │ ├── @types/ │ │ ├── image.d.ts │ │ ├── mdx.d.ts │ │ └── redux.d.ts │ ├── constants/ │ │ ├── Color.ts │ │ ├── Font.ts │ │ ├── Size.ts │ │ ├── ZIndex.ts │ │ └── index.ts │ ├── hooks/ │ │ └── useUniqueId.ts │ ├── index.ts │ ├── internal/ │ │ └── CommonStyle.ts │ ├── lv1/ │ │ ├── InlineSpinner.stories.tsx │ │ ├── InlineSpinner.tsx │ │ ├── Loading/ │ │ │ ├── loading-parts.json │ │ │ └── loading-whole-2021.json │ │ ├── Loading.stories.tsx │ │ ├── Loading.tsx │ │ ├── a11y/ │ │ │ ├── FocusHighlight.stories.tsx │ │ │ ├── FocusHighlight.tsx │ │ │ ├── FocusTrap.stories.tsx │ │ │ ├── FocusTrap.tsx │ │ │ ├── VisuallyHidden.stories.tsx │ │ │ └── VisuallyHidden.tsx │ │ ├── bases/ │ │ │ ├── Balloon.stories.tsx │ │ │ ├── Balloon.tsx │ │ │ ├── CardBase.stories.tsx │ │ │ ├── CardBase.tsx │ │ │ ├── ColumnBase.stories.tsx │ │ │ ├── ColumnBase.tsx │ │ │ ├── Container.stories.tsx │ │ │ ├── Container.tsx │ │ │ ├── ContentsBase.stories.tsx │ │ │ ├── ContentsBase.tsx │ │ │ ├── DialogBase.stories.tsx │ │ │ ├── DialogBase.tsx │ │ │ ├── FloatingBase.stories.tsx │ │ │ ├── FloatingBase.tsx │ │ │ ├── MarginBase.stories.tsx │ │ │ ├── MarginBase.tsx │ │ │ ├── NegativeContentsBase.stories.tsx │ │ │ ├── NegativeContentsBase.tsx │ │ │ ├── NegativeMarginBase.stories.tsx │ │ │ ├── NegativeMarginBase.tsx │ │ │ ├── PopupBase.stories.tsx │ │ │ ├── PopupBase.tsx │ │ │ ├── ScrimBase.stories.tsx │ │ │ ├── ScrimBase.tsx │ │ │ ├── ScrollableBase.stories.tsx │ │ │ ├── ScrollableBase.tsx │ │ │ ├── ZebraBase.stories.tsx │ │ │ ├── ZebraBase.tsx │ │ │ └── types.ts │ │ ├── buttons/ │ │ │ ├── BackwardButton.stories.tsx │ │ │ ├── BackwardButton.tsx │ │ │ ├── Button.stories.tsx │ │ │ ├── Button.tsx │ │ │ ├── GlobalNaviButton.stories.tsx │ │ │ ├── GlobalNaviButton.tsx │ │ │ ├── IconOnlyBackwardButton.stories.tsx │ │ │ ├── IconOnlyBackwardButton.tsx │ │ │ ├── IconOnlyButton.stories.tsx │ │ │ ├── IconOnlyButton.tsx │ │ │ ├── IconOnlyJumpButton.stories.tsx │ │ │ ├── IconOnlyJumpButton.tsx │ │ │ ├── InlineLink.stories.tsx │ │ │ ├── InlineLink.tsx │ │ │ ├── JumpButton.stories.tsx │ │ │ ├── JumpButton.tsx │ │ │ ├── LeftIconButton.stories.tsx │ │ │ ├── LeftIconButton.tsx │ │ │ ├── ListButton.stories.tsx │ │ │ ├── ListButton.tsx │ │ │ ├── PagerButton.stories.tsx │ │ │ ├── PagerButton.tsx │ │ │ ├── RightIconButton.stories.tsx │ │ │ ├── RightIconButton.tsx │ │ │ ├── TabButton.stories.tsx │ │ │ ├── TabButton.tsx │ │ │ ├── TextButton.stories.tsx │ │ │ └── TextButton.tsx │ │ ├── calendar/ │ │ │ ├── CalendarDate.stories.tsx │ │ │ ├── CalendarDate.tsx │ │ │ ├── CalendarHead.stories.tsx │ │ │ └── CalendarHead.tsx │ │ ├── forms/ │ │ │ ├── CheckBox.stories.tsx │ │ │ ├── CheckBox.tsx │ │ │ ├── FormControlLabel.stories.tsx │ │ │ ├── FormControlLabel.tsx │ │ │ ├── NumeralField.stories.tsx │ │ │ ├── NumeralField.tsx │ │ │ ├── OptionButton.stories.tsx │ │ │ ├── OptionButton.tsx │ │ │ ├── RadioButton.stories.tsx │ │ │ ├── RadioButton.tsx │ │ │ ├── ReadOnlyField.stories.tsx │ │ │ ├── ReadOnlyField.tsx │ │ │ ├── SearchField.stories.tsx │ │ │ ├── SearchField.tsx │ │ │ ├── SelectBox.stories.tsx │ │ │ ├── SelectBox.tsx │ │ │ ├── TextArea.stories.tsx │ │ │ ├── TextArea.tsx │ │ │ ├── TextField.stories.tsx │ │ │ ├── TextField.tsx │ │ │ ├── ToggleButton.stories.tsx │ │ │ ├── ToggleButton.tsx │ │ │ └── types.ts │ │ ├── grids/ │ │ │ ├── GridBlock.tsx │ │ │ ├── GridWrapper.tsx │ │ │ └── Grids.stories.tsx │ │ ├── icons/ │ │ │ ├── Avatar.stories.tsx │ │ │ ├── Avatar.tsx │ │ │ ├── MaterialIcon.stories.tsx │ │ │ ├── MaterialIcon.tsx │ │ │ ├── RequiredIcon.stories.tsx │ │ │ ├── RequiredIcon.tsx │ │ │ ├── StatusIcon.stories.tsx │ │ │ └── StatusIcon.tsx │ │ ├── images/ │ │ │ ├── AlertSwallow.stories.tsx │ │ │ ├── AlertSwallow.tsx │ │ │ ├── AppStoreBadge.stories.tsx │ │ │ ├── AppStoreBadge.tsx │ │ │ ├── CloudSkeletonIllust.stories.tsx │ │ │ ├── CloudSkeletonIllust.tsx │ │ │ ├── CloudUploadIllust.stories.tsx │ │ │ ├── CloudUploadIllust.tsx │ │ │ ├── CsvUploadIllust.stories.tsx │ │ │ ├── CsvUploadIllust.tsx │ │ │ ├── DiscoveryIllust.stories.tsx │ │ │ ├── DiscoveryIllust.tsx │ │ │ ├── FileUploadIllust.stories.tsx │ │ │ ├── FileUploadIllust.tsx │ │ │ ├── FinishTaskIllust.stories.tsx │ │ │ ├── FinishTaskIllust.tsx │ │ │ ├── GooglePlayBadge.stories.tsx │ │ │ ├── GooglePlayBadge.tsx │ │ │ ├── ImageUploadIllust.stories.tsx │ │ │ ├── ImageUploadIllust.tsx │ │ │ ├── NoDataIllust.stories.tsx │ │ │ ├── NoDataIllust.tsx │ │ │ ├── NoSearchResultsIllust.stories.tsx │ │ │ ├── NoSearchResultsIllust.tsx │ │ │ ├── NotFoundSwallow.stories.tsx │ │ │ ├── NotFoundSwallow.tsx │ │ │ ├── SwallowContainer.tsx │ │ │ ├── discovery-illust.json │ │ │ └── finish-task-illust.json │ │ ├── index.ts │ │ ├── interactiveParts/ │ │ │ ├── SegmentControlButton.stories.tsx │ │ │ ├── SegmentControlButton.tsx │ │ │ ├── StepBlock.stories.tsx │ │ │ ├── StepBlock.tsx │ │ │ ├── StepBorder.stories.tsx │ │ │ ├── StepBorder.tsx │ │ │ ├── StepNumber.stories.tsx │ │ │ ├── StepNumber.tsx │ │ │ ├── Tab.stories.tsx │ │ │ └── Tab.tsx │ │ ├── layout/ │ │ │ ├── HStack.stories.tsx │ │ │ ├── HStack.tsx │ │ │ ├── Stack.stories.tsx │ │ │ ├── Stack.tsx │ │ │ ├── VStack.stories.tsx │ │ │ ├── VStack.tsx │ │ │ ├── WithDescriptionContent.stories.tsx │ │ │ ├── WithDescriptionContent.tsx │ │ │ ├── WithSideContent.stories.tsx │ │ │ └── WithSideContent.tsx │ │ ├── lists/ │ │ │ ├── BorderTableListCell.stories.tsx │ │ │ ├── BorderTableListCell.tsx │ │ │ ├── CheckBoxCell.stories.tsx │ │ │ ├── CheckBoxCell.tsx │ │ │ ├── DescriptionListCell.stories.tsx │ │ │ ├── DescriptionListCell.tsx │ │ │ ├── DescriptionListHeadCell.stories.tsx │ │ │ ├── DescriptionListHeadCell.tsx │ │ │ ├── TableListCell.stories.tsx │ │ │ ├── TableListCell.tsx │ │ │ ├── TableListHead.stories.tsx │ │ │ ├── TableListHead.tsx │ │ │ ├── TableListHeadCell.stories.tsx │ │ │ ├── TableListHeadCell.tsx │ │ │ ├── TableListRow.stories.tsx │ │ │ ├── TableListRow.tsx │ │ │ ├── TreeFoldingButtonCell.stories.tsx │ │ │ └── TreeFoldingButtonCell.tsx │ │ ├── messages/ │ │ │ ├── Message.stories.tsx │ │ │ └── Message.tsx │ │ ├── progress/ │ │ │ ├── ProgressBar.stories.tsx │ │ │ └── ProgressBar.tsx │ │ ├── skeleton/ │ │ │ └── SkeletonBase.tsx │ │ └── typography/ │ │ ├── InternalHeadline.tsx │ │ ├── Note.stories.tsx │ │ ├── Note.tsx │ │ ├── PageTitle.stories.tsx │ │ ├── PageTitle.tsx │ │ ├── Paragraph.stories.tsx │ │ ├── Paragraph.tsx │ │ ├── SectionTitle.stories.tsx │ │ ├── SectionTitle.tsx │ │ ├── SubSectionTitle.stories.tsx │ │ ├── SubSectionTitle.tsx │ │ ├── Text.stories.tsx │ │ ├── Text.tsx │ │ └── TypographyStyle.ts │ ├── lv2/ │ │ ├── accordionPanel/ │ │ │ ├── AccordionPanel.stories.tsx │ │ │ └── AccordionPanel.tsx │ │ ├── basicTable/ │ │ │ ├── BasicTable.stories.tsx │ │ │ └── BasicTable.tsx │ │ ├── breadcrumbs/ │ │ │ ├── Breadcrumbs.stories.tsx │ │ │ └── Breadcrumbs.tsx │ │ ├── bulletedList/ │ │ │ ├── BulletedList.stories.tsx │ │ │ └── BulletedList.tsx │ │ ├── buttonGroup/ │ │ │ ├── ButtonGroup.stories.tsx │ │ │ └── ButtonGroup.tsx │ │ ├── calendar/ │ │ │ ├── Calendar.stories.tsx │ │ │ ├── Calendar.tsx │ │ │ ├── DatePicker.stories.tsx │ │ │ ├── DatePicker.tsx │ │ │ ├── Week.tsx │ │ │ └── Weeks.tsx │ │ ├── cardNavigation/ │ │ │ ├── CardNavigation.stories.tsx │ │ │ └── CardNavigation.tsx │ │ ├── combobox/ │ │ │ ├── ApiComboBox.stories.tsx │ │ │ ├── ApiComboBox.tsx │ │ │ ├── ApiMultiComboBox.stories.tsx │ │ │ ├── ApiMultiComboBox.tsx │ │ │ ├── CreateNewItem.tsx │ │ │ ├── ItemLabel.tsx │ │ │ ├── LoadMoreItem.tsx │ │ │ ├── MultiComboBox.stories.tsx │ │ │ ├── MultiComboBox.tsx │ │ │ ├── MultiComboBoxField.tsx │ │ │ ├── SingleComboBox.stories.tsx │ │ │ ├── SingleComboBox.tsx │ │ │ └── hooks/ │ │ │ ├── apiComboBox.ts │ │ │ ├── apiMultiComboBox.ts │ │ │ ├── index.ts │ │ │ └── singleComboBox.ts │ │ ├── descriptionList/ │ │ │ ├── DescriptionList.stories.tsx │ │ │ └── DescriptionList.tsx │ │ ├── dialogs/ │ │ │ ├── GuideDialog.stories.tsx │ │ │ ├── GuideDialog.tsx │ │ │ ├── MessageDialog.stories.tsx │ │ │ ├── MessageDialog.tsx │ │ │ ├── MessageDialogConfirm.stories.tsx │ │ │ ├── MessageDialogConfirm.tsx │ │ │ ├── TaskDialog.stories.tsx │ │ │ ├── TaskDialog.tsx │ │ │ ├── ToggleDialog.tsx │ │ │ └── parts/ │ │ │ ├── DialogFooter.stories.tsx │ │ │ ├── DialogFooter.tsx │ │ │ ├── GuideStepCount.tsx │ │ │ └── walkthroughImage.ts │ │ ├── dropdown/ │ │ │ ├── Dropdown.stories.tsx │ │ │ ├── Dropdown.tsx │ │ │ ├── DropdownMenuContent.stories.tsx │ │ │ ├── DropdownMenuContent.tsx │ │ │ ├── Item.tsx │ │ │ └── types.ts │ │ ├── dropdownButton/ │ │ │ ├── DropdownButton.stories.tsx │ │ │ └── DropdownButton.tsx │ │ ├── emptyStates/ │ │ │ ├── NoDataCreated.stories.tsx │ │ │ ├── NoDataCreated.tsx │ │ │ ├── NoSearchResults.stories.tsx │ │ │ └── NoSearchResults.tsx │ │ ├── fileUploader/ │ │ │ ├── FileDropArea.stories.tsx │ │ │ ├── FileDropArea.tsx │ │ │ ├── FileUploader.stories.tsx │ │ │ ├── FileUploader.tsx │ │ │ ├── hooks.ts │ │ │ └── types.ts │ │ ├── filterTag/ │ │ │ ├── FilterTag.stories.tsx │ │ │ └── FilterTag.tsx │ │ ├── filterableDropdownButton/ │ │ │ ├── FilterableDropdownButton.stories.tsx │ │ │ └── FilterableDropdownButton.tsx │ │ ├── footer/ │ │ │ ├── Footer.stories.tsx │ │ │ └── Footer.tsx │ │ ├── formBlock/ │ │ │ ├── DateField.stories.tsx │ │ │ ├── DateField.tsx │ │ │ ├── FormActions.stories.tsx │ │ │ ├── FormActions.tsx │ │ │ ├── NameField.stories.tsx │ │ │ ├── NameField.tsx │ │ │ ├── PhoneNumberField.stories.tsx │ │ │ └── PhoneNumberField.tsx │ │ ├── formControl/ │ │ │ ├── FormControl.stories.tsx │ │ │ ├── FormControl.tsx │ │ │ ├── FormControlGroup.stories.tsx │ │ │ └── FormControlGroup.tsx │ │ ├── formFields/ │ │ │ ├── AmountRangeField.stories.tsx │ │ │ ├── AmountRangeField.tsx │ │ │ ├── DateDurationField.stories.tsx │ │ │ ├── DateDurationField.tsx │ │ │ ├── DateInput.stories.tsx │ │ │ ├── DateInput.tsx │ │ │ ├── DecimalInput.stories.tsx │ │ │ ├── DecimalInput.tsx │ │ │ ├── DigitsInput.stories.tsx │ │ │ ├── DigitsInput.tsx │ │ │ ├── FormattedTextField.stories.tsx │ │ │ ├── FormattedTextField.tsx │ │ │ ├── NumeralCodeInput.stories.tsx │ │ │ ├── NumeralCodeInput.tsx │ │ │ ├── PasswordField.stories.tsx │ │ │ ├── PasswordField.tsx │ │ │ ├── TimeInput.stories.tsx │ │ │ ├── TimeInput.tsx │ │ │ ├── TimeLengthInput.stories.tsx │ │ │ └── TimeLengthInput.tsx │ │ ├── globalNavi/ │ │ │ ├── GlobalNavi.stories.tsx │ │ │ └── GlobalNavi.tsx │ │ ├── guidanceMessage/ │ │ │ ├── GuidanceMessage.stories.tsx │ │ │ └── GuidanceMessage.tsx │ │ ├── guidedContent/ │ │ │ ├── GuidedContent.stories.tsx │ │ │ └── GuidedContent.tsx │ │ ├── header/ │ │ │ ├── Header.stories.tsx │ │ │ ├── Header.tsx │ │ │ ├── HeaderSectionContent.tsx │ │ │ └── types.ts │ │ ├── headlineArea/ │ │ │ ├── HeadlineArea.stories.tsx │ │ │ └── HeadlineArea.tsx │ │ ├── hierarchicalTable/ │ │ │ ├── HierarchicalTable.stories.tsx │ │ │ ├── HierarchicalTable.tsx │ │ │ ├── HierarchicalTableRowHeaderCell.tsx │ │ │ └── hooks/ │ │ │ └── useHierarchicalTable.ts │ │ ├── index.ts │ │ ├── indexSearchField/ │ │ │ ├── IndexSearchField.stories.tsx │ │ │ └── IndexSearchField.tsx │ │ ├── lineSeparatedList/ │ │ │ ├── LineSeparatedList.stories.tsx │ │ │ └── LineSeparatedList.tsx │ │ ├── listButtonSelector/ │ │ │ ├── ListButtonSelector.stories.tsx │ │ │ └── ListButtonSelector.tsx │ │ ├── listButtons/ │ │ │ ├── ListButtons.stories.tsx │ │ │ └── ListButtons.tsx │ │ ├── listCard/ │ │ │ ├── ListCard.stories.tsx │ │ │ └── ListCard.tsx │ │ ├── listTable/ │ │ │ ├── GroupedListTable.stories.tsx │ │ │ ├── GroupedListTable.tsx │ │ │ ├── ListTable.stories.tsx │ │ │ └── ListTable.tsx │ │ ├── messageBlock/ │ │ │ ├── FloatingMessageBlock.stories.tsx │ │ │ ├── FloatingMessageBlock.tsx │ │ │ ├── MessageBlock.stories.tsx │ │ │ └── MessageBlock.tsx │ │ ├── messageIcon/ │ │ │ ├── MessageIcon.stories.tsx │ │ │ └── MessageIcon.tsx │ │ ├── modals/ │ │ │ ├── FullScreenModal.stories.tsx │ │ │ └── FullScreenModal.tsx │ │ ├── numericTable/ │ │ │ ├── NumericTable.stories.tsx │ │ │ └── NumericTable.tsx │ │ ├── pageSelector/ │ │ │ ├── PageSelector.stories.tsx │ │ │ └── PageSelector.tsx │ │ ├── pager/ │ │ │ ├── Pager.stories.tsx │ │ │ ├── Pager.tsx │ │ │ ├── PagerBreak.tsx │ │ │ ├── Pagination.stories.tsx │ │ │ └── Pagination.tsx │ │ ├── personTag/ │ │ │ ├── PersonTag.stories.tsx │ │ │ └── PersonTag.tsx │ │ ├── popupProgressBar/ │ │ │ ├── PopupProgressBar.stories.tsx │ │ │ └── PopupProgressBar.tsx │ │ ├── popupProgressBarPortal/ │ │ │ ├── PopupProgressBarPortal.stories.tsx │ │ │ └── PopupProgressBarPortal.tsx │ │ ├── propsListForm/ │ │ │ ├── PropsListForm.stories.tsx │ │ │ └── PropsListForm.tsx │ │ ├── scrimCoveredContent/ │ │ │ ├── ScrimCoveredContent.stories.tsx │ │ │ └── ScrimCoveredContent.tsx │ │ ├── selectableButton/ │ │ │ ├── SelectableButton.stories.tsx │ │ │ └── SelectableButton.tsx │ │ ├── skeleton/ │ │ │ ├── SkeletonBlock.stories.tsx │ │ │ ├── SkeletonBlock.tsx │ │ │ ├── SkeletonCircle.stories.tsx │ │ │ ├── SkeletonCircle.tsx │ │ │ ├── SkeletonDescriptionList.stories.tsx │ │ │ ├── SkeletonDescriptionList.tsx │ │ │ ├── SkeletonIcon.stories.tsx │ │ │ ├── SkeletonIcon.tsx │ │ │ ├── SkeletonInput.stories.tsx │ │ │ ├── SkeletonInput.tsx │ │ │ ├── SkeletonListTable.stories.tsx │ │ │ ├── SkeletonListTable.tsx │ │ │ ├── SkeletonPageTitle.stories.tsx │ │ │ ├── SkeletonPageTitle.tsx │ │ │ ├── SkeletonParagraph.stories.tsx │ │ │ ├── SkeletonParagraph.tsx │ │ │ ├── SkeletonRectangle.stories.tsx │ │ │ ├── SkeletonRectangle.tsx │ │ │ ├── SkeletonSectionTitle.stories.tsx │ │ │ ├── SkeletonSectionTitle.tsx │ │ │ ├── SkeletonStackedBarChart.stories.tsx │ │ │ └── SkeletonStackedBarChart.tsx │ │ ├── stackedBarChart/ │ │ │ ├── StackedBarChart.stories.tsx │ │ │ └── StackedBarChart.tsx │ │ ├── statusSelector/ │ │ │ ├── StatusSelector.stories.tsx │ │ │ └── StatusSelector.tsx │ │ ├── stepper/ │ │ │ ├── Stepper.stories.tsx │ │ │ ├── Stepper.tsx │ │ │ ├── VerticalSteps.stories.tsx │ │ │ └── VerticalSteps.tsx │ │ ├── tabBar/ │ │ │ ├── TabBar.stories.tsx │ │ │ └── TabBar.tsx │ │ ├── tagBox/ │ │ │ ├── MiniTag.stories.tsx │ │ │ ├── MiniTag.tsx │ │ │ ├── TagBox.stories.tsx │ │ │ └── TagBox.tsx │ │ ├── withAccordionContent/ │ │ │ ├── WithAccordionContent.stories.tsx │ │ │ └── WithAccordionContent.tsx │ │ ├── withBalloon/ │ │ │ ├── WithBalloon.stories.tsx │ │ │ ├── WithBalloon.tsx │ │ │ └── useBalloon.ts │ │ ├── withDropdown/ │ │ │ ├── WithDropdown.stories.tsx │ │ │ └── WithDropdown.tsx │ │ ├── withFilterableDropdown/ │ │ │ ├── WithFilterableDropdown.stories.tsx │ │ │ └── WithFilterableDropdown.tsx │ │ ├── withPopup/ │ │ │ ├── WithPopup.stories.tsx │ │ │ └── WithPopup.tsx │ │ └── withTOC/ │ │ ├── WithTOC.stories.tsx │ │ └── WithTOC.tsx │ └── utilities/ │ ├── AriaProps.ts │ ├── Ascii.test.ts │ ├── Ascii.ts │ ├── DOMUtil.ts │ ├── Dialog.tsx │ ├── Digits.test.ts │ ├── Digits.ts │ ├── FixedPortal.tsx │ ├── FocusableEelements.ts │ ├── Mins.ts │ ├── ScrollPortal.tsx │ ├── TimeString.ts │ ├── VibesContext.ts │ ├── VibesProvider.tsx │ ├── browsers.ts │ ├── commonProps.test.ts │ ├── commonProps.ts │ ├── convertRemToPixel.ts │ ├── date.test.ts │ ├── date.ts │ ├── functionalMarginClasses.test.ts │ ├── functionalMarginClasses.ts │ ├── index.ts │ ├── keyboard.ts │ ├── marginClasses.ts │ ├── selfWindowNavigator.ts │ ├── useMedia.ts │ └── vbClassNames.ts ├── stories/ │ ├── commonKnobs.ts │ └── index.ts ├── stylelint.config.js ├── stylesheets/ │ ├── _container_query.scss │ ├── _lv1.scss │ ├── _lv2.scss │ ├── freee.scss │ ├── lv0/ │ │ ├── _colors.scss │ │ ├── _focus.scss │ │ ├── _fonts.scss │ │ └── _size.scss │ ├── lv1/ │ │ ├── InlineSpinner.scss │ │ ├── Loading.scss │ │ ├── base.scss │ │ ├── button.scss │ │ ├── calendar.scss │ │ ├── content.scss │ │ ├── focusHighlight.scss │ │ ├── focusTrap.scss │ │ ├── form.scss │ │ ├── grid.scss │ │ ├── headline.scss │ │ ├── icon.scss │ │ ├── image.scss │ │ ├── interactive-parts.scss │ │ ├── layout.scss │ │ ├── list.scss │ │ ├── margin-option.scss │ │ ├── message.scss │ │ └── progress.scss │ ├── lv2/ │ │ ├── accordionPanel.scss │ │ ├── breadcrumbs.scss │ │ ├── bulletedList.scss │ │ ├── buttonGroup.scss │ │ ├── calendar.scss │ │ ├── cardNavigation.scss │ │ ├── comboBox.scss │ │ ├── dateInput.scss │ │ ├── descriptionList.scss │ │ ├── dialog.scss │ │ ├── dialogFooter.scss │ │ ├── dropdown.scss │ │ ├── dropdownButton.scss │ │ ├── emptyStates.scss │ │ ├── fileDropArea.scss │ │ ├── fileUploader.scss │ │ ├── filterTag.scss │ │ ├── footer.scss │ │ ├── formBlock.scss │ │ ├── formField.scss │ │ ├── formGroup.scss │ │ ├── globalNavi.scss │ │ ├── guidanceMessage.scss │ │ ├── guidedContent.scss │ │ ├── header.scss │ │ ├── headlineArea.scss │ │ ├── hierarchicalTable.scss │ │ ├── indexSearchField.scss │ │ ├── lineSeparatedList.scss │ │ ├── listButtonSelector.scss │ │ ├── listCard.scss │ │ ├── listTable.scss │ │ ├── messageBlock.scss │ │ ├── messageIcon.scss │ │ ├── modal.scss │ │ ├── numericTable.scss │ │ ├── pageSelector.scss │ │ ├── pager.scss │ │ ├── personTag.scss │ │ ├── popupProgressBar.scss │ │ ├── popupProgressBarPortal.scss │ │ ├── propListForm.scss │ │ ├── selectableButton.scss │ │ ├── skeleton.scss │ │ ├── stackedBarChart.scss │ │ ├── statusSelector.scss │ │ ├── stepper.scss │ │ ├── tabbar.scss │ │ ├── tagBox.scss │ │ ├── withAccordionContent.scss │ │ ├── withBalloon.scss │ │ ├── withFilterableDropdown.scss │ │ ├── withPopup.scss │ │ └── withTOC.scss │ └── vibes_2021.scss ├── tsconfig.build.json ├── tsconfig.json ├── utilities.js.flow └── vibes_2021.css
SYMBOL INDEX (441 symbols across 238 files)
FILE: __tests__/types/flow/lv2.js
method [
{
label: <div />,
selected: true,
href: 'test',
target: '_blank',
rel: 'test',
statusIconText: 'test',
statusIconType: 'success',
bgTransparent: true,
onClick: (_e: SyntheticEvent<HTMLButtonElement>) => {},
},
] (line 998) | [
FILE: examples/Collection.stories.tsx
type ListElm (line 40) | type ListElm = {
FILE: examples/ThroughCommonProps.stories.tsx
type InnerProps (line 11) | type InnerProps = CommonProps & { children?: React.ReactNode };
FILE: src/hooks/useUniqueId.ts
function createUniqueId (line 4) | function createUniqueId(base: string): string {
function useUniqueId (line 8) | function useUniqueId(
FILE: src/lv1/InlineSpinner.tsx
type Props (line 9) | type Props = {
FILE: src/lv1/Loading.stories.tsx
constant LOREM (line 10) | const LOREM =
FILE: src/lv1/Loading.tsx
type Props (line 11) | type Props = (
FILE: src/lv1/a11y/FocusHighlight.tsx
type Props (line 4) | type Props = {
FILE: src/lv1/a11y/FocusTrap.tsx
type Props (line 5) | type Props = {
FILE: src/lv1/a11y/VisuallyHidden.stories.tsx
type Props (line 26) | type Props = Record<string, never>;
type State (line 27) | type State = {
class TestCounter (line 32) | class TestCounter extends React.Component<Props, State> {
method constructor (line 34) | constructor(props: Props) {
method handleCountUp (line 39) | handleCountUp() {
method render (line 45) | render() {
FILE: src/lv1/a11y/VisuallyHidden.tsx
type Props (line 6) | type Props = {
FILE: src/lv1/bases/Balloon.tsx
type Props (line 5) | type Props = {
FILE: src/lv1/bases/CardBase.tsx
type Props (line 11) | type Props = {
FILE: src/lv1/bases/ColumnBase.tsx
type Props (line 7) | type Props = {
FILE: src/lv1/bases/Container.tsx
type Props (line 6) | type Props = {
FILE: src/lv1/bases/ContentsBase.tsx
type Props (line 6) | type Props = {
FILE: src/lv1/bases/DialogBase.tsx
type Props (line 6) | type Props = {
FILE: src/lv1/bases/FloatingBase.tsx
type Props (line 6) | type Props = {
FILE: src/lv1/bases/MarginBase.tsx
type Props (line 8) | type Props = {
FILE: src/lv1/bases/NegativeContentsBase.tsx
type Props (line 5) | type Props = {
FILE: src/lv1/bases/NegativeMarginBase.tsx
type Props (line 4) | type Props = {
FILE: src/lv1/bases/PopupBase.tsx
type Props (line 6) | type Props = {
FILE: src/lv1/bases/ScrimBase.tsx
type Props (line 5) | type Props = {
FILE: src/lv1/bases/ScrollableBase.tsx
type Props (line 4) | type Props = {
FILE: src/lv1/bases/ZebraBase.tsx
type Props (line 7) | type Props = {
FILE: src/lv1/bases/types.ts
type BaseComponentBorderProps (line 1) | type BaseComponentBorderProps = {
type BaseComponentPaddingProps (line 5) | type BaseComponentPaddingProps = {
type FitContentProps (line 13) | type FitContentProps = {
type BaseComponentProps (line 20) | type BaseComponentProps = BaseComponentPaddingProps &
FILE: src/lv1/buttons/BackwardButton.tsx
type Props (line 8) | type Props = {
FILE: src/lv1/buttons/Button.stories.tsx
type Story (line 12) | type Story = StoryObj<typeof Button>;
FILE: src/lv1/buttons/Button.tsx
type ButtonAppearanceType (line 14) | type ButtonAppearanceType = 'primary' | 'secondary' | 'tertiary';
type Props (line 16) | type Props = {
function ButtonInner (line 107) | function ButtonInner(
FILE: src/lv1/buttons/GlobalNaviButton.tsx
type Props (line 7) | type Props = {
FILE: src/lv1/buttons/IconOnlyBackwardButton.tsx
type Props (line 6) | type Props = {
FILE: src/lv1/buttons/IconOnlyButton.tsx
type Props (line 15) | type Props = {
function IconOnlyButtonInner (line 99) | function IconOnlyButtonInner(
FILE: src/lv1/buttons/IconOnlyJumpButton.tsx
type Props (line 6) | type Props = {
function IconOnlyJumpButton (line 42) | function IconOnlyJumpButton(props: Props): React.ReactElement {
FILE: src/lv1/buttons/InlineLink.tsx
type Props (line 14) | type Props = {
FILE: src/lv1/buttons/JumpButton.tsx
type Props (line 8) | type Props = {
FILE: src/lv1/buttons/LeftIconButton.tsx
type Props (line 6) | type Props = {
function LeftIconButton (line 22) | function LeftIconButton(props: Props): React.ReactElement {
FILE: src/lv1/buttons/ListButton.tsx
type Props (line 7) | type Props = {
FILE: src/lv1/buttons/PagerButton.tsx
type Props (line 5) | type Props = {
function PagerButton (line 30) | function PagerButton(props: Props): React.ReactElement {
FILE: src/lv1/buttons/RightIconButton.tsx
type Props (line 7) | type Props = {
function RightIconButton (line 25) | function RightIconButton(props: Props): React.ReactElement {
FILE: src/lv1/buttons/TabButton.tsx
type Props (line 5) | type Props = {
function TabButton (line 18) | function TabButton(props: Props): React.ReactElement {
FILE: src/lv1/buttons/TextButton.tsx
type Props (line 12) | type Props = {
FILE: src/lv1/calendar/CalendarDate.tsx
type TimeRecord (line 7) | type TimeRecord = {
type Props (line 16) | type Props = {
function CalendarDate (line 67) | function CalendarDate(props: Props): React.ReactElement {
FILE: src/lv1/calendar/CalendarHead.tsx
type Props (line 4) | type Props = {
function CalendarHead (line 11) | function CalendarHead(props: Props): React.ReactElement {
FILE: src/lv1/forms/CheckBox.tsx
type Props (line 7) | type Props = {
FILE: src/lv1/forms/FormControlLabel.tsx
type Props (line 6) | type Props = {
FILE: src/lv1/forms/NumeralField.tsx
type Props (line 5) | type Props = Omit<
FILE: src/lv1/forms/OptionButton.tsx
type Props (line 5) | type Props = {
FILE: src/lv1/forms/RadioButton.tsx
type Props (line 7) | type Props = {
FILE: src/lv1/forms/ReadOnlyField.tsx
type Props (line 5) | type Props = {
FILE: src/lv1/forms/SearchField.tsx
type Props (line 9) | type Props = {
FILE: src/lv1/forms/SelectBox.tsx
type SelectBoxOption (line 7) | type SelectBoxOption = {
type SelectBoxOptionGroup (line 15) | type SelectBoxOptionGroup = {
type Props (line 24) | type Props = {
FILE: src/lv1/forms/TextArea.tsx
type Props (line 7) | type Props = {
FILE: src/lv1/forms/TextField.tsx
type TextFieldType (line 20) | type TextFieldType = 'text' | 'email' | 'password' | 'number' | 'tel';
type TextFieldWidth (line 21) | type TextFieldWidth = 'xSmall' | 'small' | 'medium' | 'large' | 'full';
type NumberInputProps (line 23) | type NumberInputProps = {
type Props (line 34) | type Props = {
function filterNumberInputProps (line 198) | function filterNumberInputProps(props: Props): NumberInputProps {
function TextFieldInner (line 210) | function TextFieldInner(
FILE: src/lv1/forms/ToggleButton.tsx
type Props (line 6) | type Props = {
FILE: src/lv1/forms/types.ts
type FormHandlers (line 3) | type FormHandlers<T = Element> = {
type AutocompleteAttribute (line 12) | type AutocompleteAttribute =
FILE: src/lv1/grids/GridBlock.tsx
type GridSize (line 4) | type GridSize =
type Props (line 10) | type Props = {
function GridBlock (line 15) | function GridBlock(props: Props): React.ReactElement {
FILE: src/lv1/grids/GridWrapper.tsx
type Props (line 5) | type Props = {
function GridWrapper (line 10) | function GridWrapper(props: Props): React.ReactElement {
FILE: src/lv1/icons/Avatar.tsx
type Props (line 4) | type Props = {
FILE: src/lv1/icons/MaterialIcon.tsx
type Props (line 5) | type Props = {
FILE: src/lv1/icons/RequiredIcon.tsx
type Props (line 5) | type Props = CommonProps;
FILE: src/lv1/icons/StatusIcon.tsx
type StatusType (line 5) | type StatusType =
type Props (line 14) | type Props = {
FILE: src/lv1/images/AlertSwallow.tsx
type Props (line 4) | type Props = SwallowProps;
FILE: src/lv1/images/AppStoreBadge.tsx
type Props (line 6) | type Props = MarginClassProps & CommonProps;
FILE: src/lv1/images/CloudSkeletonIllust.tsx
type Props (line 5) | type Props = MarginClassProps & CommonProps;
FILE: src/lv1/images/CloudUploadIllust.tsx
type Props (line 5) | type Props = MarginClassProps & CommonProps;
FILE: src/lv1/images/CsvUploadIllust.tsx
type Props (line 5) | type Props = MarginClassProps & CommonProps;
FILE: src/lv1/images/DiscoveryIllust.tsx
type Props (line 7) | type Props = {
FILE: src/lv1/images/FileUploadIllust.tsx
type Props (line 5) | type Props = MarginClassProps & CommonProps;
FILE: src/lv1/images/FinishTaskIllust.tsx
type Props (line 7) | type Props = CommonProps;
FILE: src/lv1/images/GooglePlayBadge.tsx
type Props (line 6) | type Props = MarginClassProps & CommonProps;
FILE: src/lv1/images/ImageUploadIllust.tsx
type Props (line 5) | type Props = MarginClassProps & CommonProps;
FILE: src/lv1/images/NoDataIllust.tsx
type Props (line 4) | type Props = SwallowProps;
FILE: src/lv1/images/NoSearchResultsIllust.tsx
type Props (line 4) | type Props = SwallowProps;
FILE: src/lv1/images/NotFoundSwallow.tsx
type Props (line 4) | type Props = SwallowProps;
FILE: src/lv1/images/SwallowContainer.tsx
type SwallowProps (line 5) | type SwallowProps = {
type Props (line 13) | type Props = SwallowProps & {
FILE: src/lv1/interactiveParts/SegmentControlButton.tsx
type Props (line 4) | type Props = {
FILE: src/lv1/interactiveParts/StepBlock.tsx
type Props (line 6) | type Props = {
FILE: src/lv1/interactiveParts/StepBorder.tsx
type Props (line 4) | type Props = {
FILE: src/lv1/interactiveParts/StepNumber.tsx
type Props (line 5) | type Props = {
FILE: src/lv1/interactiveParts/Tab.tsx
type Props (line 5) | type Props = {
FILE: src/lv1/layout/HStack.tsx
type StackProps (line 4) | type StackProps = React.ComponentProps<typeof Stack>;
type Props (line 5) | type Props = {
FILE: src/lv1/layout/Stack.tsx
type Props (line 5) | type Props = {
FILE: src/lv1/layout/VStack.tsx
type StackProps (line 4) | type StackProps = React.ComponentProps<typeof Stack>;
type Props (line 5) | type Props = {
FILE: src/lv1/layout/WithDescriptionContent.tsx
type Props (line 5) | type Props = {
FILE: src/lv1/layout/WithSideContent.tsx
type Props (line 4) | type Props = {
FILE: src/lv1/lists/BorderTableListCell.tsx
type Props (line 6) | type Props = {
FILE: src/lv1/lists/CheckBoxCell.tsx
type Props (line 4) | type Props = {
FILE: src/lv1/lists/DescriptionListCell.tsx
type Props (line 5) | type Props = {
FILE: src/lv1/lists/DescriptionListHeadCell.tsx
type Props (line 5) | type Props = {
FILE: src/lv1/lists/TableListCell.stories.tsx
type WrapperProps (line 18) | type WrapperProps = { children: React.ReactNode; style?: React.CSSProper...
FILE: src/lv1/lists/TableListCell.tsx
type Props (line 9) | type Props = {
FILE: src/lv1/lists/TableListHead.tsx
type Props (line 4) | type Props = {
FILE: src/lv1/lists/TableListHeadCell.tsx
type Order (line 6) | type Order = 'asc' | 'desc' | 'init';
type Props (line 7) | type Props = {
FILE: src/lv1/lists/TableListRow.tsx
type Props (line 12) | type Props = {
FILE: src/lv1/lists/TreeFoldingButtonCell.tsx
type Props (line 5) | type Props = {
FILE: src/lv1/messages/Message.tsx
type MessageTypes (line 9) | type MessageTypes = {
type Props (line 28) | type Props = {
function Message (line 34) | function Message(props: Props): React.ReactElement {
FILE: src/lv1/progress/ProgressBar.tsx
type Props (line 4) | type Props = {
FILE: src/lv1/skeleton/SkeletonBase.tsx
type Props (line 5) | type Props = {
FILE: src/lv1/typography/InternalHeadline.tsx
type HeadlineProps (line 6) | type HeadlineProps = {
function InternalHeadline (line 19) | function InternalHeadline(
FILE: src/lv1/typography/Note.tsx
type Props (line 8) | type Props = {
function Note (line 19) | function Note(props: Props): React.ReactElement {
FILE: src/lv1/typography/PageTitle.tsx
type Props (line 14) | type Props = {
FILE: src/lv1/typography/Paragraph.tsx
type Props (line 6) | type Props = {
function Paragraph (line 12) | function Paragraph(props: Props): React.ReactElement {
FILE: src/lv1/typography/SectionTitle.tsx
type Props (line 14) | type Props = {
FILE: src/lv1/typography/SubSectionTitle.tsx
type Props (line 14) | type Props = {
FILE: src/lv1/typography/Text.tsx
type FontColor (line 14) | type FontColor =
type FontSize (line 27) | type FontSize = 0.75 | 0.875 | 1 | 1.5;
type FontWeight (line 28) | type FontWeight = 'normal' | 'bold';
type TextProps (line 30) | type TextProps = {
type Props (line 50) | type Props = {
FILE: src/lv1/typography/TypographyStyle.ts
type TypographyStyleProps (line 9) | type TypographyStyleProps = {
type HeadlineStyleProps (line 62) | type HeadlineStyleProps = TypographyStyleProps & {
FILE: src/lv2/accordionPanel/AccordionPanel.stories.tsx
type Story (line 12) | type Story = StoryObj<typeof AccordionPanel>;
FILE: src/lv2/accordionPanel/AccordionPanel.tsx
type Props (line 11) | type Props = {
FILE: src/lv2/basicTable/BasicTable.tsx
type Props (line 4) | type Props = Parameters<typeof ListTable>[0];
FILE: src/lv2/breadcrumbs/Breadcrumbs.stories.tsx
type Story (line 9) | type Story = StoryObj<typeof Breadcrumbs>;
FILE: src/lv2/breadcrumbs/Breadcrumbs.tsx
type Props (line 9) | type Props = {
FILE: src/lv2/bulletedList/BulletedList.tsx
type BulletedListContent (line 7) | type BulletedListContent = {
type Props (line 17) | type Props = {
FILE: src/lv2/buttonGroup/ButtonGroup.tsx
type Props (line 6) | type Props = {
FILE: src/lv2/calendar/Calendar.tsx
type Props (line 9) | type Props = {
class Calendar (line 18) | class Calendar extends React.Component<Props> {
method render (line 20) | render(): React.ReactElement {
FILE: src/lv2/calendar/DatePicker.tsx
type Props (line 23) | type Props = {
function DatePicker (line 30) | function DatePicker(
FILE: src/lv2/calendar/Week.tsx
type Props (line 14) | type Props = {
function selectDateLabel (line 29) | function selectDateLabel(
function Week (line 47) | function Week({
FILE: src/lv2/calendar/Weeks.tsx
type Props (line 6) | type Props = {
function Weeks (line 20) | function Weeks({
FILE: src/lv2/cardNavigation/CardNavigation.tsx
type Props (line 9) | type Props = {
FILE: src/lv2/combobox/ApiComboBox.stories.tsx
type Item (line 15) | type Item = {
method fetch (line 26) | async fetch({ name, page }: { name: string; page: number }) {
FILE: src/lv2/combobox/ApiComboBox.tsx
type PropsFromTextField (line 36) | type PropsFromTextField = Pick<
type Props (line 54) | type Props = {
function ApiComboBoxInner (line 92) | function ApiComboBoxInner(
FILE: src/lv2/combobox/ApiMultiComboBox.stories.tsx
type Item (line 14) | type Item = {
method fetch (line 25) | async fetch({ name, page }: { name: string; page: number }) {
FILE: src/lv2/combobox/ApiMultiComboBox.tsx
type Props (line 27) | type Props = {
function ApiMultiComboBoxInner (line 60) | function ApiMultiComboBoxInner(
FILE: src/lv2/combobox/CreateNewItem.tsx
type Props (line 5) | type Props = {
FILE: src/lv2/combobox/LoadMoreItem.tsx
type Props (line 4) | type Props = {
FILE: src/lv2/combobox/MultiComboBox.tsx
type Props (line 21) | type Props = {
FILE: src/lv2/combobox/MultiComboBoxField.tsx
type PropsFromTextField (line 8) | type PropsFromTextField = Pick<
type WithBorderProps (line 29) | type WithBorderProps = {
type Props (line 47) | type Props = {
FILE: src/lv2/combobox/SingleComboBox.tsx
type PropsFromTextField (line 32) | type PropsFromTextField = Pick<
type Props (line 52) | type Props = {
function SingleComboBoxInner (line 73) | function SingleComboBoxInner(
FILE: src/lv2/combobox/hooks/index.ts
type ComboBoxOption (line 8) | type ComboBoxOption = {
type MultiComboBoxOption (line 15) | type MultiComboBoxOption<E = never> = ComboBoxOption & {
type SingleComboBoxOption (line 20) | type SingleComboBoxOption<E = never> = ComboBoxOption & {
type InternalComboBoxOption (line 25) | type InternalComboBoxOption = ComboBoxOption & {
type FixedItem (line 30) | type FixedItem = {
type FixedItems (line 41) | type FixedItems = [FixedItem] | [FixedItem, FixedItem];
type FetchParams (line 43) | type FetchParams = {
type ApiMetaData (line 48) | type ApiMetaData = {
FILE: src/lv2/combobox/hooks/singleComboBox.ts
type TrailingItem (line 11) | type TrailingItem = {
FILE: src/lv2/descriptionList/DescriptionList.tsx
type Props (line 9) | type Props = {
FILE: src/lv2/dialogs/GuideDialog.tsx
type Props (line 12) | type Props = {
FILE: src/lv2/dialogs/MessageDialog.tsx
type Props (line 9) | type Props = {
FILE: src/lv2/dialogs/MessageDialogConfirm.tsx
type Props (line 10) | type Props = {
FILE: src/lv2/dialogs/TaskDialog.tsx
type Props (line 11) | type Props = {
FILE: src/lv2/dialogs/ToggleDialog.tsx
type ToggleDialogProps (line 4) | type ToggleDialogProps = {
FILE: src/lv2/dialogs/parts/DialogFooter.tsx
type Props (line 6) | type Props = {
FILE: src/lv2/dialogs/parts/GuideStepCount.tsx
type Props (line 4) | type Props = {
FILE: src/lv2/dropdown/Dropdown.tsx
type Props (line 7) | type Props = Pick<
FILE: src/lv2/dropdown/DropdownMenuContent.tsx
type Props (line 7) | type Props = {
FILE: src/lv2/dropdown/Item.tsx
type Props (line 9) | type Props = {
FILE: src/lv2/dropdown/types.ts
type DropdownContentSelectable (line 5) | type DropdownContentSelectable = {
type DropdownContentCheckbox (line 20) | type DropdownContentCheckbox = {
type DropdownContentTextOnly (line 30) | type DropdownContentTextOnly = {
type DropdownContent (line 36) | type DropdownContent =
FILE: src/lv2/dropdownButton/DropdownButton.tsx
type Props (line 14) | type Props = {
FILE: src/lv2/emptyStates/NoDataCreated.tsx
type Props (line 8) | type Props = {
FILE: src/lv2/emptyStates/NoSearchResults.tsx
type Props (line 8) | type Props = {
FILE: src/lv2/fileUploader/FileDropArea.tsx
type Props (line 12) | type Props = {
FILE: src/lv2/fileUploader/FileUploader.tsx
type Props (line 28) | type Props = {
type InnerProps (line 72) | type InnerProps = Omit<Props, 'isUploading'> & {
FILE: src/lv2/fileUploader/types.ts
type FileType (line 18) | type FileType = typeof FileTypes[keyof typeof FileTypes];
type FileUploaderStatus (line 27) | type FileUploaderStatus =
type FileStatus (line 31) | type FileStatus = FileUploaderStatus;
FILE: src/lv2/filterTag/FilterTag.tsx
type RenderPopupT (line 6) | type RenderPopupT = Parameters<typeof WithPopup>[0]['renderPopup'];
type Props (line 8) | type Props = {
FILE: src/lv2/filterableDropdownButton/FilterableDropdownButton.tsx
type PropsFromWithFilterableDropdown (line 8) | type PropsFromWithFilterableDropdown = Omit<
type PropsFromButton (line 13) | type PropsFromButton = Pick<
type Props (line 17) | type Props = PropsFromWithFilterableDropdown &
FILE: src/lv2/footer/Footer.tsx
type Link (line 6) | type Link = {
type Props (line 10) | type Props = {
function createLinks (line 39) | function createLinks(links?: Link[]): React.ReactNode {
FILE: src/lv2/formBlock/DateField.tsx
type DateType (line 19) | type DateType = 'year' | 'month' | 'day';
type Props (line 20) | type Props = {
FILE: src/lv2/formBlock/FormActions.tsx
type Props (line 9) | type Props = {
FILE: src/lv2/formBlock/NameField.tsx
type NameKey (line 7) | type NameKey = 'lastName' | 'firstName';
type Props (line 8) | type Props = {
FILE: src/lv2/formBlock/PhoneNumberField.tsx
type PhoneNumberKey (line 7) | type PhoneNumberKey = 'a' | 'b' | 'c';
type Props (line 8) | type Props = {
constant MAX_LENGTH_A (line 33) | const MAX_LENGTH_A = 5;
constant MAX_LENGTH_B (line 34) | const MAX_LENGTH_B = 4;
constant MAX_LENGTH_C (line 35) | const MAX_LENGTH_C = 4;
FILE: src/lv2/formControl/FormControl.tsx
type Props (line 8) | type Props = {
FILE: src/lv2/formControl/FormControlGroup.tsx
type Props (line 4) | type Props = {
FILE: src/lv2/formFields/AmountRangeField.tsx
type Props (line 6) | type Props = {
FILE: src/lv2/formFields/DateDurationField.tsx
type Props (line 9) | type Props = {
FILE: src/lv2/formFields/DateInput.stories.tsx
function InteractiveExample (line 136) | function InteractiveExample() {
FILE: src/lv2/formFields/DateInput.tsx
constant DATE_FORMAT (line 28) | const DATE_FORMAT = /^\d{4}[-/\s]?\d{2}[-/\s]?\d{2}$/;
type DeprecatedWidth (line 33) | type DeprecatedWidth = 'xSmall' | 'small';
type FieldWidth (line 34) | type FieldWidth = 'medium' | 'large' | 'full' | DeprecatedWidth;
type Props (line 36) | type Props = {
FILE: src/lv2/formFields/DecimalInput.tsx
type Props (line 6) | type Props = Pick<
FILE: src/lv2/formFields/DigitsInput.tsx
type PropsFromTextField (line 12) | type PropsFromTextField = Pick<
type Props (line 32) | type Props = {
FILE: src/lv2/formFields/FormattedTextField.stories.tsx
type Preset (line 64) | type Preset = 'postalCode' | 'corporateNumber';
FILE: src/lv2/formFields/FormattedTextField.tsx
type Formatter (line 4) | type Formatter = {
type Preset (line 17) | type Preset =
type Props (line 22) | type Props = (
function FormattedTextField (line 97) | function FormattedTextField({ ...props }: Props): ReactElement {
FILE: src/lv2/formFields/NumeralCodeInput.tsx
type Props (line 5) | type Props = Omit<
FILE: src/lv2/formFields/PasswordField.tsx
type Props (line 6) | type Props = {
FILE: src/lv2/formFields/TimeInput.tsx
type Props (line 6) | type Props = Pick<
FILE: src/lv2/formFields/TimeLengthInput.tsx
type Props (line 6) | type Props = Pick<
FILE: src/lv2/globalNavi/GlobalNavi.tsx
type LinkContent (line 8) | type LinkContent = {
type Props (line 18) | type Props = {
function createLinks (line 54) | function createLinks(links?: LinkContent[]): React.ReactNode[] {
function GlobalNavi (line 96) | function GlobalNavi(props: Props) {
FILE: src/lv2/guidanceMessage/GuidanceMessage.tsx
type CloseButtonProps (line 14) | type CloseButtonProps = Pick<
type Props (line 46) | type Props = {
FILE: src/lv2/guidedContent/GuidedContent.tsx
type Props (line 5) | type Props = {
FILE: src/lv2/header/Header.tsx
type Props (line 7) | type Props = {
FILE: src/lv2/header/HeaderSectionContent.tsx
type Props (line 6) | type Props = {
FILE: src/lv2/header/types.ts
type SectionData (line 3) | type SectionData = {
FILE: src/lv2/headlineArea/HeadlineArea.tsx
type Props (line 10) | type Props = {
FILE: src/lv2/hierarchicalTable/HierarchicalTable.tsx
type HierarchicalTableHeader (line 19) | type HierarchicalTableHeader = {
type HierarchicalTableRow (line 43) | type HierarchicalTableRow = {
type HierarchicalTableCell (line 48) | type HierarchicalTableCell = {
type Props (line 64) | type Props = {
function createHeader (line 77) | function createHeader(
function createCells (line 105) | function createCells(
function createRows (line 148) | function createRows(
FILE: src/lv2/hierarchicalTable/HierarchicalTableRowHeaderCell.tsx
type Props (line 5) | type Props = React.ComponentProps<typeof BorderTableListCell> & {
FILE: src/lv2/hierarchicalTable/hooks/useHierarchicalTable.ts
type HierarchicalRow (line 3) | type HierarchicalRow<Row> = unknown & { childRows: Array<Row> };
type FlatRow (line 5) | type FlatRow<Row> = Row & {
type FlatRowWithStatus (line 22) | type FlatRowWithStatus<Row> = FlatRow<Row> & {
FILE: src/lv2/indexSearchField/IndexSearchField.tsx
type Props (line 10) | type Props = {
FILE: src/lv2/lineSeparatedList/LineSeparatedList.tsx
type LineSeparatedListContent (line 5) | type LineSeparatedListContent = {
type Props (line 9) | type Props = {
FILE: src/lv2/listButtonSelector/ListButtonSelector.tsx
type ButtonProps (line 14) | type ButtonProps = {
type Props (line 26) | type Props = {
type State (line 38) | type State = {
class ListButtonSelector (line 47) | class ListButtonSelector extends React.Component<Props, State> {
method constructor (line 51) | constructor(props: Props) {
method componentDidMount (line 57) | componentDidMount(): void {
method componentWillUnmount (line 64) | componentWillUnmount(): void {
method handleKeyDownSelectableItem (line 83) | handleKeyDownSelectableItem(e: React.KeyboardEvent, itemIndex: number)...
method render (line 98) | render(): React.ReactNode {
FILE: src/lv2/listButtons/ListButtons.tsx
type ButtonProps (line 7) | type ButtonProps = {
type Props (line 19) | type Props = {
FILE: src/lv2/listCard/ListCard.tsx
type Props (line 9) | type Props = {
FILE: src/lv2/listTable/GroupedListTable.stories.tsx
type SortKeyT (line 169) | type SortKeyT = 'date' | 'amount' | 'tax';
FILE: src/lv2/listTable/GroupedListTable.tsx
type TableRowGroup (line 16) | type TableRowGroup = {
type Props (line 33) | type Props = {
FILE: src/lv2/listTable/ListTable.stories.tsx
type SortKeyT (line 565) | type SortKeyT = 'id' | 'value';
FILE: src/lv2/listTable/ListTable.tsx
type TableHeader (line 13) | type TableHeader = {
type TableRow (line 30) | type TableRow = {
type TableCell (line 48) | type TableCell = {
type Props (line 63) | type Props = {
FILE: src/lv2/messageBlock/FloatingMessageBlock.tsx
constant HIDE_TIMEOUT_SEC (line 11) | const HIDE_TIMEOUT_SEC = 6;
type FloatingMessageBlockProps (line 13) | type FloatingMessageBlockProps = (
FILE: src/lv2/messageBlock/MessageBlock.tsx
type InternalMessageProps (line 19) | type InternalMessageProps = {
type Props (line 64) | type Props = (
FILE: src/lv2/messageIcon/MessageIcon.tsx
type Props (line 13) | type Props = {
type ComponentProps (line 25) | type ComponentProps = Props & MarginClassProps & CommonProps;
FILE: src/lv2/modals/FullScreenModal.tsx
type Props (line 23) | type Props = {
FILE: src/lv2/numericTable/NumericTable.tsx
type NumericTableHeader (line 10) | type NumericTableHeader = {
type NumericTableRow (line 36) | type NumericTableRow = {
type NumericTableCell (line 46) | type NumericTableCell = {
type Props (line 66) | type Props = {
function createHeader (line 92) | function createHeader(
function createCells (line 136) | function createCells(
function createRows (line 179) | function createRows(
FILE: src/lv2/pageSelector/PageSelector.tsx
type RenderPopupT (line 13) | type RenderPopupT = Parameters<typeof WithPopup>[0]['renderPopup'];
type Props (line 14) | type Props = {
FILE: src/lv2/pager/Pager.stories.tsx
class PagerWithHandler (line 36) | class PagerWithHandler extends React.Component<
method render (line 44) | render() {
FILE: src/lv2/pager/Pager.tsx
type Props (line 9) | type Props = {
constant DEFAULT_PAGE_RANGE (line 37) | const DEFAULT_PAGE_RANGE = 5;
constant DEFAULT_SIDE_PAGE_RANGE (line 38) | const DEFAULT_SIDE_PAGE_RANGE = 1;
constant LEFT_BREAK (line 39) | const LEFT_BREAK = 'LEFT_BREAK';
constant RIGHT_BREAK (line 40) | const RIGHT_BREAK = 'RIGHT_BREAK';
function uniquePages (line 43) | function uniquePages(
function createPages (line 57) | function createPages({
FILE: src/lv2/pager/Pagination.tsx
type Props (line 8) | type Props = {
FILE: src/lv2/personTag/PersonTag.tsx
type PersonTagColor (line 9) | type PersonTagColor = 'success' | 'error';
type Props (line 11) | type Props = {
FILE: src/lv2/popupProgressBar/PopupProgressBar.tsx
type ProgressState (line 13) | type ProgressState = {
type Props (line 20) | type Props = ProgressState & { onClose: () => void } & CommonProps;
FILE: src/lv2/popupProgressBarPortal/PopupProgressBarPortal.tsx
type Props (line 9) | type Props = {
FILE: src/lv2/propsListForm/PropsListForm.stories.tsx
type Values (line 26) | type Values = typeof values;
type Values (line 222) | type Values = typeof values;
type Values (line 291) | type Values = typeof values;
type Values (line 380) | type Values = typeof values;
type Values (line 418) | type Values = typeof values;
type Values (line 480) | type Values = typeof values;
type Values (line 565) | type Values = typeof values;
type Values (line 604) | type Values = typeof values;
FILE: src/lv2/propsListForm/PropsListForm.tsx
type Values (line 15) | type Values = {
type FixedLengthBlock (line 19) | type FixedLengthBlock<T> =
type VariableLengthBlock (line 23) | type VariableLengthBlock<T> = {
type Errors (line 28) | type Errors<T extends Values> = Partial<Record<keyof T, string[]>>;
type Props (line 30) | type Props<T extends Values> = {
type PropsListFormFieldDefinition (line 69) | type PropsListFormFieldDefinition<T> = {
type OmitProps (line 85) | type OmitProps<T> = Omit<T, 'value'>;
type TextFieldProps (line 86) | type TextFieldProps = OmitProps<React.ComponentProps<typeof TextField>>;
type TextAreaProps (line 87) | type TextAreaProps = OmitProps<React.ComponentProps<typeof TextArea>>;
type NumberFieldProps (line 88) | type NumberFieldProps = OmitProps<React.ComponentProps<typeof DigitsInpu...
type DateFieldProps (line 89) | type DateFieldProps = OmitProps<React.ComponentProps<typeof DateInput>>;
type NumeralCodeInputProps (line 90) | type NumeralCodeInputProps = OmitProps<
type ReadOnlyFieldProps (line 93) | type ReadOnlyFieldProps = OmitProps<React.ComponentProps<typeof ReadOnly...
type PropsListFormFieldType (line 95) | type PropsListFormFieldType<V> =
type FieldProps (line 268) | type FieldProps = {
FILE: src/lv2/scrimCoveredContent/ScrimCoveredContent.stories.tsx
constant LOREM (line 11) | const LOREM =
FILE: src/lv2/scrimCoveredContent/ScrimCoveredContent.tsx
type Props (line 8) | type Props = {
FILE: src/lv2/selectableButton/SelectableButton.tsx
type Props (line 7) | type Props = {
FILE: src/lv2/skeleton/SkeletonBlock.tsx
type Props (line 6) | type Props = {
FILE: src/lv2/skeleton/SkeletonCircle.tsx
type Props (line 7) | type Props = {
FILE: src/lv2/skeleton/SkeletonDescriptionList.tsx
type Props (line 6) | type Props = {
FILE: src/lv2/skeleton/SkeletonIcon.tsx
type Props (line 6) | type Props = {
FILE: src/lv2/skeleton/SkeletonInput.tsx
type Props (line 7) | type Props = {
FILE: src/lv2/skeleton/SkeletonListTable.tsx
type Props (line 7) | type Props = {
FILE: src/lv2/skeleton/SkeletonParagraph.tsx
type Props (line 6) | type Props = {
FILE: src/lv2/skeleton/SkeletonRectangle.tsx
type Props (line 6) | type Props = {
FILE: src/lv2/skeleton/SkeletonStackedBarChart.tsx
type Props (line 6) | type Props = CommonProps;
FILE: src/lv2/stackedBarChart/StackedBarChart.tsx
type StackedBarChartItem (line 7) | type StackedBarChartItem = {
type Props (line 23) | type Props = {
FILE: src/lv2/statusSelector/StatusSelector.tsx
type Props (line 9) | type Props = {
FILE: src/lv2/stepper/Stepper.tsx
type Props (line 5) | type Props = {
FILE: src/lv2/stepper/VerticalSteps.tsx
type Props (line 6) | type Props = {
FILE: src/lv2/tabBar/TabBar.tsx
type TabElement (line 7) | type TabElement =
type Props (line 17) | type Props = {
FILE: src/lv2/tagBox/MiniTag.tsx
type Props (line 6) | type Props = {
FILE: src/lv2/tagBox/TagBox.tsx
type TagBoxMaxWidth (line 8) | type TagBoxMaxWidth = 'small' | 'medium' | 'large';
type TagBoxColor (line 9) | type TagBoxColor = 'success' | 'error' | AccentColor;
type AccentColor (line 10) | type AccentColor = 'RE' | 'OR' | 'YE' | 'YG' | 'GR' | 'BG' | 'PU' | 'GY';
type Props (line 12) | type Props = {
FILE: src/lv2/withAccordionContent/WithAccordionContent.tsx
type AccordionButtonProps (line 10) | type AccordionButtonProps = {
type Props (line 48) | type Props = {
FILE: src/lv2/withBalloon/WithBalloon.tsx
type Props (line 11) | type Props = Pick<Parameters<typeof Balloon>[0], 'border'> & {
FILE: src/lv2/withBalloon/useBalloon.ts
constant BALLOON_SHOW_DELAY (line 5) | const BALLOON_SHOW_DELAY = 200;
constant BALLOON_HIDE_DELAY (line 8) | const BALLOON_HIDE_DELAY = 400;
FILE: src/lv2/withDropdown/WithDropdown.tsx
type Props (line 8) | type Props = {
FILE: src/lv2/withFilterableDropdown/WithFilterableDropdown.tsx
type WithFilterableDropdownContentProps (line 16) | type WithFilterableDropdownContentProps = {
type FixedItem (line 146) | type FixedItem = {
type FilterableDropdownContent (line 154) | type FilterableDropdownContent = (
type Props (line 163) | type Props = Pick<Parameters<typeof WithPopup>[0], 'render' | 'disabled'...
FILE: src/lv2/withPopup/WithPopup.tsx
type Props (line 11) | type Props = {
FILE: src/lv2/withTOC/WithTOC.tsx
type WithTOCContent (line 5) | type WithTOCContent = {
type Props (line 11) | type Props = {
FILE: src/utilities/AriaProps.ts
type AriaExpandedT (line 6) | type AriaExpandedT = boolean;
type AriaPressedT (line 7) | type AriaPressedT = boolean | 'mixed';
type AriaControlsT (line 8) | type AriaControlsT = string;
type AriaOwnsT (line 9) | type AriaOwnsT = string;
type AriaHaspopupT (line 10) | type AriaHaspopupT = boolean | 'menu' | 'listbox' | 'tree' | 'grid' | 'd...
type AriaDescribedbyT (line 11) | type AriaDescribedbyT = string;
type AriaActivedescendantT (line 12) | type AriaActivedescendantT = string;
type AriaAutocompleteT (line 13) | type AriaAutocompleteT = 'inline' | 'list' | 'both' | 'none';
type AriaAtomicT (line 14) | type AriaAtomicT = boolean;
type AriaNumberValueT (line 15) | type AriaNumberValueT = number;
type AriaLevelT (line 16) | type AriaLevelT = number;
type AriaSetSizeT (line 17) | type AriaSetSizeT = number;
type AriaPosinsetT (line 18) | type AriaPosinsetT = number;
type ButtonAriaProps (line 20) | type ButtonAriaProps = {
type LinkAriaProps (line 30) | type LinkAriaProps = {
type TextBoxAriaProps (line 39) | type TextBoxAriaProps = {
type NumberInputAriaProps (line 50) | type NumberInputAriaProps = {
type TableRowAriaProps (line 56) | type TableRowAriaProps = {
function filterButtonAriaProps (line 63) | function filterButtonAriaProps(props: ButtonAriaProps): ButtonAriaProps {
function filterLinkAriaProps (line 75) | function filterLinkAriaProps(props: LinkAriaProps): LinkAriaProps {
function filterTextBoxAriaProps (line 86) | function filterTextBoxAriaProps(
function filterNumberInputAriaProps (line 101) | function filterNumberInputAriaProps(
function filterTableRowAriaProps (line 111) | function filterTableRowAriaProps(
FILE: src/utilities/Dialog.tsx
type DialogProps (line 24) | type DialogProps = {
type DialogContentProps (line 74) | type DialogContentProps = {
FILE: src/utilities/ScrollPortal.tsx
type Props (line 6) | type Props = {
FILE: src/utilities/VibesContext.ts
type VibesContextValue (line 3) | type VibesContextValue = {
FILE: src/utilities/VibesProvider.tsx
type MediaType (line 11) | type MediaType = 'pc' | 'tablet' | 'mobile';
FILE: src/utilities/commonProps.ts
type CommonDataProps (line 8) | type CommonDataProps = {
type CommonProps (line 15) | type CommonProps = CommonDataProps & FunctionalMarginProps;
function commonProps (line 31) | function commonProps(
FILE: src/utilities/date.ts
function parseDate (line 13) | function parseDate(date: Date | string | null | undefined): Date | null {
function isValidDateInRange (line 26) | function isValidDateInRange(
function getValidDateNearestTarget (line 50) | function getValidDateNearestTarget(
function formatDate (line 84) | function formatDate(date: string | Date): string {
function formatDayOfWeek (line 89) | function formatDayOfWeek(date: string | Date): string {
FILE: src/utilities/functionalMarginClasses.ts
type MarginSize (line 1) | type MarginSize =
type FunctionalMarginProps (line 16) | type FunctionalMarginProps = {
FILE: src/utilities/marginClasses.ts
type MarginClassProps (line 5) | type MarginClassProps = {
type MarginSize (line 29) | type MarginSize = 'xSmall' | 'small' | 'large' | 'xLarge' | 'xxLarge';
function marginSizeToRem (line 31) | function marginSizeToRem(
function marginClasses (line 79) | function marginClasses(props: MarginClassProps): Array<string> {
FILE: src/utilities/selfWindowNavigator.ts
type SelfWindowNavigationProp (line 5) | type SelfWindowNavigationProp = {
FILE: src/utilities/vbClassNames.ts
type ModifierClassProps (line 7) | type ModifierClassProps = {
Condensed preview — 594 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (2,652K chars).
[
{
"path": ".babelrc",
"chars": 647,
"preview": "{\n \"presets\": [\n [\n \"@babel/preset-env\",\n {\n \"targets\": {\n \"browsers\": [\n \"last"
},
{
"path": ".circleci/config.yml",
"chars": 3303,
"preview": "version: 2.1\nexecutors:\n node:\n docker:\n - image: cimg/node:18.16.1\n\ndefaults: &defaults\n working_directory: ~"
},
{
"path": ".dockerignore",
"chars": 15,
"preview": "node_modules/\n\n"
},
{
"path": ".editorconfig",
"chars": 178,
"preview": "root = true\n\n[*]\ncharset = utf-8\nend_of_line = lf\nindent_style = space\nindent_size = 2\ntrim_trailing_whitespace = true\ni"
},
{
"path": ".eslintignore",
"chars": 7,
"preview": "/dist/\n"
},
{
"path": ".eslintrc.js",
"chars": 2128,
"preview": "module.exports = {\n parser: '@babel/eslint-parser',\n env: {\n browser: true,\n jest: true,\n node: true\n },\n p"
},
{
"path": ".flowconfig",
"chars": 226,
"preview": "[ignore]\n.*/__tests__/types/.*\nlv1/.*\nlv2/.*\nutilities/.*\nhooks/.*\n\n\n[include]\n\n[libs]\n\n[lints]\n\n[options]\nserver.max_wo"
},
{
"path": ".github/PULL_REQUEST_TEMPLATE.md",
"chars": 314,
"preview": "<!--\n\n**注意**\nこれは public repositoryです。\nここに書いた情報は、全世界に公開されます。\n社内の機密情報、特にリリース前のプロダクトや機能に関する情報を記載しないでください!\n -->\n\n## :memo: 概"
},
{
"path": ".github/workflows/publish_package_to_npmjs.yml",
"chars": 492,
"preview": "name: Publish Package to npmjs\n\non:\n release:\n types: [published]\n\njobs:\n build:\n runs-on: ubuntu-latest\n\n st"
},
{
"path": ".github/workflows/publish_storybook.yml",
"chars": 870,
"preview": "name: publish Storybook\non:\n push:\n branches:\n - 'main'\njobs:\n publish:\n if: github.repository == 'freee/vi"
},
{
"path": ".github/workflows/pull_request_test_and_lint.yml",
"chars": 405,
"preview": "name: Pull Request Test and Lint\n\non:\n pull_request:\n branches:\n - main\n\njobs:\n test_and_lint:\n runs-on: ub"
},
{
"path": ".gitignore",
"chars": 169,
"preview": "node_modules\n*.log\n.DS_Store\n.idea/\n.cache\n.vscode\nstorybook-static/\ndist/\n\n# docker\ndocker/.build_timestamp\n\n# direnv /"
},
{
"path": ".husky/pre-commit",
"chars": 58,
"preview": "#!/bin/sh\n. \"$(dirname \"$0\")/_/husky.sh\"\n\nnpx lint-staged\n"
},
{
"path": ".jest/Mock.js",
"chars": 0,
"preview": ""
},
{
"path": ".jest/setup.js",
"chars": 0,
"preview": ""
},
{
"path": ".node-version",
"chars": 8,
"preview": "18.16.1\n"
},
{
"path": ".npmrc",
"chars": 18,
"preview": "save-exact = true\n"
},
{
"path": ".prettierignore",
"chars": 7,
"preview": "/dist/\n"
},
{
"path": ".storybook/main.ts",
"chars": 1504,
"preview": "import type { StorybookConfig } from '@storybook/react-webpack5';\n\nconst config: StorybookConfig = {\n addons: [\n '@s"
},
{
"path": ".storybook/manager.js",
"chars": 240,
"preview": "import { addons } from '@storybook/addons';\nimport { create } from '@storybook/theming';\nimport logo from './logo-vibes."
},
{
"path": ".storybook/preview.tsx",
"chars": 1888,
"preview": "import * as React from 'react';\nimport ReactDOM from 'react-dom';\nimport { DocsContainer } from '@storybook/addon-docs';"
},
{
"path": "Dockerfile",
"chars": 709,
"preview": "FROM node:14.15.4\nARG github_username\nARG github_token\n\nENV GITHUB_USERNAME $github_username\nENV GITHUB_TOKEN $github_to"
},
{
"path": "LICENSE.txt",
"chars": 11341,
"preview": "\n Apache License\n Version 2.0, January 2004\n "
},
{
"path": "Makefile",
"chars": 974,
"preview": "APP_NAME := vibes\nVERSION_TAG ?= latest\nIMAGE_NAME := $(APP_NAME):$(VERSION_TAG)\n\n.DEFAULT_GOAL = help # display usage w"
},
{
"path": "README.md",
"chars": 937,
"preview": "[世界を変えるためのデザインシステム](https://speakerdeck.com/ymrl/shi-jie-wobian-erutamefalsedezainsisutemu)です\n\n# Getting started\n\n## Ins"
},
{
"path": "__tests__/types/flow/.flowconfig",
"chars": 573,
"preview": "[ignore]\n\n[include]\n./../../../index.js\n./../../../lv1.js\n./../../../lv2.js\n./../../../utilities.js\n./../../../node_modu"
},
{
"path": "__tests__/types/flow/lv1.js",
"chars": 21152,
"preview": "// @flow\n\nimport * as React from 'react';\nimport {\n FocusHighlight,\n VisuallyHidden,\n Balloon,\n ColumnBase,\n Contai"
},
{
"path": "__tests__/types/flow/lv2.js",
"chars": 37444,
"preview": "// @flow\n\nimport * as React from 'react';\nimport {\n TextField,\n RadioButton,\n CheckBox,\n CardNavigation,\n SelectBox"
},
{
"path": "__tests__/types/flow/utilities.js",
"chars": 423,
"preview": "// @flow\nimport {\n getFocusableElements,\n isFocusableElement,\n // eslint-disable-next-line import/no-unresolved\n} fro"
},
{
"path": "docker/git-credential-github-token",
"chars": 112,
"preview": "#!/bin/sh\n\necho protocol=https\necho host=github.com\necho username=$GITHUB_USERNAME\necho password=$GITHUB_TOKEN\n\n"
},
{
"path": "docs/Colors.stories.mdx",
"chars": 2984,
"preview": "import { Meta } from \"@storybook/addon-docs\";\n\n<Meta title=\"doc/Colors\" />\n\n# 色についての方針\n\nvibes 内で使用する色、ならびに vibes を使用するプロ"
},
{
"path": "docs/Contribution.stories.mdx",
"chars": 1852,
"preview": "import { Meta } from \"@storybook/addon-docs\";\n\n<Meta title='doc/Contribution' />\n\n# vibes の開発に参加する\n\nこのドキュメントを読んでいただいてありが"
},
{
"path": "docs/Design/Layout/Layout.stories.mdx",
"chars": 1566,
"preview": "import { Meta } from \"@storybook/addon-docs\";\n\nimport widthImg from './width.png';\nimport heightImg from './height.jpg';"
},
{
"path": "docs/Design/Readme.stories.mdx",
"chars": 1311,
"preview": "import { Meta } from \"@storybook/addon-docs\";\n\n<Meta title='doc/Design/Readme' />\n\n\n# コンポーネント設計の指針\n\nVibesのコンポーネントを設計するとき"
},
{
"path": "docs/Readme.stories.mdx",
"chars": 4941,
"preview": "import { Meta } from '@storybook/addon-docs';\n\n<Meta title=\"doc/Readme\" />\n\n# vibes\n\nコンポーネントベースのデザインシステムです。\n\n## vibes の動"
},
{
"path": "docs/Storybook.stories.mdx",
"chars": 3365,
"preview": "import { Meta } from \"@storybook/addon-docs\";\n\n<Meta title='doc/Storybook' />\n\n## Storybookの使い方\n\nStorybookではコンポーネントの見た目や"
},
{
"path": "docs/Stylesheets.stories.mdx",
"chars": 2636,
"preview": "import { Meta } from \"@storybook/addon-docs\";\n\n<Meta title='doc/Stylesheets' />\n\n# CSS\n\n変更する場合は **stylesheetsディレクトリに格納され"
},
{
"path": "docs/TypeScript.stories.mdx",
"chars": 1205,
"preview": "import { Meta } from \"@storybook/addon-docs\";\n\n<Meta title='doc/TypeScript' />\n\n# TypeScript (JavaScript)\n\n変更する場合は **src"
},
{
"path": "examples/Collection.mdx",
"chars": 2842,
"preview": "import { Story, Canvas } from '@storybook/addon-docs/blocks';\n\n# コレクション画面\n\nオブジェクトを一覧表示し、それぞれのオブジェクト個別の画面(シングル画面)に遷移させたり、"
},
{
"path": "examples/Collection.stories.tsx",
"chars": 37040,
"preview": "import * as React from 'react';\nimport { MdAdd, MdFilterList } from 'react-icons/md';\nimport Collection from './Collecti"
},
{
"path": "examples/Forms.mdx",
"chars": 4965,
"preview": "import { Story, Canvas } from '@storybook/addon-docs/blocks';\n\n# フォーム\n\nvibes では3種類のフォームの組み方を提供しています\n\n- 縦フォーム\n- 横フォーム\n- リ"
},
{
"path": "examples/Forms.stories.tsx",
"chars": 37958,
"preview": "import * as React from 'react';\nimport Forms from './Forms.mdx';\nimport {\n Button,\n CheckBox,\n DateField,\n DateInput"
},
{
"path": "examples/ImportWizard.mdx",
"chars": 1070,
"preview": "import { Story, Canvas } from '@storybook/addon-docs/blocks';\n\n# インポートウィザード\n\nインポートによってオブジェクトの新規追加を行う画面の例です。\n\n- スクリーンリーダー"
},
{
"path": "examples/ImportWizard.stories.tsx",
"chars": 7920,
"preview": "import * as React from 'react';\nimport { MdFileDownload } from 'react-icons/md';\nimport ImportWizard from './ImportWizar"
},
{
"path": "examples/Pages.stories.tsx",
"chars": 15959,
"preview": "import * as React from 'react';\nimport { Container, ContentsBase, Footer, GlobalNavi, Header } from '../src';\nimport {\n "
},
{
"path": "examples/ResponsiveLayout.mdx",
"chars": 246,
"preview": "import { Story, Canvas } from '@storybook/addon-docs/blocks';\n\n# レスポンシブレイアウト\n\n<Canvas>\n {/* Container を使っているとinlineでは見辛"
},
{
"path": "examples/ResponsiveLayout.stories.tsx",
"chars": 6291,
"preview": "import * as React from 'react';\nimport ResponsiveLayout from './ResponsiveLayout.mdx';\n\nimport {\n Button,\n ColumnBase,"
},
{
"path": "examples/ThroughCommonProps.mdx",
"chars": 337,
"preview": "import { Story, Canvas } from '@storybook/addon-docs/blocks';\n\n# CommonProps の通過(子要素への移譲)\n\n`pickCommonProps` ユーティリティ関数は、"
},
{
"path": "examples/ThroughCommonProps.stories.tsx",
"chars": 1103,
"preview": "import * as React from 'react';\nimport ThroughCommonProps from './ThroughCommonProps.mdx';\nimport { Button, CommonProps,"
},
{
"path": "index.d.ts",
"chars": 24,
"preview": "export * from './dist';\n"
},
{
"path": "index.js",
"chars": 24,
"preview": "export * from './dist';\n"
},
{
"path": "index.js.flow",
"chars": 3280,
"preview": "// @flow\n/* eslint-disable import/no-unresolved */\nimport * as Lv1 from './lv1';\nimport * as Lv2 from './lv2';\nimport * "
},
{
"path": "injectFileName.js",
"chars": 0,
"preview": ""
},
{
"path": "jest.config.js",
"chars": 104,
"preview": "module.exports = {\n testEnvironment: 'jsdom',\n roots: ['<rootDir>'],\n modulePaths: ['<rootDir>'],\n};\n"
},
{
"path": "lv1.js.flow",
"chars": 33179,
"preview": "// @flow\n\nimport * as React from 'react';\n// eslint-disable-next-line import/no-unresolved\nimport * as Utilities from '."
},
{
"path": "lv2.js.flow",
"chars": 56792,
"preview": "// @flow\n\nimport * as React from 'react';\n// eslint-disable-next-line import/no-unresolved\nimport * as Utilities from '."
},
{
"path": "package.json",
"chars": 5441,
"preview": "{\n \"name\": \"@freee_jp/vibes\",\n \"version\": \"100.1.0\",\n \"repository\": \"git@github.com:freee/vibes.git\",\n \"author\": \"fr"
},
{
"path": "scripts/release.js",
"chars": 1500,
"preview": "const path = require('path');\nconst replace = require('replace-in-file');\nconst execSync = require('child_process').exec"
},
{
"path": "src/@types/image.d.ts",
"chars": 73,
"preview": "declare module '*.jpeg';\ndeclare module '*.jpg';\ndeclare module '*.png';\n"
},
{
"path": "src/@types/mdx.d.ts",
"chars": 125,
"preview": "// types/mdx.d.ts\ndeclare module '*.mdx' {\n let MDXComponent: (props: any) => JSX.Element;\n export default MDXComponen"
},
{
"path": "src/@types/redux.d.ts",
"chars": 160,
"preview": "// FIXME: Vibesではreduxを使わないのだが、react-dndの問題でmoduleだけ宣言している\n// https://github.com/react-dnd/react-dnd/issues/1560#issueco"
},
{
"path": "src/constants/Color.ts",
"chars": 3803,
"preview": "export const VibesBlackColor = '#252525';\nexport const VibesBase1Color = '#efede8';\nexport const VibesBase2Color = '#e4e"
},
{
"path": "src/constants/Font.ts",
"chars": 2012,
"preview": "// type-scale\n// design-token (各コンポーネントの実装に直接使わないでください)\nexport const FontSize0625 = '0.625rem'; // 10dp\nexport const Fon"
},
{
"path": "src/constants/Size.ts",
"chars": 2133,
"preview": "export const MinimumSize = '1px';\n\nexport const XSmallSize = '0.25rem'; // 4dp\nexport const SmallSize = '0.5rem'; // 8dp"
},
{
"path": "src/constants/ZIndex.ts",
"chars": 348,
"preview": "export const OverlayZIndex = '100';\nexport const FormActionsZIndex = '200';\nexport const FloatingZIndex = '500';\nexport "
},
{
"path": "src/constants/index.ts",
"chars": 99,
"preview": "export * from './Color';\nexport * from './Font';\nexport * from './Size';\nexport * from './ZIndex';\n"
},
{
"path": "src/hooks/useUniqueId.ts",
"chars": 409,
"preview": "import { useEffect, useState } from 'react';\n\nlet idCounter = 0;\nfunction createUniqueId(base: string): string {\n retur"
},
{
"path": "src/index.ts",
"chars": 104,
"preview": "export * from './lv1';\nexport * from './lv2';\nexport * from './utilities';\nexport * from './constants';\n"
},
{
"path": "src/internal/CommonStyle.ts",
"chars": 1283,
"preview": "import styled from 'styled-components';\nimport { NormalFont, VibesBlackColor } from '../constants';\nimport {\n MarginSiz"
},
{
"path": "src/lv1/InlineSpinner.stories.tsx",
"chars": 819,
"preview": "import * as React from 'react';\n\nimport { boolean } from '@storybook/addon-knobs';\nimport { commonKnobs } from '../../st"
},
{
"path": "src/lv1/InlineSpinner.tsx",
"chars": 1825,
"preview": "import lottie from 'lottie-web';\nimport * as React from 'react';\nimport { CSSTransition } from 'react-transition-group';"
},
{
"path": "src/lv1/Loading/loading-parts.json",
"chars": 2542,
"preview": "{\"v\":\"5.7.4\",\"fr\":29.9700012207031,\"ip\":0,\"op\":60.0000024438501,\"w\":48,\"h\":48,\"nm\":\"コンポ 1\",\"ddd\":0,\"assets\":[],\"layers\":"
},
{
"path": "src/lv1/Loading/loading-whole-2021.json",
"chars": 9869,
"preview": "{\"v\":\"5.7.11\",\"fr\":29.9700012207031,\"ip\":0,\"op\":80.0000032584668,\"w\":504,\"h\":168,\"nm\":\"レイヤー 1/loading_swallow\",\"ddd\":0,\""
},
{
"path": "src/lv1/Loading.stories.tsx",
"chars": 1967,
"preview": "import { boolean, text } from '@storybook/addon-knobs';\nimport * as React from 'react';\n\nimport Loading from './Loading'"
},
{
"path": "src/lv1/Loading.tsx",
"chars": 3940,
"preview": "import lottie from 'lottie-web';\nimport * as React from 'react';\nimport ReactDOM from 'react-dom';\nimport { CSSTransitio"
},
{
"path": "src/lv1/a11y/FocusHighlight.stories.tsx",
"chars": 2424,
"preview": "import * as React from 'react';\n\nimport { commonKnobs } from '../../../stories';\nimport { FocusHighlight } from './Focus"
},
{
"path": "src/lv1/a11y/FocusHighlight.tsx",
"chars": 1249,
"preview": "import * as React from 'react';\nimport commonProps, { CommonProps } from '../../utilities/commonProps';\n\ntype Props = {\n"
},
{
"path": "src/lv1/a11y/FocusTrap.stories.tsx",
"chars": 6210,
"preview": "import * as React from 'react';\n\nimport { commonKnobs } from '../../../stories';\nimport { FocusTrap } from './FocusTrap'"
},
{
"path": "src/lv1/a11y/FocusTrap.tsx",
"chars": 4515,
"preview": "import * as React from 'react';\nimport commonProps, { CommonProps } from '../../utilities/commonProps';\nimport { getFocu"
},
{
"path": "src/lv1/a11y/VisuallyHidden.stories.tsx",
"chars": 2019,
"preview": "import * as React from 'react';\n\nimport { text, boolean } from '@storybook/addon-knobs';\nimport { commonKnobs } from '.."
},
{
"path": "src/lv1/a11y/VisuallyHidden.tsx",
"chars": 1790,
"preview": "import * as React from 'react';\nimport { CommonProps, pickCommonProps } from '../../utilities/commonProps';\nimport style"
},
{
"path": "src/lv1/bases/Balloon.stories.tsx",
"chars": 1338,
"preview": "import * as React from 'react';\n\nimport { select, boolean } from '@storybook/addon-knobs';\nimport Balloon, { BalloonInte"
},
{
"path": "src/lv1/bases/Balloon.tsx",
"chars": 1708,
"preview": "import * as React from 'react';\nimport { MarginClassProps } from '../../utilities/marginClasses';\nimport commonProps, { "
},
{
"path": "src/lv1/bases/CardBase.stories.tsx",
"chars": 3982,
"preview": "import * as React from 'react';\n\nimport CardBase from './CardBase';\nimport Paragraph from '../typography/Paragraph';\nimp"
},
{
"path": "src/lv1/bases/CardBase.tsx",
"chars": 3701,
"preview": "import * as React from 'react';\nimport commonProps, { CommonProps } from '../../utilities/commonProps';\nimport { MarginC"
},
{
"path": "src/lv1/bases/ColumnBase.stories.tsx",
"chars": 3374,
"preview": "import * as React from 'react';\n\nimport ColumnBase from './ColumnBase';\nimport Paragraph from '../typography/Paragraph';"
},
{
"path": "src/lv1/bases/ColumnBase.tsx",
"chars": 2044,
"preview": "import * as React from 'react';\nimport { MarginClassProps } from '../../utilities/marginClasses';\nimport commonProps, { "
},
{
"path": "src/lv1/bases/Container.stories.tsx",
"chars": 1337,
"preview": "import * as React from 'react';\n\nimport Container from './Container';\nimport ContentsBase from './ContentsBase';\nimport "
},
{
"path": "src/lv1/bases/Container.tsx",
"chars": 1335,
"preview": "import * as React from 'react';\nimport { MarginClassProps } from '../../utilities/marginClasses';\nimport commonProps, { "
},
{
"path": "src/lv1/bases/ContentsBase.stories.tsx",
"chars": 553,
"preview": "import * as React from 'react';\n\nimport ContentsBase from './ContentsBase';\nimport { commonKnobs } from '../../../storie"
},
{
"path": "src/lv1/bases/ContentsBase.tsx",
"chars": 1013,
"preview": "import * as React from 'react';\nimport { MarginClassProps } from '../../utilities/marginClasses';\nimport commonProps, { "
},
{
"path": "src/lv1/bases/DialogBase.stories.tsx",
"chars": 2422,
"preview": "import * as React from 'react';\n\nimport DialogBase from './DialogBase';\nimport Paragraph from '../typography/Paragraph';"
},
{
"path": "src/lv1/bases/DialogBase.tsx",
"chars": 1643,
"preview": "import * as React from 'react';\nimport { MarginClassProps } from '../../utilities/marginClasses';\nimport commonProps, { "
},
{
"path": "src/lv1/bases/FloatingBase.stories.tsx",
"chars": 3101,
"preview": "import * as React from 'react';\n\nimport FloatingBase from './FloatingBase';\nimport Paragraph from '../typography/Paragra"
},
{
"path": "src/lv1/bases/FloatingBase.tsx",
"chars": 1559,
"preview": "import * as React from 'react';\nimport { MarginClassProps } from '../../utilities/marginClasses';\nimport commonProps, { "
},
{
"path": "src/lv1/bases/MarginBase.stories.tsx",
"chars": 2759,
"preview": "import * as React from 'react';\n\nimport MarginBase from './MarginBase';\nimport { boolean, select } from '@storybook/addo"
},
{
"path": "src/lv1/bases/MarginBase.tsx",
"chars": 839,
"preview": "import * as React from 'react';\nimport { MarginClassProps } from '../../utilities/marginClasses';\nimport { CommonProps }"
},
{
"path": "src/lv1/bases/NegativeContentsBase.stories.tsx",
"chars": 3303,
"preview": "import * as React from 'react';\n\nimport { NegativeContentsBase } from './NegativeContentsBase';\nimport ContentsBase from"
},
{
"path": "src/lv1/bases/NegativeContentsBase.tsx",
"chars": 1025,
"preview": "import * as React from 'react';\nimport commonProps, { CommonProps } from '../../utilities/commonProps';\nimport { useResp"
},
{
"path": "src/lv1/bases/NegativeMarginBase.stories.tsx",
"chars": 2212,
"preview": "import * as React from 'react';\n\nimport NegativeMarginBase from './NegativeMarginBase';\nimport Container from './Contain"
},
{
"path": "src/lv1/bases/NegativeMarginBase.tsx",
"chars": 904,
"preview": "import * as React from 'react';\nimport commonProps, { CommonProps } from '../../utilities/commonProps';\n\ntype Props = {\n"
},
{
"path": "src/lv1/bases/PopupBase.stories.tsx",
"chars": 2450,
"preview": "import * as React from 'react';\n\nimport PopupBase from './PopupBase';\nimport Paragraph from '../typography/Paragraph';\ni"
},
{
"path": "src/lv1/bases/PopupBase.tsx",
"chars": 1721,
"preview": "import * as React from 'react';\nimport { MarginClassProps } from '../../utilities/marginClasses';\nimport commonProps, { "
},
{
"path": "src/lv1/bases/ScrimBase.stories.tsx",
"chars": 400,
"preview": "import * as React from 'react';\n\nimport ScrimBase from './ScrimBase';\nimport { boolean } from '@storybook/addon-knobs';\n"
},
{
"path": "src/lv1/bases/ScrimBase.tsx",
"chars": 791,
"preview": "import * as React from 'react';\nimport { MarginClassProps } from '../../utilities/marginClasses';\nimport commonProps, { "
},
{
"path": "src/lv1/bases/ScrollableBase.stories.tsx",
"chars": 2226,
"preview": "import * as React from 'react';\n\nimport ScrollableBase from './ScrollableBase';\nimport { boolean } from '@storybook/addo"
},
{
"path": "src/lv1/bases/ScrollableBase.tsx",
"chars": 643,
"preview": "import * as React from 'react';\nimport commonProps, { CommonProps } from '../../utilities/commonProps';\n\ntype Props = {\n"
},
{
"path": "src/lv1/bases/ZebraBase.stories.tsx",
"chars": 4214,
"preview": "import * as React from 'react';\n\nimport ZebraBase from './ZebraBase';\nimport { commonKnobs } from '../../../stories';\nim"
},
{
"path": "src/lv1/bases/ZebraBase.tsx",
"chars": 1109,
"preview": "import * as React from 'react';\nimport { MarginClassProps } from '../../utilities/marginClasses';\nimport commonProps, { "
},
{
"path": "src/lv1/bases/types.ts",
"chars": 753,
"preview": "export type BaseComponentBorderProps = {\n border?: 'default' | 'alert' | 'notice' | 'success';\n};\n\nexport type BaseComp"
},
{
"path": "src/lv1/buttons/BackwardButton.stories.tsx",
"chars": 4884,
"preview": "import * as React from 'react';\n\nimport { action } from '@storybook/addon-actions';\nimport { boolean, text, select } fro"
},
{
"path": "src/lv1/buttons/BackwardButton.tsx",
"chars": 2397,
"preview": "import * as React from 'react';\nimport { MdChevronLeft } from 'react-icons/md';\nimport { MarginClassProps } from '../../"
},
{
"path": "src/lv1/buttons/Button.stories.tsx",
"chars": 10592,
"preview": "import * as React from 'react';\n\nimport { MdArrowDropDown } from 'react-icons/md';\nimport { expect, fn, userEvent, withi"
},
{
"path": "src/lv1/buttons/Button.tsx",
"chars": 7285,
"preview": "import * as React from 'react';\nimport { MarginClassProps } from '../../utilities/marginClasses';\nimport commonProps, { "
},
{
"path": "src/lv1/buttons/GlobalNaviButton.stories.tsx",
"chars": 1039,
"preview": "import * as React from 'react';\n\nimport { MdHome, MdRouter } from 'react-icons/md';\nimport { boolean, text } from '@stor"
},
{
"path": "src/lv1/buttons/GlobalNaviButton.tsx",
"chars": 1660,
"preview": "import * as React from 'react';\nimport commonProps, { CommonProps } from '../../utilities/commonProps';\nimport selfWindo"
},
{
"path": "src/lv1/buttons/IconOnlyBackwardButton.stories.tsx",
"chars": 5995,
"preview": "import * as React from 'react';\n\nimport { action } from '@storybook/addon-actions';\nimport { boolean, select, text } fro"
},
{
"path": "src/lv1/buttons/IconOnlyBackwardButton.tsx",
"chars": 1680,
"preview": "import * as React from 'react';\nimport { MdChevronLeft } from 'react-icons/md';\nimport commonProps, { CommonProps } from"
},
{
"path": "src/lv1/buttons/IconOnlyButton.stories.tsx",
"chars": 7259,
"preview": "import * as React from 'react';\n\nimport { MdArrowDropDown } from 'react-icons/md';\nimport { action } from '@storybook/ad"
},
{
"path": "src/lv1/buttons/IconOnlyButton.tsx",
"chars": 4867,
"preview": "import * as React from 'react';\nimport { MarginClassProps } from '../../utilities/marginClasses';\nimport commonProps, { "
},
{
"path": "src/lv1/buttons/IconOnlyJumpButton.stories.tsx",
"chars": 7077,
"preview": "import * as React from 'react';\n\nimport { action } from '@storybook/addon-actions';\nimport { boolean, select, text } fro"
},
{
"path": "src/lv1/buttons/IconOnlyJumpButton.tsx",
"chars": 1851,
"preview": "import * as React from 'react';\nimport { MdChevronRight, MdOpenInNew } from 'react-icons/md';\nimport commonProps, { Comm"
},
{
"path": "src/lv1/buttons/InlineLink.stories.tsx",
"chars": 1853,
"preview": "import * as React from 'react';\n\nimport { action } from '@storybook/addon-actions';\nimport { select, text } from '@story"
},
{
"path": "src/lv1/buttons/InlineLink.tsx",
"chars": 3297,
"preview": "import * as React from 'react';\nimport commonProps, { CommonProps } from '../../utilities/commonProps';\nimport {\n Butto"
},
{
"path": "src/lv1/buttons/JumpButton.stories.tsx",
"chars": 5800,
"preview": "import * as React from 'react';\n\nimport { action } from '@storybook/addon-actions';\nimport { boolean, select, text } fro"
},
{
"path": "src/lv1/buttons/JumpButton.tsx",
"chars": 2979,
"preview": "import * as React from 'react';\nimport { MdChevronRight, MdOpenInNew } from 'react-icons/md';\nimport { MarginClassProps "
},
{
"path": "src/lv1/buttons/LeftIconButton.stories.tsx",
"chars": 903,
"preview": "import * as React from 'react';\n\nimport LeftIconButton from './LeftIconButton';\nimport { commonKnobs } from '../../../st"
},
{
"path": "src/lv1/buttons/LeftIconButton.tsx",
"chars": 701,
"preview": "import * as React from 'react';\nimport { MarginClassProps } from '../../utilities/marginClasses';\nimport { CommonProps }"
},
{
"path": "src/lv1/buttons/ListButton.stories.tsx",
"chars": 1310,
"preview": "import * as React from 'react';\n\nimport { action } from '@storybook/addon-actions';\nimport { boolean, select, text } fro"
},
{
"path": "src/lv1/buttons/ListButton.tsx",
"chars": 3780,
"preview": "import * as React from 'react';\nimport StatusIcon, { StatusType } from '../icons/StatusIcon';\nimport { MdOpenInNew } fro"
},
{
"path": "src/lv1/buttons/PagerButton.stories.tsx",
"chars": 2037,
"preview": "import * as React from 'react';\n\nimport { action } from '@storybook/addon-actions';\nimport { boolean, text } from '@stor"
},
{
"path": "src/lv1/buttons/PagerButton.tsx",
"chars": 1357,
"preview": "import * as React from 'react';\nimport { MarginClassProps } from '../../utilities/marginClasses';\nimport commonProps, { "
},
{
"path": "src/lv1/buttons/RightIconButton.stories.tsx",
"chars": 916,
"preview": "import * as React from 'react';\n\nimport RightIconButton from './RightIconButton';\nimport { commonKnobs } from '../../../"
},
{
"path": "src/lv1/buttons/RightIconButton.tsx",
"chars": 778,
"preview": "import * as React from 'react';\nimport { MarginClassProps } from '../../utilities/marginClasses';\nimport { CommonProps }"
},
{
"path": "src/lv1/buttons/TabButton.stories.tsx",
"chars": 971,
"preview": "import * as React from 'react';\n\nimport { boolean, text } from '@storybook/addon-knobs';\nimport { commonKnobs } from '.."
},
{
"path": "src/lv1/buttons/TabButton.tsx",
"chars": 922,
"preview": "import * as React from 'react';\nimport { MarginClassProps } from '../../utilities/marginClasses';\nimport commonProps, { "
},
{
"path": "src/lv1/buttons/TextButton.stories.tsx",
"chars": 1105,
"preview": "import * as React from 'react';\n\nimport TextButton from './TextButton';\nimport { commonKnobs } from '../../../stories';\n"
},
{
"path": "src/lv1/buttons/TextButton.tsx",
"chars": 2783,
"preview": "import * as React from 'react';\nimport { MarginClassProps } from '../../utilities/marginClasses';\nimport commonProps, { "
},
{
"path": "src/lv1/calendar/CalendarDate.stories.tsx",
"chars": 3785,
"preview": "import * as React from 'react';\nimport { action } from '@storybook/addon-actions';\nimport { boolean, text } from '@story"
},
{
"path": "src/lv1/calendar/CalendarDate.tsx",
"chars": 3903,
"preview": "import * as React from 'react';\nimport { getDate } from 'date-fns';\nimport { MdError, MdWarning, MdCheckCircle } from 'r"
},
{
"path": "src/lv1/calendar/CalendarHead.stories.tsx",
"chars": 507,
"preview": "import * as React from 'react';\nimport { select } from '@storybook/addon-knobs';\nimport { commonKnobs } from '../../../s"
},
{
"path": "src/lv1/calendar/CalendarHead.tsx",
"chars": 1488,
"preview": "import * as React from 'react';\nimport commonProps, { CommonProps } from '../../utilities/commonProps';\n\ntype Props = {\n"
},
{
"path": "src/lv1/forms/CheckBox.stories.tsx",
"chars": 1531,
"preview": "import * as React from 'react';\n\nimport { actions } from '@storybook/addon-actions';\nimport { boolean, text } from '@sto"
},
{
"path": "src/lv1/forms/CheckBox.tsx",
"chars": 2783,
"preview": "import * as React from 'react';\nimport { MarginClassProps } from '../../utilities/marginClasses';\nimport commonProps, { "
},
{
"path": "src/lv1/forms/FormControlLabel.stories.tsx",
"chars": 650,
"preview": "import * as React from 'react';\n\nimport { boolean, text } from '@storybook/addon-knobs';\nimport { commonKnobs } from '.."
},
{
"path": "src/lv1/forms/FormControlLabel.tsx",
"chars": 1528,
"preview": "import * as React from 'react';\nimport { RequiredIcon } from '../../lv1/icons/RequiredIcon';\nimport { MarginClassProps }"
},
{
"path": "src/lv1/forms/NumeralField.stories.tsx",
"chars": 3881,
"preview": "import * as React from 'react';\n\nimport { actions } from '@storybook/addon-actions';\nimport { boolean, number, select, t"
},
{
"path": "src/lv1/forms/NumeralField.tsx",
"chars": 1006,
"preview": "import React from 'react';\nimport TextField from './TextField';\nimport { Ascii, Digits } from '../../utilities';\n\ntype P"
},
{
"path": "src/lv1/forms/OptionButton.stories.tsx",
"chars": 4653,
"preview": "import * as React from 'react';\n\nimport { commonKnobs } from '../../../stories';\nimport { OptionButton } from './OptionB"
},
{
"path": "src/lv1/forms/OptionButton.tsx",
"chars": 2674,
"preview": "import * as React from 'react';\nimport commonProps, { CommonProps } from '../../utilities/commonProps';\nimport useUnique"
},
{
"path": "src/lv1/forms/RadioButton.stories.tsx",
"chars": 1618,
"preview": "import * as React from 'react';\n\nimport { actions } from '@storybook/addon-actions';\nimport { boolean, text } from '@sto"
},
{
"path": "src/lv1/forms/RadioButton.tsx",
"chars": 3685,
"preview": "import * as React from 'react';\nimport { MarginClassProps } from '../../utilities/marginClasses';\nimport commonProps, { "
},
{
"path": "src/lv1/forms/ReadOnlyField.stories.tsx",
"chars": 2111,
"preview": "import * as React from 'react';\n\nimport { boolean, select, text } from '@storybook/addon-knobs';\nimport { commonKnobs } "
},
{
"path": "src/lv1/forms/ReadOnlyField.tsx",
"chars": 2071,
"preview": "import * as React from 'react';\nimport commonProps, { CommonProps } from '../../utilities/commonProps';\nimport { TextFie"
},
{
"path": "src/lv1/forms/SearchField.stories.tsx",
"chars": 3003,
"preview": "import * as React from 'react';\n\nimport { actions } from '@storybook/addon-actions';\nimport { boolean, number, select, t"
},
{
"path": "src/lv1/forms/SearchField.tsx",
"chars": 3535,
"preview": "import * as React from 'react';\nimport { MdSearch } from 'react-icons/md';\nimport vbClassNames from '../../utilities/vbC"
},
{
"path": "src/lv1/forms/SelectBox.stories.tsx",
"chars": 5424,
"preview": "import * as React from 'react';\n\nimport { actions } from '@storybook/addon-actions';\nimport { boolean, select, number } "
},
{
"path": "src/lv1/forms/SelectBox.tsx",
"chars": 5958,
"preview": "import * as React from 'react';\nimport vbClassNames from '../../utilities/vbClassNames';\nimport commonProps, { CommonPro"
},
{
"path": "src/lv1/forms/TextArea.stories.tsx",
"chars": 4845,
"preview": "import * as React from 'react';\n\nimport { actions } from '@storybook/addon-actions';\nimport { boolean, number, select, t"
},
{
"path": "src/lv1/forms/TextArea.tsx",
"chars": 5968,
"preview": "import * as React from 'react';\nimport commonProps, { CommonProps } from '../../utilities/commonProps';\nimport { MarginC"
},
{
"path": "src/lv1/forms/TextField.stories.tsx",
"chars": 8077,
"preview": "import * as React from 'react';\n\nimport { MdDateRange, MdExpandMore, MdFavorite } from 'react-icons/md';\nimport { action"
},
{
"path": "src/lv1/forms/TextField.tsx",
"chars": 9920,
"preview": "import * as React from 'react';\nimport { MarginClassProps } from '../../utilities/marginClasses';\nimport commonProps, {\n"
},
{
"path": "src/lv1/forms/ToggleButton.stories.tsx",
"chars": 2214,
"preview": "import * as React from 'react';\nimport { actions } from '@storybook/addon-actions';\nimport { boolean, select, text } fro"
},
{
"path": "src/lv1/forms/ToggleButton.tsx",
"chars": 1725,
"preview": "import * as React from 'react';\n\nimport { FormHandlers } from '../../lv1/forms/types';\nimport commonProps, { CommonProps"
},
{
"path": "src/lv1/forms/types.ts",
"chars": 1357,
"preview": "import * as React from 'react';\n\nexport type FormHandlers<T = Element> = {\n onChange?: React.ChangeEventHandler<T>;\n o"
},
{
"path": "src/lv1/grids/GridBlock.tsx",
"chars": 725,
"preview": "import * as React from 'react';\nimport commonProps, { CommonProps } from '../../utilities/commonProps';\n\nexport type Gri"
},
{
"path": "src/lv1/grids/GridWrapper.tsx",
"chars": 725,
"preview": "import * as React from 'react';\nimport { MarginClassProps } from '../../utilities/marginClasses';\nimport commonProps, { "
},
{
"path": "src/lv1/grids/Grids.stories.tsx",
"chars": 3396,
"preview": "import * as React from 'react';\n\nimport { select } from '@storybook/addon-knobs';\nimport { commonKnobs } from '../../../"
},
{
"path": "src/lv1/icons/Avatar.stories.tsx",
"chars": 1358,
"preview": "import * as React from 'react';\nimport Avatar from './Avatar';\n\nimport { commonKnobs } from '../../../stories';\nimport {"
},
{
"path": "src/lv1/icons/Avatar.tsx",
"chars": 2743,
"preview": "import * as React from 'react';\nimport commonProps, { CommonProps } from '../../utilities/commonProps';\n\ntype Props = {\n"
},
{
"path": "src/lv1/icons/MaterialIcon.stories.tsx",
"chars": 6100,
"preview": "import * as React from 'react';\n\nimport { MdInfo } from 'react-icons/md';\nimport { boolean, text } from '@storybook/addo"
},
{
"path": "src/lv1/icons/MaterialIcon.tsx",
"chars": 2990,
"preview": "import * as React from 'react';\nimport { MarginClassProps } from '../../utilities/marginClasses';\nimport commonProps, { "
},
{
"path": "src/lv1/icons/RequiredIcon.stories.tsx",
"chars": 1014,
"preview": "import * as React from 'react';\n\nimport { RequiredIcon } from './RequiredIcon';\nimport { commonKnobs } from '../../../st"
},
{
"path": "src/lv1/icons/RequiredIcon.tsx",
"chars": 1174,
"preview": "import * as React from 'react';\nimport commonProps, { CommonProps } from '../../utilities/commonProps';\nimport { useLang"
},
{
"path": "src/lv1/icons/StatusIcon.stories.tsx",
"chars": 1434,
"preview": "import * as React from 'react';\n\nimport { select, text } from '@storybook/addon-knobs';\nimport { commonKnobs } from '../"
},
{
"path": "src/lv1/icons/StatusIcon.tsx",
"chars": 1960,
"preview": "import * as React from 'react';\nimport commonProps, { CommonProps } from '../../utilities/commonProps';\nimport { MarginC"
},
{
"path": "src/lv1/images/AlertSwallow.stories.tsx",
"chars": 1554,
"preview": "import * as React from 'react';\n\nimport { commonKnobs } from '../../../stories';\nimport { Vibes2021BackgroundColor } fro"
},
{
"path": "src/lv1/images/AlertSwallow.tsx",
"chars": 12111,
"preview": "import * as React from 'react';\nimport { SwallowProps, SwallowContainer } from './SwallowContainer';\n\ntype Props = Swall"
},
{
"path": "src/lv1/images/AppStoreBadge.stories.tsx",
"chars": 264,
"preview": "import * as React from 'react';\n\nimport { commonKnobs } from '../../../stories';\nimport AppStoreBadge from './AppStoreBa"
},
{
"path": "src/lv1/images/AppStoreBadge.tsx",
"chars": 16161,
"preview": "import * as React from 'react';\nimport commonProps, { CommonProps } from '../../utilities/commonProps';\nimport { MarginC"
},
{
"path": "src/lv1/images/CloudSkeletonIllust.stories.tsx",
"chars": 294,
"preview": "import * as React from 'react';\n\nimport { commonKnobs } from '../../../stories';\nimport CloudSkeletonIllust from './Clou"
},
{
"path": "src/lv1/images/CloudSkeletonIllust.tsx",
"chars": 1277,
"preview": "import * as React from 'react';\nimport commonProps, { CommonProps } from '../../utilities/commonProps';\nimport { MarginC"
},
{
"path": "src/lv1/images/CloudUploadIllust.stories.tsx",
"chars": 284,
"preview": "import * as React from 'react';\n\nimport { commonKnobs } from '../../../stories';\nimport CloudUploadIllust from './CloudU"
},
{
"path": "src/lv1/images/CloudUploadIllust.tsx",
"chars": 1199,
"preview": "import * as React from 'react';\nimport commonProps, { CommonProps } from '../../utilities/commonProps';\nimport { MarginC"
},
{
"path": "src/lv1/images/CsvUploadIllust.stories.tsx",
"chars": 274,
"preview": "import * as React from 'react';\n\nimport { commonKnobs } from '../../../stories';\nimport CsvUploadIllust from './CsvUploa"
},
{
"path": "src/lv1/images/CsvUploadIllust.tsx",
"chars": 2171,
"preview": "import * as React from 'react';\nimport commonProps, { CommonProps } from '../../utilities/commonProps';\nimport { MarginC"
},
{
"path": "src/lv1/images/DiscoveryIllust.stories.tsx",
"chars": 408,
"preview": "import * as React from 'react';\n\nimport DiscoveryIllust from './DiscoveryIllust';\nimport { commonKnobs } from '../../../"
},
{
"path": "src/lv1/images/DiscoveryIllust.tsx",
"chars": 1059,
"preview": "import lottie from 'lottie-web';\nimport * as React from 'react';\n\nimport animDiscoveryIllust from './discovery-illust.js"
},
{
"path": "src/lv1/images/FileUploadIllust.stories.tsx",
"chars": 279,
"preview": "import * as React from 'react';\n\nimport { commonKnobs } from '../../../stories';\nimport FileUploadIllust from './FileUpl"
},
{
"path": "src/lv1/images/FileUploadIllust.tsx",
"chars": 1316,
"preview": "import * as React from 'react';\nimport commonProps, { CommonProps } from '../../utilities/commonProps';\nimport { MarginC"
},
{
"path": "src/lv1/images/FinishTaskIllust.stories.tsx",
"chars": 279,
"preview": "import * as React from 'react';\n\nimport FinishTaskIllust from './FinishTaskIllust';\nimport { commonKnobs } from '../../."
},
{
"path": "src/lv1/images/FinishTaskIllust.tsx",
"chars": 813,
"preview": "import lottie from 'lottie-web';\nimport * as React from 'react';\n\nimport animFinishTaskIllust from './finish-task-illust"
},
{
"path": "src/lv1/images/GooglePlayBadge.stories.tsx",
"chars": 274,
"preview": "import * as React from 'react';\n\nimport { commonKnobs } from '../../../stories';\nimport GooglePlayBadge from './GooglePl"
},
{
"path": "src/lv1/images/GooglePlayBadge.tsx",
"chars": 13459,
"preview": "import * as React from 'react';\nimport commonProps, { CommonProps } from '../../utilities/commonProps';\nimport { MarginC"
},
{
"path": "src/lv1/images/ImageUploadIllust.stories.tsx",
"chars": 284,
"preview": "import * as React from 'react';\n\nimport { commonKnobs } from '../../../stories';\nimport ImageUploadIllust from './ImageU"
},
{
"path": "src/lv1/images/ImageUploadIllust.tsx",
"chars": 1277,
"preview": "import * as React from 'react';\nimport commonProps, { CommonProps } from '../../utilities/commonProps';\nimport { MarginC"
},
{
"path": "src/lv1/images/NoDataIllust.stories.tsx",
"chars": 540,
"preview": "import * as React from 'react';\n\nimport { commonKnobs } from '../../../stories';\nimport { select } from '@storybook/addo"
},
{
"path": "src/lv1/images/NoDataIllust.tsx",
"chars": 5630,
"preview": "import * as React from 'react';\nimport { SwallowProps, SwallowContainer } from './SwallowContainer';\n\ntype Props = Swall"
},
{
"path": "src/lv1/images/NoSearchResultsIllust.stories.tsx",
"chars": 594,
"preview": "import * as React from 'react';\n\nimport { commonKnobs } from '../../../stories';\nimport { select } from '@storybook/addo"
},
{
"path": "src/lv1/images/NoSearchResultsIllust.tsx",
"chars": 9392,
"preview": "import * as React from 'react';\nimport { SwallowProps, SwallowContainer } from './SwallowContainer';\n\ntype Props = Swall"
}
]
// ... and 394 more files (download for full content)
About this extraction
This page contains the full source code of the freee/vibes GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 594 files (2.2 MB), approximately 603.5k tokens, and a symbol index with 441 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.