velvetkit 0.1.0.beta
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/CHANGELOG.md +16 -0
- data/LICENSE.txt +21 -0
- data/README.md +197 -0
- data/lib/generators/velvetkit/add/add_generator.rb +111 -0
- data/lib/generators/velvetkit/ai/ai_generator.rb +78 -0
- data/lib/generators/velvetkit/helpers.rb +64 -0
- data/lib/generators/velvetkit/install/install_generator.rb +108 -0
- data/lib/velvetkit/namespace.rb +57 -0
- data/lib/velvetkit/reference.rb +108 -0
- data/lib/velvetkit/registry.rb +162 -0
- data/lib/velvetkit/version.rb +5 -0
- data/lib/velvetkit.rb +28 -0
- data/registry/app/assets/tailwind/velvetkit.css +342 -0
- data/registry/app/components/ui/accordion/content.html.erb +5 -0
- data/registry/app/components/ui/accordion/content.rb +34 -0
- data/registry/app/components/ui/accordion/item.html.erb +1 -0
- data/registry/app/components/ui/accordion/item.rb +40 -0
- data/registry/app/components/ui/accordion/trigger.html.erb +1 -0
- data/registry/app/components/ui/accordion/trigger.rb +37 -0
- data/registry/app/components/ui/accordion.html.erb +7 -0
- data/registry/app/components/ui/accordion.rb +34 -0
- data/registry/app/components/ui/alert/description.html.erb +1 -0
- data/registry/app/components/ui/alert/description.rb +12 -0
- data/registry/app/components/ui/alert/title.html.erb +1 -0
- data/registry/app/components/ui/alert/title.rb +12 -0
- data/registry/app/components/ui/alert.html.erb +16 -0
- data/registry/app/components/ui/alert.rb +55 -0
- data/registry/app/components/ui/avatar/fallback.html.erb +1 -0
- data/registry/app/components/ui/avatar/fallback.rb +12 -0
- data/registry/app/components/ui/avatar/image.rb +20 -0
- data/registry/app/components/ui/avatar.html.erb +1 -0
- data/registry/app/components/ui/avatar.rb +56 -0
- data/registry/app/components/ui/avatar_group.html.erb +1 -0
- data/registry/app/components/ui/avatar_group.rb +35 -0
- data/registry/app/components/ui/badge.html.erb +1 -0
- data/registry/app/components/ui/badge.rb +38 -0
- data/registry/app/components/ui/banner.html.erb +10 -0
- data/registry/app/components/ui/banner.rb +47 -0
- data/registry/app/components/ui/base.rb +53 -0
- data/registry/app/components/ui/breadcrumb/auto_ellipsis.html.erb +23 -0
- data/registry/app/components/ui/breadcrumb/auto_ellipsis.rb +12 -0
- data/registry/app/components/ui/breadcrumb/ellipsis.html.erb +18 -0
- data/registry/app/components/ui/breadcrumb/ellipsis.rb +8 -0
- data/registry/app/components/ui/breadcrumb/item.html.erb +1 -0
- data/registry/app/components/ui/breadcrumb/item.rb +12 -0
- data/registry/app/components/ui/breadcrumb/item_slot.html.erb +10 -0
- data/registry/app/components/ui/breadcrumb/item_slot.rb +47 -0
- data/registry/app/components/ui/breadcrumb/list.html.erb +1 -0
- data/registry/app/components/ui/breadcrumb/list.rb +12 -0
- data/registry/app/components/ui/breadcrumb/separator.html.erb +1 -0
- data/registry/app/components/ui/breadcrumb/separator.rb +12 -0
- data/registry/app/components/ui/breadcrumb.html.erb +30 -0
- data/registry/app/components/ui/breadcrumb.rb +83 -0
- data/registry/app/components/ui/button.html.erb +1 -0
- data/registry/app/components/ui/button.rb +81 -0
- data/registry/app/components/ui/button_to.html.erb +1 -0
- data/registry/app/components/ui/button_to.rb +33 -0
- data/registry/app/components/ui/card/content.html.erb +1 -0
- data/registry/app/components/ui/card/content.rb +12 -0
- data/registry/app/components/ui/card/description.html.erb +1 -0
- data/registry/app/components/ui/card/description.rb +12 -0
- data/registry/app/components/ui/card/footer.html.erb +1 -0
- data/registry/app/components/ui/card/footer.rb +12 -0
- data/registry/app/components/ui/card/header.html.erb +1 -0
- data/registry/app/components/ui/card/header.rb +15 -0
- data/registry/app/components/ui/card/title.html.erb +1 -0
- data/registry/app/components/ui/card/title.rb +12 -0
- data/registry/app/components/ui/card.html.erb +1 -0
- data/registry/app/components/ui/card.rb +18 -0
- data/registry/app/components/ui/checkbox.html.erb +18 -0
- data/registry/app/components/ui/checkbox.rb +133 -0
- data/registry/app/components/ui/data_grid.html.erb +2 -0
- data/registry/app/components/ui/data_grid.rb +84 -0
- data/registry/app/components/ui/date_picker.html.erb +70 -0
- data/registry/app/components/ui/date_picker.rb +111 -0
- data/registry/app/components/ui/dialog/close.html.erb +7 -0
- data/registry/app/components/ui/dialog/close.rb +53 -0
- data/registry/app/components/ui/dialog/content.html.erb +26 -0
- data/registry/app/components/ui/dialog/content.rb +102 -0
- data/registry/app/components/ui/dialog/description.html.erb +1 -0
- data/registry/app/components/ui/dialog/description.rb +16 -0
- data/registry/app/components/ui/dialog/footer.html.erb +1 -0
- data/registry/app/components/ui/dialog/footer.rb +14 -0
- data/registry/app/components/ui/dialog/header.html.erb +1 -0
- data/registry/app/components/ui/dialog/header.rb +16 -0
- data/registry/app/components/ui/dialog/title.html.erb +1 -0
- data/registry/app/components/ui/dialog/title.rb +16 -0
- data/registry/app/components/ui/dialog/trigger.html.erb +5 -0
- data/registry/app/components/ui/dialog/trigger.rb +75 -0
- data/registry/app/components/ui/dialog.html.erb +3 -0
- data/registry/app/components/ui/dialog.rb +41 -0
- data/registry/app/components/ui/divider.html.erb +9 -0
- data/registry/app/components/ui/divider.rb +41 -0
- data/registry/app/components/ui/icon.html.erb +1 -0
- data/registry/app/components/ui/icon.rb +41 -0
- data/registry/app/components/ui/image.html.erb +1 -0
- data/registry/app/components/ui/image.rb +74 -0
- data/registry/app/components/ui/input.html.erb +9 -0
- data/registry/app/components/ui/input.rb +127 -0
- data/registry/app/components/ui/input_group/addon.html.erb +6 -0
- data/registry/app/components/ui/input_group/addon.rb +51 -0
- data/registry/app/components/ui/input_group/field.rb +26 -0
- data/registry/app/components/ui/input_group.html.erb +5 -0
- data/registry/app/components/ui/input_group.rb +55 -0
- data/registry/app/components/ui/label.html.erb +2 -0
- data/registry/app/components/ui/label.rb +48 -0
- data/registry/app/components/ui/link.html.erb +1 -0
- data/registry/app/components/ui/link.rb +64 -0
- data/registry/app/components/ui/menu/content.html.erb +5 -0
- data/registry/app/components/ui/menu/content.rb +106 -0
- data/registry/app/components/ui/menu/group.html.erb +1 -0
- data/registry/app/components/ui/menu/group.rb +19 -0
- data/registry/app/components/ui/menu/item.html.erb +1 -0
- data/registry/app/components/ui/menu/item.rb +68 -0
- data/registry/app/components/ui/menu/label.html.erb +1 -0
- data/registry/app/components/ui/menu/label.rb +33 -0
- data/registry/app/components/ui/menu/separator.html.erb +1 -0
- data/registry/app/components/ui/menu/separator.rb +20 -0
- data/registry/app/components/ui/menu/trigger.html.erb +1 -0
- data/registry/app/components/ui/menu/trigger.rb +29 -0
- data/registry/app/components/ui/menu.html.erb +3 -0
- data/registry/app/components/ui/menu.rb +23 -0
- data/registry/app/components/ui/native_select.html.erb +16 -0
- data/registry/app/components/ui/native_select.rb +109 -0
- data/registry/app/components/ui/pagination.html.erb +34 -0
- data/registry/app/components/ui/pagination.rb +62 -0
- data/registry/app/components/ui/password_input.html.erb +20 -0
- data/registry/app/components/ui/password_input.rb +94 -0
- data/registry/app/components/ui/popover/content.html.erb +12 -0
- data/registry/app/components/ui/popover/content.rb +83 -0
- data/registry/app/components/ui/popover/trigger.html.erb +1 -0
- data/registry/app/components/ui/popover/trigger.rb +29 -0
- data/registry/app/components/ui/popover.html.erb +3 -0
- data/registry/app/components/ui/popover.rb +24 -0
- data/registry/app/components/ui/radio_group/item.html.erb +18 -0
- data/registry/app/components/ui/radio_group/item.rb +97 -0
- data/registry/app/components/ui/radio_group.html.erb +1 -0
- data/registry/app/components/ui/radio_group.rb +35 -0
- data/registry/app/components/ui/select.html.erb +60 -0
- data/registry/app/components/ui/select.rb +181 -0
- data/registry/app/components/ui/sheet/close.rb +4 -0
- data/registry/app/components/ui/sheet/content.html.erb +24 -0
- data/registry/app/components/ui/sheet/content.rb +97 -0
- data/registry/app/components/ui/sheet/trigger.rb +4 -0
- data/registry/app/components/ui/sheet.html.erb +3 -0
- data/registry/app/components/ui/sheet.rb +37 -0
- data/registry/app/components/ui/sidebar/content.html.erb +1 -0
- data/registry/app/components/ui/sidebar/content.rb +18 -0
- data/registry/app/components/ui/sidebar/footer.html.erb +3 -0
- data/registry/app/components/ui/sidebar/footer.rb +16 -0
- data/registry/app/components/ui/sidebar/group.html.erb +1 -0
- data/registry/app/components/ui/sidebar/group.rb +19 -0
- data/registry/app/components/ui/sidebar/group_label.html.erb +1 -0
- data/registry/app/components/ui/sidebar/group_label.rb +18 -0
- data/registry/app/components/ui/sidebar/header.html.erb +1 -0
- data/registry/app/components/ui/sidebar/header.rb +16 -0
- data/registry/app/components/ui/sidebar/inset.html.erb +1 -0
- data/registry/app/components/ui/sidebar/inset.rb +17 -0
- data/registry/app/components/ui/sidebar/menu.html.erb +1 -0
- data/registry/app/components/ui/sidebar/menu.rb +18 -0
- data/registry/app/components/ui/sidebar/menu_button.html.erb +1 -0
- data/registry/app/components/ui/sidebar/menu_button.rb +30 -0
- data/registry/app/components/ui/sidebar/menu_item.html.erb +1 -0
- data/registry/app/components/ui/sidebar/menu_item.rb +23 -0
- data/registry/app/components/ui/sidebar/trigger.html.erb +1 -0
- data/registry/app/components/ui/sidebar/trigger.rb +24 -0
- data/registry/app/components/ui/sidebar/wrapper.html.erb +1 -0
- data/registry/app/components/ui/sidebar/wrapper.rb +40 -0
- data/registry/app/components/ui/sidebar.html.erb +11 -0
- data/registry/app/components/ui/sidebar.rb +29 -0
- data/registry/app/components/ui/spinner.html.erb +1 -0
- data/registry/app/components/ui/spinner.rb +20 -0
- data/registry/app/components/ui/switch.html.erb +12 -0
- data/registry/app/components/ui/switch.rb +109 -0
- data/registry/app/components/ui/table/cell.html.erb +1 -0
- data/registry/app/components/ui/table/cell.rb +16 -0
- data/registry/app/components/ui/table/column.html.erb +1 -0
- data/registry/app/components/ui/table/column.rb +18 -0
- data/registry/app/components/ui/table/head.html.erb +1 -0
- data/registry/app/components/ui/table/head.rb +13 -0
- data/registry/app/components/ui/table/row.html.erb +1 -0
- data/registry/app/components/ui/table/row.rb +26 -0
- data/registry/app/components/ui/table.html.erb +4 -0
- data/registry/app/components/ui/table.rb +45 -0
- data/registry/app/components/ui/tabs/content.html.erb +1 -0
- data/registry/app/components/ui/tabs/content.rb +43 -0
- data/registry/app/components/ui/tabs/list.html.erb +1 -0
- data/registry/app/components/ui/tabs/list.rb +28 -0
- data/registry/app/components/ui/tabs/trigger.html.erb +1 -0
- data/registry/app/components/ui/tabs/trigger.rb +91 -0
- data/registry/app/components/ui/tabs.html.erb +3 -0
- data/registry/app/components/ui/tabs.rb +27 -0
- data/registry/app/components/ui/text.html.erb +1 -0
- data/registry/app/components/ui/text.rb +48 -0
- data/registry/app/components/ui/theme_toggle.html.erb +9 -0
- data/registry/app/components/ui/theme_toggle.rb +63 -0
- data/registry/app/components/ui/tooltip/content.html.erb +1 -0
- data/registry/app/components/ui/tooltip/content.rb +65 -0
- data/registry/app/components/ui/tooltip/trigger.html.erb +1 -0
- data/registry/app/components/ui/tooltip/trigger.rb +33 -0
- data/registry/app/components/ui/tooltip.html.erb +3 -0
- data/registry/app/components/ui/tooltip.rb +24 -0
- data/registry/app/components/ui/typography/blockquote.html.erb +1 -0
- data/registry/app/components/ui/typography/blockquote.rb +15 -0
- data/registry/app/components/ui/typography/code.html.erb +1 -0
- data/registry/app/components/ui/typography/code.rb +25 -0
- data/registry/app/components/ui/typography/h1.rb +7 -0
- data/registry/app/components/ui/typography/h2.rb +7 -0
- data/registry/app/components/ui/typography/h3.rb +7 -0
- data/registry/app/components/ui/typography/h4.rb +7 -0
- data/registry/app/components/ui/typography/large.rb +7 -0
- data/registry/app/components/ui/typography/lead.rb +7 -0
- data/registry/app/components/ui/typography/muted.rb +7 -0
- data/registry/app/components/ui/typography/small.rb +7 -0
- data/registry/app/javascript/ag_grid.ts +26 -0
- data/registry/app/javascript/cally.ts +8 -0
- data/registry/app/javascript/controllers/ui/_internal/use_dismiss.ts +88 -0
- data/registry/app/javascript/controllers/ui/_internal/use_floating.ts +112 -0
- data/registry/app/javascript/controllers/ui/accordion_controller.ts +100 -0
- data/registry/app/javascript/controllers/ui/alert_controller.ts +7 -0
- data/registry/app/javascript/controllers/ui/banner_controller.ts +7 -0
- data/registry/app/javascript/controllers/ui/data_grid_controller.ts +198 -0
- data/registry/app/javascript/controllers/ui/date_picker_controller.ts +141 -0
- data/registry/app/javascript/controllers/ui/dialog_controller.ts +70 -0
- data/registry/app/javascript/controllers/ui/menu_controller.ts +201 -0
- data/registry/app/javascript/controllers/ui/password_visibility_controller.ts +43 -0
- data/registry/app/javascript/controllers/ui/popover_controller.ts +108 -0
- data/registry/app/javascript/controllers/ui/select_controller.ts +274 -0
- data/registry/app/javascript/controllers/ui/sidebar_controller.ts +64 -0
- data/registry/app/javascript/controllers/ui/tabs_controller.ts +74 -0
- data/registry/app/javascript/controllers/ui/theme_controller.ts +101 -0
- data/registry/app/javascript/controllers/ui/tooltip_controller.ts +102 -0
- data/registry/app/javascript/velvetkit_icons.ts +58 -0
- data/registry/app/views/layouts/_theme_script.html.erb +19 -0
- data/registry/components.yml +102 -0
- data/registry/config/locales/velvetkit.en.yml +56 -0
- data/registry/docs/agents.md +62 -0
- data/registry/test/components/previews/ui/accordion_preview/faq_style.html.erb +20 -0
- data/registry/test/components/previews/ui/accordion_preview/multiple.html.erb +27 -0
- data/registry/test/components/previews/ui/accordion_preview/single.html.erb +22 -0
- data/registry/test/components/previews/ui/accordion_preview.rb +31 -0
- data/registry/test/components/previews/ui/alert_preview/title_only.html.erb +12 -0
- data/registry/test/components/previews/ui/alert_preview/variants.html.erb +22 -0
- data/registry/test/components/previews/ui/alert_preview/with_icon.html.erb +12 -0
- data/registry/test/components/previews/ui/alert_preview/with_link.html.erb +18 -0
- data/registry/test/components/previews/ui/alert_preview.rb +34 -0
- data/registry/test/components/previews/ui/avatar_group_preview/default.html.erb +11 -0
- data/registry/test/components/previews/ui/avatar_group_preview/overflow.html.erb +23 -0
- data/registry/test/components/previews/ui/avatar_group_preview/small.html.erb +12 -0
- data/registry/test/components/previews/ui/avatar_group_preview.rb +23 -0
- data/registry/test/components/previews/ui/avatar_preview/colors.html.erb +8 -0
- data/registry/test/components/previews/ui/avatar_preview/shapes.html.erb +10 -0
- data/registry/test/components/previews/ui/avatar_preview/sizes.html.erb +8 -0
- data/registry/test/components/previews/ui/avatar_preview/with_image.html.erb +10 -0
- data/registry/test/components/previews/ui/avatar_preview.rb +24 -0
- data/registry/test/components/previews/ui/badge_preview/sizes.html.erb +5 -0
- data/registry/test/components/previews/ui/badge_preview/variants.html.erb +8 -0
- data/registry/test/components/previews/ui/badge_preview/with_icon.html.erb +14 -0
- data/registry/test/components/previews/ui/badge_preview.rb +20 -0
- data/registry/test/components/previews/ui/banner_preview/introduction.html.erb +9 -0
- data/registry/test/components/previews/ui/banner_preview/promotion.html.erb +9 -0
- data/registry/test/components/previews/ui/banner_preview/with_illustration.html.erb +12 -0
- data/registry/test/components/previews/ui/banner_preview.rb +25 -0
- data/registry/test/components/previews/ui/breadcrumb_preview/collapsed.html.erb +50 -0
- data/registry/test/components/previews/ui/breadcrumb_preview/default.html.erb +8 -0
- data/registry/test/components/previews/ui/breadcrumb_preview/long_path.html.erb +8 -0
- data/registry/test/components/previews/ui/breadcrumb_preview/medium.html.erb +7 -0
- data/registry/test/components/previews/ui/breadcrumb_preview/with_avatar.html.erb +8 -0
- data/registry/test/components/previews/ui/breadcrumb_preview.rb +66 -0
- data/registry/test/components/previews/ui/button_preview/disabled.html.erb +39 -0
- data/registry/test/components/previews/ui/button_preview/icon_only.html.erb +17 -0
- data/registry/test/components/previews/ui/button_preview/loading.html.erb +12 -0
- data/registry/test/components/previews/ui/button_preview/sizes.html.erb +6 -0
- data/registry/test/components/previews/ui/button_preview/variants.html.erb +8 -0
- data/registry/test/components/previews/ui/button_preview/with_both_icons.html.erb +12 -0
- data/registry/test/components/previews/ui/button_preview/with_leading_icon.html.erb +22 -0
- data/registry/test/components/previews/ui/button_preview/with_trailing_icon.html.erb +14 -0
- data/registry/test/components/previews/ui/button_preview.rb +65 -0
- data/registry/test/components/previews/ui/button_to_preview/states.html.erb +8 -0
- data/registry/test/components/previews/ui/button_to_preview/variants.html.erb +5 -0
- data/registry/test/components/previews/ui/button_to_preview/with_icon.html.erb +11 -0
- data/registry/test/components/previews/ui/button_to_preview.rb +21 -0
- data/registry/test/components/previews/ui/card_preview/complete.html.erb +31 -0
- data/registry/test/components/previews/ui/card_preview/default.html.erb +7 -0
- data/registry/test/components/previews/ui/card_preview/settings_form.html.erb +25 -0
- data/registry/test/components/previews/ui/card_preview/with_footer.html.erb +13 -0
- data/registry/test/components/previews/ui/card_preview/with_header.html.erb +11 -0
- data/registry/test/components/previews/ui/card_preview.rb +44 -0
- data/registry/test/components/previews/ui/checkbox_preview/in_form.html.erb +9 -0
- data/registry/test/components/previews/ui/checkbox_preview/states.html.erb +22 -0
- data/registry/test/components/previews/ui/checkbox_preview/with_label.html.erb +5 -0
- data/registry/test/components/previews/ui/checkbox_preview.rb +27 -0
- data/registry/test/components/previews/ui/data_grid_preview.rb +18 -0
- data/registry/test/components/previews/ui/date_picker_preview/default.html.erb +3 -0
- data/registry/test/components/previews/ui/date_picker_preview/states.html.erb +7 -0
- data/registry/test/components/previews/ui/date_picker_preview.rb +13 -0
- data/registry/test/components/previews/ui/dialog_preview/confirmation.html.erb +11 -0
- data/registry/test/components/previews/ui/dialog_preview/custom_trigger.html.erb +16 -0
- data/registry/test/components/previews/ui/dialog_preview/default.html.erb +15 -0
- data/registry/test/components/previews/ui/dialog_preview/long_content.html.erb +16 -0
- data/registry/test/components/previews/ui/dialog_preview/playground.html.erb +12 -0
- data/registry/test/components/previews/ui/dialog_preview/scrollable_body.html.erb +29 -0
- data/registry/test/components/previews/ui/dialog_preview/sizes.html.erb +46 -0
- data/registry/test/components/previews/ui/dialog_preview/without_close_button.html.erb +11 -0
- data/registry/test/components/previews/ui/dialog_preview/without_title.html.erb +13 -0
- data/registry/test/components/previews/ui/dialog_preview.rb +38 -0
- data/registry/test/components/previews/ui/divider_preview/horizontal.html.erb +5 -0
- data/registry/test/components/previews/ui/divider_preview/vertical.html.erb +9 -0
- data/registry/test/components/previews/ui/divider_preview/with_label.html.erb +5 -0
- data/registry/test/components/previews/ui/divider_preview.rb +19 -0
- data/registry/test/components/previews/ui/icon_preview/common.html.erb +8 -0
- data/registry/test/components/previews/ui/icon_preview/sizes.html.erb +7 -0
- data/registry/test/components/previews/ui/icon_preview.rb +20 -0
- data/registry/test/components/previews/ui/image_preview/radii.html.erb +8 -0
- data/registry/test/components/previews/ui/image_preview/sizing.html.erb +9 -0
- data/registry/test/components/previews/ui/image_preview.rb +16 -0
- data/registry/test/components/previews/ui/input_group_preview/addons.html.erb +29 -0
- data/registry/test/components/previews/ui/input_group_preview/playground.html.erb +12 -0
- data/registry/test/components/previews/ui/input_group_preview.rb +17 -0
- data/registry/test/components/previews/ui/input_preview/sizes.html.erb +5 -0
- data/registry/test/components/previews/ui/input_preview/states.html.erb +7 -0
- data/registry/test/components/previews/ui/input_preview/with_labels.html.erb +10 -0
- data/registry/test/components/previews/ui/input_preview.rb +33 -0
- data/registry/test/components/previews/ui/label_preview/sizes.html.erb +6 -0
- data/registry/test/components/previews/ui/label_preview/states.html.erb +6 -0
- data/registry/test/components/previews/ui/label_preview/weights.html.erb +5 -0
- data/registry/test/components/previews/ui/label_preview.rb +22 -0
- data/registry/test/components/previews/ui/link_preview/sizes.html.erb +5 -0
- data/registry/test/components/previews/ui/link_preview/variants.html.erb +8 -0
- data/registry/test/components/previews/ui/link_preview/with_icons.html.erb +11 -0
- data/registry/test/components/previews/ui/link_preview.rb +20 -0
- data/registry/test/components/previews/ui/menu_preview/positions.html.erb +122 -0
- data/registry/test/components/previews/ui/menu_preview/variants.html.erb +218 -0
- data/registry/test/components/previews/ui/menu_preview.rb +26 -0
- data/registry/test/components/previews/ui/native_select_preview/sizes.html.erb +6 -0
- data/registry/test/components/previews/ui/native_select_preview/states.html.erb +6 -0
- data/registry/test/components/previews/ui/native_select_preview.rb +23 -0
- data/registry/test/components/previews/ui/pagination_preview/states.html.erb +15 -0
- data/registry/test/components/previews/ui/pagination_preview.rb +15 -0
- data/registry/test/components/previews/ui/password_input_preview/states.html.erb +19 -0
- data/registry/test/components/previews/ui/password_input_preview.rb +21 -0
- data/registry/test/components/previews/ui/popover_preview/default.html.erb +7 -0
- data/registry/test/components/previews/ui/popover_preview/positions.html.erb +30 -0
- data/registry/test/components/previews/ui/popover_preview/with_close_button.html.erb +8 -0
- data/registry/test/components/previews/ui/popover_preview/with_rich_content.html.erb +17 -0
- data/registry/test/components/previews/ui/popover_preview/without_title.html.erb +9 -0
- data/registry/test/components/previews/ui/popover_preview.rb +32 -0
- data/registry/test/components/previews/ui/radio_group_preview/default.html.erb +10 -0
- data/registry/test/components/previews/ui/radio_group_preview/disabled.html.erb +17 -0
- data/registry/test/components/previews/ui/radio_group_preview/with_required.html.erb +10 -0
- data/registry/test/components/previews/ui/radio_group_preview.rb +25 -0
- data/registry/test/components/previews/ui/select_preview/default.html.erb +4 -0
- data/registry/test/components/previews/ui/select_preview/long_list.html.erb +5 -0
- data/registry/test/components/previews/ui/select_preview/multiple.html.erb +6 -0
- data/registry/test/components/previews/ui/select_preview/sizes.html.erb +5 -0
- data/registry/test/components/previews/ui/select_preview/states.html.erb +6 -0
- data/registry/test/components/previews/ui/select_preview.rb +45 -0
- data/registry/test/components/previews/ui/sheet_preview/bottom.html.erb +12 -0
- data/registry/test/components/previews/ui/sheet_preview/left.html.erb +10 -0
- data/registry/test/components/previews/ui/sheet_preview/playground.html.erb +15 -0
- data/registry/test/components/previews/ui/sheet_preview/right.html.erb +11 -0
- data/registry/test/components/previews/ui/sheet_preview/top.html.erb +8 -0
- data/registry/test/components/previews/ui/sheet_preview.rb +22 -0
- data/registry/test/components/previews/ui/sidebar_preview/_brand.html.erb +14 -0
- data/registry/test/components/previews/ui/sidebar_preview/_shell.html.erb +60 -0
- data/registry/test/components/previews/ui/sidebar_preview/collapsible.html.erb +55 -0
- data/registry/test/components/previews/ui/sidebar_preview/default.html.erb +17 -0
- data/registry/test/components/previews/ui/sidebar_preview/with_dropdown_header.html.erb +33 -0
- data/registry/test/components/previews/ui/sidebar_preview/with_groups.html.erb +22 -0
- data/registry/test/components/previews/ui/sidebar_preview.rb +15 -0
- data/registry/test/components/previews/ui/spinner_preview/in_buttons.html.erb +7 -0
- data/registry/test/components/previews/ui/spinner_preview/sizes.html.erb +7 -0
- data/registry/test/components/previews/ui/spinner_preview.rb +19 -0
- data/registry/test/components/previews/ui/switch_preview/default.html.erb +4 -0
- data/registry/test/components/previews/ui/switch_preview/sizes.html.erb +5 -0
- data/registry/test/components/previews/ui/switch_preview/states.html.erb +7 -0
- data/registry/test/components/previews/ui/switch_preview.rb +26 -0
- data/registry/test/components/previews/ui/table_preview/customers.html.erb +15 -0
- data/registry/test/components/previews/ui/table_preview/empty.html.erb +8 -0
- data/registry/test/components/previews/ui/table_preview/with_caption.html.erb +14 -0
- data/registry/test/components/previews/ui/table_preview.rb +12 -0
- data/registry/test/components/previews/ui/tabs_preview/default.html.erb +39 -0
- data/registry/test/components/previews/ui/tabs_preview/pill_variant.html.erb +18 -0
- data/registry/test/components/previews/ui/tabs_preview/settings_form.html.erb +42 -0
- data/registry/test/components/previews/ui/tabs_preview/with_icons.html.erb +34 -0
- data/registry/test/components/previews/ui/tabs_preview.rb +33 -0
- data/registry/test/components/previews/ui/text_preview/sizes.html.erb +10 -0
- data/registry/test/components/previews/ui/text_preview/tones.html.erb +5 -0
- data/registry/test/components/previews/ui/text_preview/weights.html.erb +6 -0
- data/registry/test/components/previews/ui/text_preview.rb +26 -0
- data/registry/test/components/previews/ui/theme_toggle_preview/in_a_header.html.erb +14 -0
- data/registry/test/components/previews/ui/theme_toggle_preview/palette.html.erb +47 -0
- data/registry/test/components/previews/ui/theme_toggle_preview.rb +15 -0
- data/registry/test/components/previews/ui/tooltip_preview/default.html.erb +8 -0
- data/registry/test/components/previews/ui/tooltip_preview/positions.html.erb +34 -0
- data/registry/test/components/previews/ui/tooltip_preview/with_button.html.erb +8 -0
- data/registry/test/components/previews/ui/tooltip_preview/with_icon.html.erb +10 -0
- data/registry/test/components/previews/ui/tooltip_preview.rb +29 -0
- data/registry/test/components/previews/ui/typography_preview/article.html.erb +11 -0
- data/registry/test/components/previews/ui/typography_preview/code_and_quotes.html.erb +6 -0
- data/registry/test/components/previews/ui/typography_preview/headings.html.erb +6 -0
- data/registry/test/components/previews/ui/typography_preview/text_styles.html.erb +7 -0
- data/registry/test/components/previews/ui/typography_preview.rb +23 -0
- data/registry/test/components/ui/accordion/content_test.rb +64 -0
- data/registry/test/components/ui/accordion/item_test.rb +61 -0
- data/registry/test/components/ui/accordion/trigger_test.rb +80 -0
- data/registry/test/components/ui/accordion_test.rb +96 -0
- data/registry/test/components/ui/alert/description_test.rb +37 -0
- data/registry/test/components/ui/alert/title_test.rb +38 -0
- data/registry/test/components/ui/alert_test.rb +184 -0
- data/registry/test/components/ui/avatar_group_test.rb +79 -0
- data/registry/test/components/ui/avatar_test.rb +129 -0
- data/registry/test/components/ui/badge_test.rb +112 -0
- data/registry/test/components/ui/banner_test.rb +116 -0
- data/registry/test/components/ui/base_test.rb +98 -0
- data/registry/test/components/ui/breadcrumb/ellipsis_test.rb +51 -0
- data/registry/test/components/ui/breadcrumb/item_test.rb +39 -0
- data/registry/test/components/ui/breadcrumb/list_test.rb +34 -0
- data/registry/test/components/ui/breadcrumb/separator_test.rb +36 -0
- data/registry/test/components/ui/breadcrumb_test.rb +276 -0
- data/registry/test/components/ui/button_test.rb +276 -0
- data/registry/test/components/ui/button_to_test.rb +74 -0
- data/registry/test/components/ui/card/content_test.rb +41 -0
- data/registry/test/components/ui/card/description_test.rb +32 -0
- data/registry/test/components/ui/card/footer_test.rb +34 -0
- data/registry/test/components/ui/card/header_test.rb +57 -0
- data/registry/test/components/ui/card/title_test.rb +34 -0
- data/registry/test/components/ui/card_test.rb +67 -0
- data/registry/test/components/ui/checkbox_test.rb +236 -0
- data/registry/test/components/ui/data_grid_test.rb +88 -0
- data/registry/test/components/ui/date_picker_test.rb +168 -0
- data/registry/test/components/ui/dialog/content_test.rb +156 -0
- data/registry/test/components/ui/dialog/trigger_test.rb +74 -0
- data/registry/test/components/ui/dialog_test.rb +54 -0
- data/registry/test/components/ui/divider_test.rb +69 -0
- data/registry/test/components/ui/icon_test.rb +82 -0
- data/registry/test/components/ui/image_test.rb +74 -0
- data/registry/test/components/ui/input_group_test.rb +143 -0
- data/registry/test/components/ui/input_test.rb +251 -0
- data/registry/test/components/ui/label_test.rb +96 -0
- data/registry/test/components/ui/link_test.rb +95 -0
- data/registry/test/components/ui/menu/content_test.rb +233 -0
- data/registry/test/components/ui/menu/group_test.rb +24 -0
- data/registry/test/components/ui/menu/item_test.rb +76 -0
- data/registry/test/components/ui/menu/label_test.rb +43 -0
- data/registry/test/components/ui/menu/separator_test.rb +32 -0
- data/registry/test/components/ui/menu/trigger_test.rb +109 -0
- data/registry/test/components/ui/menu_test.rb +152 -0
- data/registry/test/components/ui/native_select_test.rb +118 -0
- data/registry/test/components/ui/pagination_test.rb +92 -0
- data/registry/test/components/ui/password_input_test.rb +98 -0
- data/registry/test/components/ui/popover/content_test.rb +226 -0
- data/registry/test/components/ui/popover/trigger_test.rb +83 -0
- data/registry/test/components/ui/popover_test.rb +101 -0
- data/registry/test/components/ui/radio_group/item_test.rb +14 -0
- data/registry/test/components/ui/radio_group_test.rb +139 -0
- data/registry/test/components/ui/select_test.rb +181 -0
- data/registry/test/components/ui/sheet/content_test.rb +135 -0
- data/registry/test/components/ui/sheet/trigger_test.rb +23 -0
- data/registry/test/components/ui/sheet_test.rb +43 -0
- data/registry/test/components/ui/sidebar/menu_button_test.rb +43 -0
- data/registry/test/components/ui/sidebar/trigger_test.rb +17 -0
- data/registry/test/components/ui/sidebar/wrapper_test.rb +15 -0
- data/registry/test/components/ui/sidebar_test.rb +43 -0
- data/registry/test/components/ui/spinner_test.rb +63 -0
- data/registry/test/components/ui/switch_test.rb +206 -0
- data/registry/test/components/ui/table_test.rb +106 -0
- data/registry/test/components/ui/tabs/content_test.rb +93 -0
- data/registry/test/components/ui/tabs/list_test.rb +65 -0
- data/registry/test/components/ui/tabs/trigger_test.rb +159 -0
- data/registry/test/components/ui/tabs_test.rb +88 -0
- data/registry/test/components/ui/text_test.rb +84 -0
- data/registry/test/components/ui/theme_toggle_test.rb +88 -0
- data/registry/test/components/ui/tooltip/content_test.rb +135 -0
- data/registry/test/components/ui/tooltip/trigger_test.rb +52 -0
- data/registry/test/components/ui/tooltip_test.rb +97 -0
- data/registry/test/components/ui/typography/code_blockquote_test.rb +57 -0
- data/registry/test/components/ui/typography/heading_test.rb +71 -0
- data/registry/test/components/ui/typography/text_variants_test.rb +71 -0
- metadata +540 -0
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "test_helper"
|
|
4
|
+
|
|
5
|
+
class UI::SwitchTest < ViewComponent::TestCase
|
|
6
|
+
def setup_fixtures(*); end
|
|
7
|
+
def teardown_fixtures(*); end
|
|
8
|
+
|
|
9
|
+
# ── Input element ────────────────────────────────────────────────────────────
|
|
10
|
+
|
|
11
|
+
def test_renders_input_with_role_switch
|
|
12
|
+
render_inline(UI::Switch.new(name: "notify"))
|
|
13
|
+
input = page.find("input[type='checkbox']", visible: :all)
|
|
14
|
+
assert_equal "switch", input[:role]
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def test_input_is_visually_hidden_via_sr_only
|
|
18
|
+
render_inline(UI::Switch.new(name: "notify"))
|
|
19
|
+
classes = page.find("input[type='checkbox']", visible: :all)[:class].split
|
|
20
|
+
assert_includes classes, "peer"
|
|
21
|
+
assert_includes classes, "sr-only"
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def test_input_does_not_carry_track_classes
|
|
25
|
+
# Native browser tick painted whenever <input type=checkbox> stays in flow
|
|
26
|
+
# — we keep it sr-only so no track/visual classes belong on the input.
|
|
27
|
+
render_inline(UI::Switch.new(name: "notify"))
|
|
28
|
+
classes = page.find("input[type='checkbox']", visible: :all)[:class].split
|
|
29
|
+
refute_includes classes, "rounded-full"
|
|
30
|
+
refute_includes classes, "bg-input"
|
|
31
|
+
refute_includes classes, "appearance-none"
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def test_checked_sets_checked_attribute
|
|
35
|
+
render_inline(UI::Switch.new(name: "notify", checked: true))
|
|
36
|
+
input = page.find("input[type='checkbox']", visible: :all)
|
|
37
|
+
assert input["checked"].present?
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def test_value_defaults_to_one
|
|
41
|
+
render_inline(UI::Switch.new(name: "notify"))
|
|
42
|
+
assert_equal "1", page.find("input[type='checkbox']", visible: :all)[:value]
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def test_custom_value
|
|
46
|
+
render_inline(UI::Switch.new(name: "notify", value: "on"))
|
|
47
|
+
assert_equal "on", page.find("input[type='checkbox']", visible: :all)[:value]
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def test_disabled_sets_disabled_attribute
|
|
51
|
+
render_inline(UI::Switch.new(name: "notify", disabled: true))
|
|
52
|
+
input = page.find("input[type='checkbox'][disabled]", visible: :all)
|
|
53
|
+
assert input.disabled?
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def test_required_sets_required_attribute
|
|
57
|
+
render_inline(UI::Switch.new(name: "notify", required: true))
|
|
58
|
+
assert_selector "input[type='checkbox'][required]", visible: :all
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def test_data_attributes_passthrough
|
|
62
|
+
render_inline(UI::Switch.new(name: "notify", data: { test_id: "sw-x" }))
|
|
63
|
+
assert_selector "input[type='checkbox'][data-test-id='sw-x']", visible: :all
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def test_auto_generated_id_when_label_present
|
|
67
|
+
render_inline(UI::Switch.new(name: "notify", label: "Notify"))
|
|
68
|
+
input = page.find("input[type='checkbox']", visible: :all)
|
|
69
|
+
assert input[:id].present?
|
|
70
|
+
assert input[:id].start_with?("ui-sw-")
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
# ── Track (sibling span) ─────────────────────────────────────────────────────
|
|
74
|
+
|
|
75
|
+
def test_track_span_uses_peer_checked_for_state
|
|
76
|
+
render_inline(UI::Switch.new(name: "notify"))
|
|
77
|
+
track = page.all("span", visible: :all).find { |s|
|
|
78
|
+
s[:class].to_s.include?("peer-checked:bg-primary")
|
|
79
|
+
}
|
|
80
|
+
assert track, "track span with peer-checked:bg-primary should exist"
|
|
81
|
+
classes = track[:class].split
|
|
82
|
+
assert_includes classes, "rounded-full"
|
|
83
|
+
assert_includes classes, "bg-input"
|
|
84
|
+
assert_includes classes, "peer-checked:bg-primary"
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def test_track_span_has_focus_ring_via_peer_focus_visible
|
|
88
|
+
render_inline(UI::Switch.new(name: "notify"))
|
|
89
|
+
track = page.all("span", visible: :all).find { |s|
|
|
90
|
+
s[:class].to_s.include?("peer-focus:ring-2")
|
|
91
|
+
}
|
|
92
|
+
assert track, "track span with peer-focus:ring-2 should exist"
|
|
93
|
+
classes = track[:class].split
|
|
94
|
+
assert_includes classes, "peer-focus:ring-primary"
|
|
95
|
+
assert_includes classes, "peer-focus:ring-offset-1"
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
# ── Thumb ────────────────────────────────────────────────────────────────────
|
|
99
|
+
|
|
100
|
+
def test_thumb_span_present_with_default_translate_class
|
|
101
|
+
render_inline(UI::Switch.new(name: "notify"))
|
|
102
|
+
thumb = page.all("span", visible: :all).find { |s|
|
|
103
|
+
s[:class].to_s.include?("peer-checked:translate-x-")
|
|
104
|
+
}
|
|
105
|
+
assert thumb, "thumb span should exist"
|
|
106
|
+
classes = thumb[:class].split
|
|
107
|
+
assert_includes classes, "rounded-full"
|
|
108
|
+
assert_includes classes, "bg-white"
|
|
109
|
+
assert_includes classes, "peer-checked:translate-x-5"
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
# ── Sizes ────────────────────────────────────────────────────────────────────
|
|
113
|
+
|
|
114
|
+
def test_size_sm_renders_smaller_track_and_thumb
|
|
115
|
+
render_inline(UI::Switch.new(name: "notify", size: :sm))
|
|
116
|
+
spans = page.all("span", visible: :all)
|
|
117
|
+
root = spans.find { |s| s[:class].to_s.include?("inline-flex shrink-0") }
|
|
118
|
+
assert_includes root[:class].split, "w-8"
|
|
119
|
+
assert_includes root[:class].split, "h-4"
|
|
120
|
+
thumb = spans.find { |s| s[:class].to_s.include?("peer-checked:translate-x-") }
|
|
121
|
+
assert_includes thumb[:class].split, "w-3"
|
|
122
|
+
assert_includes thumb[:class].split, "h-3"
|
|
123
|
+
assert_includes thumb[:class].split, "peer-checked:translate-x-4"
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def test_size_lg_renders_larger_track_and_thumb
|
|
127
|
+
render_inline(UI::Switch.new(name: "notify", size: :lg))
|
|
128
|
+
spans = page.all("span", visible: :all)
|
|
129
|
+
root = spans.find { |s| s[:class].to_s.include?("inline-flex shrink-0") }
|
|
130
|
+
assert_includes root[:class].split, "w-12"
|
|
131
|
+
assert_includes root[:class].split, "h-6"
|
|
132
|
+
thumb = spans.find { |s| s[:class].to_s.include?("peer-checked:translate-x-") }
|
|
133
|
+
assert_includes thumb[:class].split, "w-5"
|
|
134
|
+
assert_includes thumb[:class].split, "h-5"
|
|
135
|
+
assert_includes thumb[:class].split, "peer-checked:translate-x-6"
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
# ── Wrapper / label ──────────────────────────────────────────────────────────
|
|
139
|
+
|
|
140
|
+
def test_label_renders_label_element_with_for_id
|
|
141
|
+
render_inline(UI::Switch.new(name: "notify", label: "Enable notifications"))
|
|
142
|
+
label_el = page.find("label")
|
|
143
|
+
input_el = page.find("input[type='checkbox']", visible: :all)
|
|
144
|
+
assert_equal label_el["for"], input_el[:id]
|
|
145
|
+
assert_match(/Enable notifications/, label_el.text)
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
# The checkbox is visually hidden, so without a <label> around it a click on
|
|
149
|
+
# the track reaches nothing and the switch can't be flipped by mouse.
|
|
150
|
+
def test_without_visible_label_the_switch_is_still_a_clickable_label
|
|
151
|
+
render_inline(UI::Switch.new(name: "notify", aria: { label: "Notifications" }))
|
|
152
|
+
label_el = page.find("label")
|
|
153
|
+
assert label_el.has_css?("input[type='checkbox']", visible: :all)
|
|
154
|
+
assert_empty label_el.text.strip
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
# White in both themes: in dark mode the background token is near black,
|
|
158
|
+
# which made the knob vanish into the track.
|
|
159
|
+
def test_thumb_is_white_in_both_themes
|
|
160
|
+
render_inline(UI::Switch.new(name: "notify"))
|
|
161
|
+
thumb = page.all("span", visible: :all).find { |s| s[:class].to_s.include?("rounded-full") && s[:class].to_s.include?("shadow") }
|
|
162
|
+
assert_includes thumb[:class].split, "bg-white"
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
def test_disabled_wrapper_dims
|
|
166
|
+
render_inline(UI::Switch.new(name: "notify", disabled: true, label: "Notifications"))
|
|
167
|
+
classes = page.find("label")[:class].split
|
|
168
|
+
assert_includes classes, "cursor-not-allowed"
|
|
169
|
+
assert_includes classes, "opacity-60"
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
def test_required_label_shows_styled_asterisk
|
|
173
|
+
render_inline(UI::Switch.new(name: "notify", label: "Sub", required: true))
|
|
174
|
+
asterisk = page.find("label span.text-destructive")
|
|
175
|
+
assert_match(/\*/, asterisk.text)
|
|
176
|
+
classes = asterisk[:class].split
|
|
177
|
+
assert_includes classes, "text-destructive"
|
|
178
|
+
assert_includes classes, "ml-0.5"
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
def test_non_required_label_has_no_destructive_span
|
|
182
|
+
render_inline(UI::Switch.new(name: "notify", label: "Sub"))
|
|
183
|
+
refute_selector "label span.text-destructive"
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
def test_label_text_renders_as_span_with_typography
|
|
187
|
+
render_inline(UI::Switch.new(name: "notify", label: "Toggle"))
|
|
188
|
+
span = page.find("label").all("span").find { |s| s.text.strip.start_with?("Toggle") }
|
|
189
|
+
assert span, "label text span should exist"
|
|
190
|
+
classes = span[:class].split
|
|
191
|
+
assert_includes classes, "text-sm"
|
|
192
|
+
assert_includes classes, "leading-none"
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
# ── User-class merge ────────────────────────────────────────────────────────
|
|
196
|
+
|
|
197
|
+
def test_user_class_lands_on_track_not_input
|
|
198
|
+
render_inline(UI::Switch.new(name: "notify", class: "ring-orange-500"))
|
|
199
|
+
track = page.all("span", visible: :all).find { |s|
|
|
200
|
+
s[:class].to_s.include?("peer-checked:bg-primary")
|
|
201
|
+
}
|
|
202
|
+
assert_includes track[:class].split, "ring-orange-500"
|
|
203
|
+
input_classes = page.find("input[type='checkbox']", visible: :all)[:class].split
|
|
204
|
+
refute_includes input_classes, "ring-orange-500"
|
|
205
|
+
end
|
|
206
|
+
end
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "test_helper"
|
|
4
|
+
|
|
5
|
+
class UI::TableTest < ViewComponent::TestCase
|
|
6
|
+
def setup_fixtures(*); end
|
|
7
|
+
def teardown_fixtures(*); end
|
|
8
|
+
|
|
9
|
+
def render_table
|
|
10
|
+
render_inline(UI::Table.new) do |table|
|
|
11
|
+
table.with_head do |head|
|
|
12
|
+
head.with_column { "Name" }
|
|
13
|
+
head.with_column(align: :end) { "Amount" }
|
|
14
|
+
end
|
|
15
|
+
table.with_row do |row|
|
|
16
|
+
row.with_cell { "Aditi Rao" }
|
|
17
|
+
row.with_cell(align: :end) { "12" }
|
|
18
|
+
end
|
|
19
|
+
table.with_row do |row|
|
|
20
|
+
row.with_cell { "Arjun Nair" }
|
|
21
|
+
row.with_cell(align: :end) { "13" }
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def test_renders_a_semantic_table
|
|
27
|
+
render_table
|
|
28
|
+
assert_selector "table thead tr th", count: 2
|
|
29
|
+
assert_selector "table tbody tr", count: 2
|
|
30
|
+
assert_selector "table tbody tr:first-child td", text: "Aditi Rao"
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# Screen readers announce a header cell differently from a data cell, so the
|
|
34
|
+
# distinction has to be real markup rather than styling.
|
|
35
|
+
def test_header_cells_are_th_with_a_scope
|
|
36
|
+
render_table
|
|
37
|
+
assert_equal "col", page.find("thead th", text: "Name")[:scope]
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# A long table scrolls sideways on a phone rather than breaking the page.
|
|
41
|
+
def test_wraps_the_table_in_a_horizontal_scroll_container
|
|
42
|
+
render_table
|
|
43
|
+
assert_includes page.find("div", match: :first)[:class].split, "overflow-x-auto"
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def test_alignment_applies_to_matching_head_and_body_cells
|
|
47
|
+
render_table
|
|
48
|
+
assert_includes page.find("thead th", text: "Amount")[:class].split, "text-right"
|
|
49
|
+
assert_includes page.find("tbody td", text: "12")[:class].split, "text-right"
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def test_caption_is_rendered_when_given
|
|
53
|
+
render_inline(UI::Table.new(caption: "Invoices")) do |table|
|
|
54
|
+
table.with_row { |row| row.with_cell { "x" } }
|
|
55
|
+
end
|
|
56
|
+
assert_selector "table caption", text: "Invoices"
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def test_no_caption_element_without_one
|
|
60
|
+
render_table
|
|
61
|
+
assert_no_selector "caption"
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# An empty table should say so rather than render a bare header.
|
|
65
|
+
def test_empty_state_replaces_the_body_when_there_are_no_rows
|
|
66
|
+
render_inline(UI::Table.new) do |table|
|
|
67
|
+
table.with_head { |head| head.with_column { "Name" } }
|
|
68
|
+
table.with_empty_state { "No invoices yet" }
|
|
69
|
+
end
|
|
70
|
+
assert_text "No invoices yet"
|
|
71
|
+
assert_no_selector "tbody tr td", text: "Aditi"
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def test_empty_state_is_ignored_once_rows_exist
|
|
75
|
+
render_inline(UI::Table.new) do |table|
|
|
76
|
+
table.with_head { |head| head.with_column { "Name" } }
|
|
77
|
+
table.with_empty_state { "No invoices yet" }
|
|
78
|
+
table.with_row { |row| row.with_cell { "Aditi Rao" } }
|
|
79
|
+
end
|
|
80
|
+
assert_no_text "No invoices yet"
|
|
81
|
+
assert_selector "tbody td", text: "Aditi Rao"
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def test_rows_can_be_marked_selected
|
|
85
|
+
render_inline(UI::Table.new) do |table|
|
|
86
|
+
table.with_row(selected: true) { |row| row.with_cell { "Aditi" } }
|
|
87
|
+
end
|
|
88
|
+
assert_equal "true", page.find("tbody tr")[:"aria-selected"]
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def test_user_class_overrides_default_via_tailwind_merge
|
|
92
|
+
render_inline(UI::Table.new(class: "text-base")) do |table|
|
|
93
|
+
table.with_row { |row| row.with_cell { "x" } }
|
|
94
|
+
end
|
|
95
|
+
classes = page.find("table")[:class].split
|
|
96
|
+
assert_includes classes, "text-base"
|
|
97
|
+
refute_includes classes, "text-sm"
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def test_html_attrs_pass_through
|
|
101
|
+
render_inline(UI::Table.new(id: "invoices", data: { test_id: "t" })) do |table|
|
|
102
|
+
table.with_row { |row| row.with_cell { "x" } }
|
|
103
|
+
end
|
|
104
|
+
assert_selector "table#invoices[data-test-id='t']"
|
|
105
|
+
end
|
|
106
|
+
end
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "test_helper"
|
|
4
|
+
|
|
5
|
+
class UI::Tabs::ContentTest < ViewComponent::TestCase
|
|
6
|
+
def setup_fixtures(*); end
|
|
7
|
+
def teardown_fixtures(*); end
|
|
8
|
+
|
|
9
|
+
def render_content(default_value: "a")
|
|
10
|
+
render_inline(UI::Tabs.new(default_value: default_value)) do |tabs|
|
|
11
|
+
tabs.with_list do |list|
|
|
12
|
+
list.with_trigger(value: "a") { "A" }
|
|
13
|
+
list.with_trigger(value: "b") { "B" }
|
|
14
|
+
end
|
|
15
|
+
tabs.with_panel(value: "a") { "panel-a" }
|
|
16
|
+
tabs.with_panel(value: "b") { "panel-b" }
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def test_renders_tabpanel_role
|
|
21
|
+
render_content
|
|
22
|
+
assert_selector "[role='tabpanel']"
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def test_aria_labelledby_matches_trigger_id
|
|
26
|
+
render_content
|
|
27
|
+
panel = page.find("[role='tabpanel'][data-value='a']")
|
|
28
|
+
trigger = page.find("[role='tab'][data-value='a']")
|
|
29
|
+
assert_equal trigger[:id], panel["aria-labelledby"]
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def test_tabindex_zero
|
|
33
|
+
render_content
|
|
34
|
+
panel = page.find("[role='tabpanel']")
|
|
35
|
+
assert_equal "0", panel[:tabindex]
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def test_data_state_active_when_active
|
|
39
|
+
render_content(default_value: "a")
|
|
40
|
+
panel = page.find("[role='tabpanel'][data-state='active']")
|
|
41
|
+
assert_equal "a", panel["data-value"]
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def test_data_state_inactive_when_inactive
|
|
45
|
+
render_content(default_value: "a")
|
|
46
|
+
panel = page.find("[role='tabpanel'][data-state='inactive']", visible: :all)
|
|
47
|
+
assert_equal "b", panel["data-value"]
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def test_data_ui_tabs_target_panel
|
|
51
|
+
render_content
|
|
52
|
+
panel = page.find("[role='tabpanel']")
|
|
53
|
+
assert_equal "panel", panel["data-ui--tabs-target"]
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def test_hidden_when_inactive
|
|
57
|
+
render_content(default_value: "a")
|
|
58
|
+
panel = page.find("[role='tabpanel'][data-value='b']", visible: :all)
|
|
59
|
+
assert panel[:hidden].present?
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def test_not_hidden_when_active
|
|
63
|
+
render_content(default_value: "a")
|
|
64
|
+
panel = page.find("[role='tabpanel'][data-value='a']")
|
|
65
|
+
refute panel[:hidden].present?
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def test_user_class_merge
|
|
69
|
+
render_inline(UI::Tabs.new(default_value: "a")) do |tabs|
|
|
70
|
+
tabs.with_list do |list|
|
|
71
|
+
list.with_trigger(value: "a") { "A" }
|
|
72
|
+
end
|
|
73
|
+
tabs.with_panel(value: "a", class: "my-panel") { "panel-a" }
|
|
74
|
+
end
|
|
75
|
+
classes = page.find("[role='tabpanel']")[:class].split
|
|
76
|
+
assert_includes classes, "my-panel"
|
|
77
|
+
assert_includes classes, "mt-2"
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def test_auto_generated_panel_id
|
|
81
|
+
render_content
|
|
82
|
+
panel = page.find("[role='tabpanel']")
|
|
83
|
+
assert panel[:id].present?
|
|
84
|
+
assert panel[:id].start_with?("ui-panel-")
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def test_panel_id_matches_trigger_aria_controls
|
|
88
|
+
render_content
|
|
89
|
+
panel = page.find("[role='tabpanel'][data-value='a']")
|
|
90
|
+
trigger = page.find("[role='tab'][data-value='a']")
|
|
91
|
+
assert_equal panel[:id], trigger["aria-controls"]
|
|
92
|
+
end
|
|
93
|
+
end
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "test_helper"
|
|
4
|
+
|
|
5
|
+
class UI::Tabs::ListTest < ViewComponent::TestCase
|
|
6
|
+
def setup_fixtures(*); end
|
|
7
|
+
def teardown_fixtures(*); end
|
|
8
|
+
|
|
9
|
+
def render_list(variant: :line)
|
|
10
|
+
render_inline(UI::Tabs.new(default_value: "a", variant: variant)) do |tabs|
|
|
11
|
+
tabs.with_list do |list|
|
|
12
|
+
list.with_trigger(value: "a") { "A" }
|
|
13
|
+
list.with_trigger(value: "b") { "B" }
|
|
14
|
+
end
|
|
15
|
+
tabs.with_panel(value: "a") { "panel-a" }
|
|
16
|
+
tabs.with_panel(value: "b") { "panel-b" }
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def test_renders_tablist_role
|
|
21
|
+
render_list
|
|
22
|
+
assert_selector "[role='tablist']"
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def test_line_variant_classes
|
|
26
|
+
render_list(variant: :line)
|
|
27
|
+
classes = page.find("[role='tablist']")[:class].split
|
|
28
|
+
assert_includes classes, "inline-flex"
|
|
29
|
+
assert_includes classes, "border-b"
|
|
30
|
+
assert_includes classes, "border-input"
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def test_pill_variant_classes
|
|
34
|
+
render_list(variant: :pill)
|
|
35
|
+
classes = page.find("[role='tablist']")[:class].split
|
|
36
|
+
assert_includes classes, "bg-muted"
|
|
37
|
+
assert_includes classes, "p-1"
|
|
38
|
+
assert_includes classes, "rounded-md"
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def test_solid_variant_classes
|
|
42
|
+
render_list(variant: :solid)
|
|
43
|
+
classes = page.find("[role='tablist']")[:class].split
|
|
44
|
+
assert_includes classes, "border"
|
|
45
|
+
assert_includes classes, "divide-x"
|
|
46
|
+
assert_includes classes, "bg-background"
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def test_user_class_merge
|
|
50
|
+
render_inline(UI::Tabs.new(default_value: "a")) do |tabs|
|
|
51
|
+
tabs.with_list(class: "my-list-class") do |list|
|
|
52
|
+
list.with_trigger(value: "a") { "A" }
|
|
53
|
+
end
|
|
54
|
+
tabs.with_panel(value: "a") { "panel-a" }
|
|
55
|
+
end
|
|
56
|
+
classes = page.find("[role='tablist']")[:class].split
|
|
57
|
+
assert_includes classes, "my-list-class"
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def test_triggers_rendered_inside_list
|
|
61
|
+
render_list
|
|
62
|
+
triggers = page.all("[role='tablist'] [role='tab']")
|
|
63
|
+
assert_equal 2, triggers.size
|
|
64
|
+
end
|
|
65
|
+
end
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "test_helper"
|
|
4
|
+
|
|
5
|
+
class UI::Tabs::TriggerTest < ViewComponent::TestCase
|
|
6
|
+
def setup_fixtures(*); end
|
|
7
|
+
def teardown_fixtures(*); end
|
|
8
|
+
|
|
9
|
+
def render_trigger(variant: :line, size: :default, disabled: false)
|
|
10
|
+
render_inline(UI::Tabs.new(default_value: "a", variant: variant, size: size)) do |tabs|
|
|
11
|
+
tabs.with_list do |list|
|
|
12
|
+
list.with_trigger(value: "a", disabled: disabled) { "A" }
|
|
13
|
+
list.with_trigger(value: "b") { "B" }
|
|
14
|
+
end
|
|
15
|
+
tabs.with_panel(value: "a") { "panel-a" }
|
|
16
|
+
tabs.with_panel(value: "b") { "panel-b" }
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def test_renders_button_with_role_tab
|
|
21
|
+
render_trigger
|
|
22
|
+
btn = page.find("[role='tab'][data-value='a']")
|
|
23
|
+
assert_equal "button", btn.tag_name
|
|
24
|
+
assert_equal "tab", btn[:role]
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def test_type_button
|
|
28
|
+
render_trigger
|
|
29
|
+
btn = page.find("[role='tab'][data-value='a']")
|
|
30
|
+
assert_equal "button", btn[:type]
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def test_aria_selected_true_when_active
|
|
34
|
+
render_trigger
|
|
35
|
+
active = page.find("[role='tab'][aria-selected='true']")
|
|
36
|
+
assert_equal "a", active["data-value"]
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def test_aria_selected_false_when_inactive
|
|
40
|
+
render_trigger
|
|
41
|
+
inactive = page.find("[role='tab'][aria-selected='false']")
|
|
42
|
+
assert_equal "b", inactive["data-value"]
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def test_aria_controls_matches_panel_id
|
|
46
|
+
render_trigger
|
|
47
|
+
trigger = page.find("[role='tab'][data-value='a']")
|
|
48
|
+
panel = page.find("[role='tabpanel'][data-value='a']")
|
|
49
|
+
assert_equal panel[:id], trigger["aria-controls"]
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def test_tabindex_zero_when_active
|
|
53
|
+
render_trigger
|
|
54
|
+
active = page.find("[role='tab'][tabindex='0']")
|
|
55
|
+
assert_equal "a", active["data-value"]
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def test_tabindex_minus_one_when_inactive
|
|
59
|
+
render_trigger
|
|
60
|
+
inactive = page.find("[role='tab'][tabindex='-1']")
|
|
61
|
+
assert_equal "b", inactive["data-value"]
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def test_data_state_active_when_active
|
|
65
|
+
render_trigger
|
|
66
|
+
active = page.find("[role='tab'][data-state='active']")
|
|
67
|
+
assert_equal "a", active["data-value"]
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def test_data_state_inactive_when_inactive
|
|
71
|
+
render_trigger
|
|
72
|
+
inactive = page.find("[role='tab'][data-state='inactive']")
|
|
73
|
+
assert_equal "b", inactive["data-value"]
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def test_data_action_includes_click_and_keydown
|
|
77
|
+
render_trigger
|
|
78
|
+
btn = page.find("[role='tab'][data-value='a']")
|
|
79
|
+
assert_includes btn["data-action"], "click->ui--tabs#activate"
|
|
80
|
+
assert_includes btn["data-action"], "keydown->ui--tabs#keydown"
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def test_data_ui_tabs_target_trigger
|
|
84
|
+
render_trigger
|
|
85
|
+
btn = page.find("[role='tab'][data-value='a']")
|
|
86
|
+
assert_equal "trigger", btn["data-ui--tabs-target"]
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def test_disabled_sets_disabled_attribute
|
|
90
|
+
render_trigger(disabled: true)
|
|
91
|
+
btn = page.find("[role='tab'][disabled]")
|
|
92
|
+
assert btn.disabled?
|
|
93
|
+
assert_equal "a", btn["data-value"]
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def test_leading_icon_slot
|
|
97
|
+
render_inline(UI::Tabs.new(default_value: "a")) do |tabs|
|
|
98
|
+
tabs.with_list do |list|
|
|
99
|
+
list.with_trigger(value: "a") do |trigger|
|
|
100
|
+
trigger.with_leading_icon(name: "user")
|
|
101
|
+
"Account"
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
tabs.with_panel(value: "a") { "panel-a" }
|
|
105
|
+
end
|
|
106
|
+
icon = page.find("[role='tab'][data-value='a'] i", visible: :all)
|
|
107
|
+
assert_equal "user", icon["data-lucide"]
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def test_line_variant_size_sm
|
|
111
|
+
render_trigger(variant: :line, size: :sm)
|
|
112
|
+
btn = page.find("[role='tab'][data-value='a']")
|
|
113
|
+
classes = btn[:class].split
|
|
114
|
+
assert_includes classes, "h-8"
|
|
115
|
+
assert_includes classes, "text-xs"
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
def test_line_variant_size_lg
|
|
119
|
+
render_trigger(variant: :line, size: :lg)
|
|
120
|
+
btn = page.find("[role='tab'][data-value='a']")
|
|
121
|
+
classes = btn[:class].split
|
|
122
|
+
assert_includes classes, "h-11"
|
|
123
|
+
assert_includes classes, "text-base"
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def test_pill_variant_size_default
|
|
127
|
+
render_trigger(variant: :pill, size: :default)
|
|
128
|
+
btn = page.find("[role='tab'][data-value='a']")
|
|
129
|
+
classes = btn[:class].split
|
|
130
|
+
assert_includes classes, "h-8"
|
|
131
|
+
assert_includes classes, "text-sm"
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
def test_solid_variant_size_default
|
|
135
|
+
render_trigger(variant: :solid, size: :default)
|
|
136
|
+
btn = page.find("[role='tab'][data-value='a']")
|
|
137
|
+
classes = btn[:class].split
|
|
138
|
+
assert_includes classes, "h-10"
|
|
139
|
+
assert_includes classes, "text-sm"
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
def test_user_class_merge
|
|
143
|
+
render_inline(UI::Tabs.new(default_value: "a")) do |tabs|
|
|
144
|
+
tabs.with_list do |list|
|
|
145
|
+
list.with_trigger(value: "a", class: "my-trigger") { "A" }
|
|
146
|
+
end
|
|
147
|
+
tabs.with_panel(value: "a") { "panel-a" }
|
|
148
|
+
end
|
|
149
|
+
classes = page.find("[role='tab']")[:class].split
|
|
150
|
+
assert_includes classes, "my-trigger"
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
def test_auto_generated_id
|
|
154
|
+
render_trigger
|
|
155
|
+
btn = page.find("[role='tab'][data-value='a']")
|
|
156
|
+
assert btn[:id].present?
|
|
157
|
+
assert btn[:id].start_with?("ui-tab-")
|
|
158
|
+
end
|
|
159
|
+
end
|