poetry-ui 0.0.2
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/.gitattributes +6 -0
- data/CHANGELOG.md +3 -0
- data/LICENSE.txt +21 -0
- data/README.md +87 -0
- data/THIRD_PARTY_NOTICES.md +201 -0
- data/app/components/poetry/ui/accordion/component.html.erb +3 -0
- data/app/components/poetry/ui/accordion/component.rb +197 -0
- data/app/components/poetry/ui/accordion/preview.rb +39 -0
- data/app/components/poetry/ui/accordion/style.rb +37 -0
- data/app/components/poetry/ui/alert/component.html.erb +6 -0
- data/app/components/poetry/ui/alert/component.rb +61 -0
- data/app/components/poetry/ui/alert/preview.rb +35 -0
- data/app/components/poetry/ui/alert/style.rb +24 -0
- data/app/components/poetry/ui/alert_dialog/component.html.erb +17 -0
- data/app/components/poetry/ui/alert_dialog/component.rb +208 -0
- data/app/components/poetry/ui/alert_dialog/preview.rb +48 -0
- data/app/components/poetry/ui/alert_dialog/style.rb +46 -0
- data/app/components/poetry/ui/aspect_ratio/component.rb +63 -0
- data/app/components/poetry/ui/aspect_ratio/preview.rb +29 -0
- data/app/components/poetry/ui/aspect_ratio/style.rb +13 -0
- data/app/components/poetry/ui/attachment/component.html.erb +13 -0
- data/app/components/poetry/ui/attachment/component.rb +139 -0
- data/app/components/poetry/ui/attachment/preview.rb +83 -0
- data/app/components/poetry/ui/attachment/style.rb +59 -0
- data/app/components/poetry/ui/autocomplete/component.html.erb +16 -0
- data/app/components/poetry/ui/autocomplete/component.rb +193 -0
- data/app/components/poetry/ui/autocomplete/preview.rb +39 -0
- data/app/components/poetry/ui/autocomplete/style.rb +18 -0
- data/app/components/poetry/ui/avatar/component.rb +103 -0
- data/app/components/poetry/ui/avatar/group_preview.html.erb +11 -0
- data/app/components/poetry/ui/avatar/preview.rb +50 -0
- data/app/components/poetry/ui/avatar/style.rb +34 -0
- data/app/components/poetry/ui/badge/component.rb +73 -0
- data/app/components/poetry/ui/badge/preview.rb +54 -0
- data/app/components/poetry/ui/badge/style.rb +35 -0
- data/app/components/poetry/ui/breadcrumb/component.html.erb +32 -0
- data/app/components/poetry/ui/breadcrumb/component.rb +102 -0
- data/app/components/poetry/ui/breadcrumb/dropdown_crumb_preview.html.erb +21 -0
- data/app/components/poetry/ui/breadcrumb/preview.rb +45 -0
- data/app/components/poetry/ui/breadcrumb/style.rb +25 -0
- data/app/components/poetry/ui/bubble/component.html.erb +4 -0
- data/app/components/poetry/ui/bubble/component.rb +106 -0
- data/app/components/poetry/ui/bubble/preview.rb +62 -0
- data/app/components/poetry/ui/bubble/style.rb +47 -0
- data/app/components/poetry/ui/button/component.html.erb +14 -0
- data/app/components/poetry/ui/button/component.rb +168 -0
- data/app/components/poetry/ui/button/preview.rb +94 -0
- data/app/components/poetry/ui/button/style.rb +41 -0
- data/app/components/poetry/ui/button_group/component.rb +70 -0
- data/app/components/poetry/ui/button_group/popup_triggers_preview.html.erb +21 -0
- data/app/components/poetry/ui/button_group/preview.rb +46 -0
- data/app/components/poetry/ui/button_group/style.rb +27 -0
- data/app/components/poetry/ui/button_group/text_prefix_preview.html.erb +7 -0
- data/app/components/poetry/ui/calendar/component.html.erb +72 -0
- data/app/components/poetry/ui/calendar/component.rb +366 -0
- data/app/components/poetry/ui/calendar/preview.rb +55 -0
- data/app/components/poetry/ui/calendar/style.rb +55 -0
- data/app/components/poetry/ui/card/component.html.erb +11 -0
- data/app/components/poetry/ui/card/component.rb +90 -0
- data/app/components/poetry/ui/card/preview.rb +29 -0
- data/app/components/poetry/ui/card/style.rb +23 -0
- data/app/components/poetry/ui/carousel/component.html.erb +17 -0
- data/app/components/poetry/ui/carousel/component.rb +179 -0
- data/app/components/poetry/ui/carousel/preview.rb +67 -0
- data/app/components/poetry/ui/carousel/style.rb +39 -0
- data/app/components/poetry/ui/checkbox/component.html.erb +22 -0
- data/app/components/poetry/ui/checkbox/component.rb +179 -0
- data/app/components/poetry/ui/checkbox/preview.rb +68 -0
- data/app/components/poetry/ui/checkbox/style.rb +28 -0
- data/app/components/poetry/ui/clipboard_text/component.html.erb +15 -0
- data/app/components/poetry/ui/clipboard_text/component.rb +154 -0
- data/app/components/poetry/ui/clipboard_text/preview.rb +24 -0
- data/app/components/poetry/ui/clipboard_text/style.rb +23 -0
- data/app/components/poetry/ui/code_block/component.html.erb +13 -0
- data/app/components/poetry/ui/code_block/component.rb +141 -0
- data/app/components/poetry/ui/code_block/preview.rb +40 -0
- data/app/components/poetry/ui/code_block/style.rb +38 -0
- data/app/components/poetry/ui/collapsible/component.html.erb +4 -0
- data/app/components/poetry/ui/collapsible/component.rb +134 -0
- data/app/components/poetry/ui/collapsible/preview.rb +24 -0
- data/app/components/poetry/ui/combobox/component.html.erb +49 -0
- data/app/components/poetry/ui/combobox/component.rb +1086 -0
- data/app/components/poetry/ui/combobox/preview.rb +164 -0
- data/app/components/poetry/ui/combobox/style.rb +148 -0
- data/app/components/poetry/ui/command/component.html.erb +20 -0
- data/app/components/poetry/ui/command/component.rb +526 -0
- data/app/components/poetry/ui/command/dialog_component.html.erb +14 -0
- data/app/components/poetry/ui/command/dialog_component.rb +206 -0
- data/app/components/poetry/ui/command/dialog_preview.rb +39 -0
- data/app/components/poetry/ui/command/preview.rb +121 -0
- data/app/components/poetry/ui/command/style.rb +78 -0
- data/app/components/poetry/ui/composable_trigger.rb +63 -0
- data/app/components/poetry/ui/context_menu/component.html.erb +6 -0
- data/app/components/poetry/ui/context_menu/component.rb +358 -0
- data/app/components/poetry/ui/context_menu/preview.rb +111 -0
- data/app/components/poetry/ui/context_menu/style.rb +59 -0
- data/app/components/poetry/ui/data_table/component.html.erb +78 -0
- data/app/components/poetry/ui/data_table/component.rb +277 -0
- data/app/components/poetry/ui/data_table/preview.rb +68 -0
- data/app/components/poetry/ui/data_table/state.rb +115 -0
- data/app/components/poetry/ui/data_table/style.rb +42 -0
- data/app/components/poetry/ui/date_field/component.html.erb +7 -0
- data/app/components/poetry/ui/date_field/component.rb +209 -0
- data/app/components/poetry/ui/date_field/preview.rb +42 -0
- data/app/components/poetry/ui/date_field/style.rb +35 -0
- data/app/components/poetry/ui/date_picker/component.html.erb +23 -0
- data/app/components/poetry/ui/date_picker/component.rb +242 -0
- data/app/components/poetry/ui/date_picker/preview.rb +31 -0
- data/app/components/poetry/ui/date_picker/style.rb +25 -0
- data/app/components/poetry/ui/deferred/component.html.erb +16 -0
- data/app/components/poetry/ui/deferred/component.rb +90 -0
- data/app/components/poetry/ui/deferred/preview.rb +24 -0
- data/app/components/poetry/ui/dialog/component.html.erb +21 -0
- data/app/components/poetry/ui/dialog/component.rb +194 -0
- data/app/components/poetry/ui/dialog/preview.rb +33 -0
- data/app/components/poetry/ui/dialog/style.rb +30 -0
- data/app/components/poetry/ui/drawer/component.html.erb +16 -0
- data/app/components/poetry/ui/drawer/component.rb +207 -0
- data/app/components/poetry/ui/drawer/preview.rb +69 -0
- data/app/components/poetry/ui/drawer/style.rb +105 -0
- data/app/components/poetry/ui/dropdown_menu/component.html.erb +6 -0
- data/app/components/poetry/ui/dropdown_menu/component.rb +350 -0
- data/app/components/poetry/ui/dropdown_menu/preview.rb +101 -0
- data/app/components/poetry/ui/dropdown_menu/style.rb +60 -0
- data/app/components/poetry/ui/empty/component.html.erb +10 -0
- data/app/components/poetry/ui/empty/component.rb +79 -0
- data/app/components/poetry/ui/empty/preview.rb +27 -0
- data/app/components/poetry/ui/empty/style.rb +27 -0
- data/app/components/poetry/ui/family_identity.rb +52 -0
- data/app/components/poetry/ui/field/component.html.erb +19 -0
- data/app/components/poetry/ui/field/component.rb +183 -0
- data/app/components/poetry/ui/field/preview.html.erb +30 -0
- data/app/components/poetry/ui/field/preview.rb +72 -0
- data/app/components/poetry/ui/field/style.rb +68 -0
- data/app/components/poetry/ui/field_group/component.rb +61 -0
- data/app/components/poetry/ui/field_group/preview.html.erb +30 -0
- data/app/components/poetry/ui/field_group/preview.rb +20 -0
- data/app/components/poetry/ui/field_group/style.rb +25 -0
- data/app/components/poetry/ui/field_separator/component.rb +70 -0
- data/app/components/poetry/ui/field_separator/preview.rb +18 -0
- data/app/components/poetry/ui/field_separator/style.rb +20 -0
- data/app/components/poetry/ui/fieldset/component.rb +90 -0
- data/app/components/poetry/ui/fieldset/preview.html.erb +22 -0
- data/app/components/poetry/ui/fieldset/preview.rb +25 -0
- data/app/components/poetry/ui/fieldset/style.rb +18 -0
- data/app/components/poetry/ui/file_input/component.html.erb +14 -0
- data/app/components/poetry/ui/file_input/component.rb +188 -0
- data/app/components/poetry/ui/file_input/preview.rb +30 -0
- data/app/components/poetry/ui/file_input/style.rb +35 -0
- data/app/components/poetry/ui/hover_card/component.html.erb +6 -0
- data/app/components/poetry/ui/hover_card/component.rb +201 -0
- data/app/components/poetry/ui/hover_card/preview.rb +60 -0
- data/app/components/poetry/ui/hover_card/style.rb +17 -0
- data/app/components/poetry/ui/icon/component.rb +127 -0
- data/app/components/poetry/ui/icon/preview.rb +21 -0
- data/app/components/poetry/ui/input/component.rb +80 -0
- data/app/components/poetry/ui/input/preview.rb +35 -0
- data/app/components/poetry/ui/input/style.rb +18 -0
- data/app/components/poetry/ui/input_group/button_preview.html.erb +12 -0
- data/app/components/poetry/ui/input_group/component.rb +69 -0
- data/app/components/poetry/ui/input_group/preview.rb +23 -0
- data/app/components/poetry/ui/input_group/search_preview.html.erb +13 -0
- data/app/components/poetry/ui/input_group/style.rb +40 -0
- data/app/components/poetry/ui/input_group/textarea_preview.html.erb +10 -0
- data/app/components/poetry/ui/input_group_field.rb +61 -0
- data/app/components/poetry/ui/input_otp/component.html.erb +25 -0
- data/app/components/poetry/ui/input_otp/component.rb +250 -0
- data/app/components/poetry/ui/input_otp/preview.rb +91 -0
- data/app/components/poetry/ui/input_otp/style.rb +36 -0
- data/app/components/poetry/ui/item/component.html.erb +13 -0
- data/app/components/poetry/ui/item/component.rb +99 -0
- data/app/components/poetry/ui/item/group_preview.html.erb +15 -0
- data/app/components/poetry/ui/item/preview.rb +52 -0
- data/app/components/poetry/ui/item/style.rb +45 -0
- data/app/components/poetry/ui/kbd/component.rb +47 -0
- data/app/components/poetry/ui/kbd/group_preview.html.erb +4 -0
- data/app/components/poetry/ui/kbd/preview.rb +23 -0
- data/app/components/poetry/ui/kbd/style.rb +13 -0
- data/app/components/poetry/ui/label/component.rb +46 -0
- data/app/components/poetry/ui/label/preview.html.erb +5 -0
- data/app/components/poetry/ui/label/preview.rb +16 -0
- data/app/components/poetry/ui/label/style.rb +13 -0
- data/app/components/poetry/ui/link/component.rb +62 -0
- data/app/components/poetry/ui/link/preview.rb +31 -0
- data/app/components/poetry/ui/link/style.rb +20 -0
- data/app/components/poetry/ui/marker/component.html.erb +4 -0
- data/app/components/poetry/ui/marker/component.rb +81 -0
- data/app/components/poetry/ui/marker/preview.rb +43 -0
- data/app/components/poetry/ui/marker/style.rb +24 -0
- data/app/components/poetry/ui/menubar/component.html.erb +3 -0
- data/app/components/poetry/ui/menubar/component.rb +493 -0
- data/app/components/poetry/ui/menubar/preview.rb +164 -0
- data/app/components/poetry/ui/menubar/style.rb +71 -0
- data/app/components/poetry/ui/menus.rb +386 -0
- data/app/components/poetry/ui/message/component.html.erb +8 -0
- data/app/components/poetry/ui/message/component.rb +64 -0
- data/app/components/poetry/ui/message/preview.rb +33 -0
- data/app/components/poetry/ui/message/style.rb +29 -0
- data/app/components/poetry/ui/message_scroller/component.html.erb +15 -0
- data/app/components/poetry/ui/message_scroller/component.rb +163 -0
- data/app/components/poetry/ui/message_scroller/preview.rb +33 -0
- data/app/components/poetry/ui/message_scroller/style.rb +39 -0
- data/app/components/poetry/ui/metadata_list/component.rb +88 -0
- data/app/components/poetry/ui/metadata_list/preview.rb +55 -0
- data/app/components/poetry/ui/metadata_list/style.rb +35 -0
- data/app/components/poetry/ui/meter/component.rb +117 -0
- data/app/components/poetry/ui/meter/preview.rb +28 -0
- data/app/components/poetry/ui/meter/style.rb +16 -0
- data/app/components/poetry/ui/native_select/component.rb +124 -0
- data/app/components/poetry/ui/native_select/labelled_preview.html.erb +7 -0
- data/app/components/poetry/ui/native_select/preview.rb +20 -0
- data/app/components/poetry/ui/native_select/style.rb +22 -0
- data/app/components/poetry/ui/navigation_menu/bar_preview.html.erb +14 -0
- data/app/components/poetry/ui/navigation_menu/component.html.erb +25 -0
- data/app/components/poetry/ui/navigation_menu/component.rb +254 -0
- data/app/components/poetry/ui/navigation_menu/preview.rb +20 -0
- data/app/components/poetry/ui/navigation_menu/style.rb +73 -0
- data/app/components/poetry/ui/navigation_menu/viewport_preview.html.erb +23 -0
- data/app/components/poetry/ui/number_field/component.html.erb +19 -0
- data/app/components/poetry/ui/number_field/component.rb +263 -0
- data/app/components/poetry/ui/number_field/preview.rb +66 -0
- data/app/components/poetry/ui/number_field/style.rb +14 -0
- data/app/components/poetry/ui/pagination/component.html.erb +40 -0
- data/app/components/poetry/ui/pagination/component.rb +182 -0
- data/app/components/poetry/ui/pagination/preview.rb +47 -0
- data/app/components/poetry/ui/pagination/style.rb +27 -0
- data/app/components/poetry/ui/popover/component.html.erb +17 -0
- data/app/components/poetry/ui/popover/component.rb +203 -0
- data/app/components/poetry/ui/popover/preview.html.erb +29 -0
- data/app/components/poetry/ui/popover/preview.rb +66 -0
- data/app/components/poetry/ui/popover/style.rb +26 -0
- data/app/components/poetry/ui/popper_consumer.rb +69 -0
- data/app/components/poetry/ui/progress/component.rb +95 -0
- data/app/components/poetry/ui/progress/preview.rb +22 -0
- data/app/components/poetry/ui/progress/style.rb +19 -0
- data/app/components/poetry/ui/questionnaire/component.html.erb +80 -0
- data/app/components/poetry/ui/questionnaire/component.rb +396 -0
- data/app/components/poetry/ui/questionnaire/preview.rb +99 -0
- data/app/components/poetry/ui/questionnaire/style.rb +53 -0
- data/app/components/poetry/ui/radio_group/component.html.erb +4 -0
- data/app/components/poetry/ui/radio_group/component.rb +323 -0
- data/app/components/poetry/ui/radio_group/preview.rb +109 -0
- data/app/components/poetry/ui/radio_group/style.rb +53 -0
- data/app/components/poetry/ui/resizable/component.html.erb +10 -0
- data/app/components/poetry/ui/resizable/component.rb +178 -0
- data/app/components/poetry/ui/resizable/preview.rb +42 -0
- data/app/components/poetry/ui/resizable/style.rb +30 -0
- data/app/components/poetry/ui/scroll_area/component.rb +76 -0
- data/app/components/poetry/ui/scroll_area/preview.rb +35 -0
- data/app/components/poetry/ui/scroll_area/style.rb +19 -0
- data/app/components/poetry/ui/search_field/component.html.erb +15 -0
- data/app/components/poetry/ui/search_field/component.rb +137 -0
- data/app/components/poetry/ui/search_field/preview.rb +23 -0
- data/app/components/poetry/ui/search_field/style.rb +18 -0
- data/app/components/poetry/ui/select/component.html.erb +15 -0
- data/app/components/poetry/ui/select/component.rb +639 -0
- data/app/components/poetry/ui/select/preview.rb +113 -0
- data/app/components/poetry/ui/select/style.rb +58 -0
- data/app/components/poetry/ui/sensitive_input/component.html.erb +29 -0
- data/app/components/poetry/ui/sensitive_input/component.rb +274 -0
- data/app/components/poetry/ui/sensitive_input/preview.rb +40 -0
- data/app/components/poetry/ui/sensitive_input/style.rb +49 -0
- data/app/components/poetry/ui/separator/component.rb +57 -0
- data/app/components/poetry/ui/separator/preview.rb +19 -0
- data/app/components/poetry/ui/separator/style.rb +15 -0
- data/app/components/poetry/ui/sheet/component.html.erb +21 -0
- data/app/components/poetry/ui/sheet/component.rb +97 -0
- data/app/components/poetry/ui/sheet/preview.rb +57 -0
- data/app/components/poetry/ui/sheet/style.rb +47 -0
- data/app/components/poetry/ui/sidebar/component.html.erb +20 -0
- data/app/components/poetry/ui/sidebar/component.rb +249 -0
- data/app/components/poetry/ui/sidebar/preview.rb +21 -0
- data/app/components/poetry/ui/sidebar/shell_preview.html.erb +47 -0
- data/app/components/poetry/ui/sidebar/style.rb +139 -0
- data/app/components/poetry/ui/skeleton/component.rb +39 -0
- data/app/components/poetry/ui/skeleton/preview.rb +22 -0
- data/app/components/poetry/ui/skeleton/style.rb +13 -0
- data/app/components/poetry/ui/slider/component.html.erb +13 -0
- data/app/components/poetry/ui/slider/component.rb +360 -0
- data/app/components/poetry/ui/slider/preview.rb +73 -0
- data/app/components/poetry/ui/slider/style.rb +55 -0
- data/app/components/poetry/ui/spinner/component.rb +58 -0
- data/app/components/poetry/ui/spinner/preview.rb +22 -0
- data/app/components/poetry/ui/spinner/style.rb +12 -0
- data/app/components/poetry/ui/stat/component.html.erb +13 -0
- data/app/components/poetry/ui/stat/component.rb +102 -0
- data/app/components/poetry/ui/stat/preview.rb +47 -0
- data/app/components/poetry/ui/stat/style.rb +29 -0
- data/app/components/poetry/ui/switch/component.html.erb +15 -0
- data/app/components/poetry/ui/switch/component.rb +136 -0
- data/app/components/poetry/ui/switch/preview.rb +66 -0
- data/app/components/poetry/ui/switch/style.rb +22 -0
- data/app/components/poetry/ui/table/component.html.erb +3 -0
- data/app/components/poetry/ui/table/component.rb +93 -0
- data/app/components/poetry/ui/table/preview.rb +96 -0
- data/app/components/poetry/ui/table/style.rb +26 -0
- data/app/components/poetry/ui/tabs/component.html.erb +10 -0
- data/app/components/poetry/ui/tabs/component.rb +253 -0
- data/app/components/poetry/ui/tabs/preview.rb +43 -0
- data/app/components/poetry/ui/tabs/style.rb +49 -0
- data/app/components/poetry/ui/tag_group/component.html.erb +8 -0
- data/app/components/poetry/ui/tag_group/component.rb +203 -0
- data/app/components/poetry/ui/tag_group/preview.rb +43 -0
- data/app/components/poetry/ui/tag_group/style.rb +29 -0
- data/app/components/poetry/ui/textarea/component.rb +64 -0
- data/app/components/poetry/ui/textarea/preview.rb +75 -0
- data/app/components/poetry/ui/textarea/style.rb +18 -0
- data/app/components/poetry/ui/time_field/component.html.erb +6 -0
- data/app/components/poetry/ui/time_field/component.rb +85 -0
- data/app/components/poetry/ui/time_field/preview.rb +40 -0
- data/app/components/poetry/ui/time_field/style.rb +30 -0
- data/app/components/poetry/ui/timeline/component.rb +134 -0
- data/app/components/poetry/ui/timeline/preview.rb +61 -0
- data/app/components/poetry/ui/timeline/style.rb +54 -0
- data/app/components/poetry/ui/toast/component.html.erb +24 -0
- data/app/components/poetry/ui/toast/component.rb +178 -0
- data/app/components/poetry/ui/toast/preview.rb +69 -0
- data/app/components/poetry/ui/toast/style.rb +33 -0
- data/app/components/poetry/ui/toast_trigger/addressed_preview.html.erb +12 -0
- data/app/components/poetry/ui/toast_trigger/component.html.erb +3 -0
- data/app/components/poetry/ui/toast_trigger/component.rb +82 -0
- data/app/components/poetry/ui/toast_trigger/default_preview.html.erb +11 -0
- data/app/components/poetry/ui/toast_trigger/preview.rb +19 -0
- data/app/components/poetry/ui/toaster/component.html.erb +1 -0
- data/app/components/poetry/ui/toaster/component.rb +99 -0
- data/app/components/poetry/ui/toaster/positions_preview.html.erb +24 -0
- data/app/components/poetry/ui/toaster/preview.rb +46 -0
- data/app/components/poetry/ui/toaster/stamp_preview.html.erb +15 -0
- data/app/components/poetry/ui/toaster/style.rb +26 -0
- data/app/components/poetry/ui/toggle/component.rb +120 -0
- data/app/components/poetry/ui/toggle/preview.rb +79 -0
- data/app/components/poetry/ui/toggle/style.rb +33 -0
- data/app/components/poetry/ui/toggle_group/component.html.erb +4 -0
- data/app/components/poetry/ui/toggle_group/component.rb +252 -0
- data/app/components/poetry/ui/toggle_group/preview.rb +143 -0
- data/app/components/poetry/ui/toggle_group/style.rb +33 -0
- data/app/components/poetry/ui/toolbar/component.rb +125 -0
- data/app/components/poetry/ui/toolbar/preview.rb +41 -0
- data/app/components/poetry/ui/toolbar/style.rb +20 -0
- data/app/components/poetry/ui/tooltip/component.html.erb +20 -0
- data/app/components/poetry/ui/tooltip/component.rb +185 -0
- data/app/components/poetry/ui/tooltip/preview.rb +74 -0
- data/app/components/poetry/ui/tooltip/previews/provider_row.html.erb +25 -0
- data/app/components/poetry/ui/tooltip/style.rb +24 -0
- data/app/components/poetry/ui/tree/component.html.erb +32 -0
- data/app/components/poetry/ui/tree/component.rb +233 -0
- data/app/components/poetry/ui/tree/preview.rb +55 -0
- data/app/components/poetry/ui/tree/style.rb +29 -0
- data/app/components/poetry/ui/typeset/component.rb +73 -0
- data/app/components/poetry/ui/typeset/preview.rb +45 -0
- data/app/components/poetry/ui/typeset/style.rb +15 -0
- data/app/controllers/poetry/ui/llms_controller.rb +28 -0
- data/app/helpers/poetry/ui/components_helper.rb +1794 -0
- data/app/helpers/poetry/ui/form_builder/type_inference.rb +117 -0
- data/app/helpers/poetry/ui/form_builder.rb +916 -0
- data/app/helpers/poetry/ui/optimistic_form_builder.rb +55 -0
- data/app/helpers/poetry/ui/toast_stream_actions.rb +27 -0
- data/config/component_descriptions.yml +96 -0
- data/config/component_registry.yml +12257 -0
- data/config/locales/en.yml +93 -0
- data/config/routes.rb +8 -0
- data/config/template_classes.txt +113 -0
- data/lib/generators/poetry/add/add_generator.rb +218 -0
- data/lib/generators/poetry/agent_rules/agent_rules_generator.rb +91 -0
- data/lib/generators/poetry/agents/agents_generator.rb +24 -0
- data/lib/generators/poetry/agents_section.rb +148 -0
- data/lib/generators/poetry/block/block_generator.rb +67 -0
- data/lib/generators/poetry/block/templates/action_bar.html.erb +45 -0
- data/lib/generators/poetry/block/templates/app_shell.html.erb +139 -0
- data/lib/generators/poetry/block/templates/data_index.html.erb +85 -0
- data/lib/generators/poetry/block/templates/destructive_panel.html.erb +26 -0
- data/lib/generators/poetry/block/templates/page_header.html.erb +25 -0
- data/lib/generators/poetry/block/templates/section_card.html.erb +42 -0
- data/lib/generators/poetry/block/templates/stepper.html.erb +79 -0
- data/lib/generators/poetry/block/templates/top_nav.html.erb +44 -0
- data/lib/generators/poetry/diff/diff_generator.rb +135 -0
- data/lib/generators/poetry/editor/editor_generator.rb +344 -0
- data/lib/generators/poetry/editor/templates/poetry-check-hook +81 -0
- data/lib/generators/poetry/install/install_generator.rb +364 -0
- data/lib/generators/poetry/pagination/pagination_generator.rb +76 -0
- data/lib/generators/poetry/pagination/templates/kaminari_paginator.html.erb +29 -0
- data/lib/generators/poetry/pagination/templates/poetry_link_renderer.rb +34 -0
- data/lib/generators/poetry/pagination/templates/poetry_pagy_helper.rb +28 -0
- data/lib/generators/poetry/recipes/agent_embed/app/javascript/controllers/agent_demo_controller.js +131 -0
- data/lib/generators/poetry/recipes/screen_data_index/app/controllers/orders_controller.rb +15 -0
- data/lib/generators/poetry/recipes/screen_data_index/app/views/orders/index.html.erb +7 -0
- data/lib/generators/poetry/recipes/screen_data_index/test/system/orders_index_test.rb +16 -0
- data/lib/generators/poetry/recipes/screen_settings/app/controllers/settings_controller.rb +14 -0
- data/lib/generators/poetry/recipes/screen_settings/app/views/settings/show.html.erb +9 -0
- data/lib/generators/poetry/recipes/screen_settings/test/system/settings_show_test.rb +16 -0
- data/lib/generators/poetry/scaffold_templates/scaffold_templates_generator.rb +60 -0
- data/lib/generators/poetry/scaffold_templates/templates/_form.html.erb.tt +77 -0
- data/lib/generators/poetry/scaffold_templates/templates/controller.rb.tt +78 -0
- data/lib/generators/poetry/scaffold_templates/templates/edit.html.erb.tt +14 -0
- data/lib/generators/poetry/scaffold_templates/templates/index.html.erb.tt +24 -0
- data/lib/generators/poetry/scaffold_templates/templates/new.html.erb.tt +11 -0
- data/lib/generators/poetry/scaffold_templates/templates/partial.html.erb.tt +13 -0
- data/lib/generators/poetry/scaffold_templates/templates/show.html.erb.tt +18 -0
- data/lib/generators/poetry/skill/skill_generator.rb +34 -0
- data/lib/generators/poetry/skill/templates/poetry-component/SKILL.md +58 -0
- data/lib/generators/poetry/skill/templates/poetry-component/references/anatomy.md +152 -0
- data/lib/generators/poetry/skill/templates/poetry-component/references/checklist.md +67 -0
- data/lib/generators/poetry/skill/templates/poetry-component/references/documentation.md +137 -0
- data/lib/generators/poetry/skill/templates/poetry-design/SKILL.md +56 -0
- data/lib/generators/poetry/skill/templates/poetry-design/references/audit.md +123 -0
- data/lib/generators/poetry/skill/templates/poetry-design/references/compose.md +73 -0
- data/lib/generators/poetry/skill/templates/poetry-design/references/figma.md +68 -0
- data/lib/generators/poetry/skill/templates/poetry-design/references/paper.md +56 -0
- data/lib/generators/poetry/skill/templates/poetry-design/references/study.md +47 -0
- data/lib/generators/poetry/skill/templates/poetry-design/references/theme.md +70 -0
- data/lib/generators/poetry/skills_section.rb +58 -0
- data/lib/poetry/ui/chat.rb +283 -0
- data/lib/poetry/ui/code_block_highlighter.rb +60 -0
- data/lib/poetry/ui/engine.rb +55 -0
- data/lib/poetry/ui/recipes.rb +157 -0
- data/lib/poetry/ui/testing/combobox.rb +167 -0
- data/lib/poetry/ui/testing/dialog.rb +73 -0
- data/lib/poetry/ui/testing/menu.rb +127 -0
- data/lib/poetry/ui/testing/registration.rb +58 -0
- data/lib/poetry/ui/testing/select.rb +147 -0
- data/lib/poetry/ui/testing/tester.rb +130 -0
- data/lib/poetry/ui/testing.rb +130 -0
- data/lib/poetry/ui/themes.rb +109 -0
- data/lib/poetry/ui/version.rb +8 -0
- data/lib/poetry/ui/webmcp.rb +91 -0
- data/lib/poetry/ui.rb +285 -0
- data/lib/poetry-ui.rb +5 -0
- data/lib/tasks/poetry/check.rake +73 -0
- data/lib/tasks/poetry/design.rake +293 -0
- data/lib/tasks/poetry/verify.rake +36 -0
- data/themes/default.css +1793 -0
- data/themes/luma.css +1764 -0
- data/themes/lyra.css +1768 -0
- data/themes/maia.css +1768 -0
- data/themes/mira.css +1760 -0
- data/themes/nova.css +1755 -0
- data/themes/rhea.css +1771 -0
- data/themes/sera.css +1757 -0
- data/themes/vega.css +1775 -0
- data/typeset/typeset.css +499 -0
- metadata +502 -0
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Poetry
|
|
4
|
+
module Ui
|
|
5
|
+
# The recipes channel: multi-file payloads served
|
|
6
|
+
# through the registry beside components and blocks. Every definition's
|
|
7
|
+
# files are LAZY callables over gem-shipped sources - the same files
|
|
8
|
+
# the generators install - so the projection cannot drift from the
|
|
9
|
+
# generator path. Names share the flat kebab namespace (RegistryIndex
|
|
10
|
+
# collision-checks recipes against components and blocks at first
|
|
11
|
+
# touch).
|
|
12
|
+
module Recipes
|
|
13
|
+
# Gem-relative path of the scaffold override template set.
|
|
14
|
+
SCAFFOLD_TEMPLATES = "lib/generators/poetry/scaffold_templates/templates"
|
|
15
|
+
# Gem-relative path holding the screen-slice and embed sources.
|
|
16
|
+
SCREEN_SOURCES = "lib/generators/poetry/recipes"
|
|
17
|
+
|
|
18
|
+
class << self
|
|
19
|
+
# Every recipe definition, in registry order: the agent embed, the
|
|
20
|
+
# three skill bundles, the scaffold-template set, and the screen
|
|
21
|
+
# slices.
|
|
22
|
+
#
|
|
23
|
+
# @return [Array<Hash>] name/title/description + lazy "files"
|
|
24
|
+
def definitions
|
|
25
|
+
[agent_embed, skill_poetry, skill_poetry_design, skill_poetry_component, scaffold_templates,
|
|
26
|
+
screen(
|
|
27
|
+
"screen-data-index", title: "Data index screen",
|
|
28
|
+
description: "The data-index block as a working feature slice: OrdersController, " \
|
|
29
|
+
"its index view rendering the block, and a system test. Add " \
|
|
30
|
+
"`resources :orders, only: :index` to routes.",
|
|
31
|
+
dependencies: ["data-index"]
|
|
32
|
+
),
|
|
33
|
+
screen(
|
|
34
|
+
"screen-settings", title: "Settings screen",
|
|
35
|
+
description: "A settings page composed from the page-header, section-card, and " \
|
|
36
|
+
"destructive-panel blocks: SettingsController, the composed view, " \
|
|
37
|
+
"and a system test. Add `resource :settings, only: :show` to routes.",
|
|
38
|
+
dependencies: %w[page-header section-card destructive-panel]
|
|
39
|
+
)]
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
private
|
|
43
|
+
|
|
44
|
+
def skill_poetry
|
|
45
|
+
{
|
|
46
|
+
"name" => "skill-poetry",
|
|
47
|
+
"title" => "poetry skill bundle",
|
|
48
|
+
"description" => "The component-usage Claude Code skill (SKILL.md + per-family " \
|
|
49
|
+
"references), generated from the live registry - the same files " \
|
|
50
|
+
"`bin/rails g poetry:skill` installs.",
|
|
51
|
+
"files" => lambda {
|
|
52
|
+
Poetry::Ui.runtime_skill_files.map do |rel, content|
|
|
53
|
+
{ "path" => rel, "target" => ".claude/skills/poetry/#{rel}", "content" => content }
|
|
54
|
+
end
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def skill_poetry_design
|
|
60
|
+
{
|
|
61
|
+
"name" => "skill-poetry-design",
|
|
62
|
+
"title" => "poetry-design skill bundle",
|
|
63
|
+
"description" => "The design-taste Claude Code skill (theme / compose / audit / study " \
|
|
64
|
+
"references) - the same files `bin/rails g poetry:skill` installs.",
|
|
65
|
+
"files" => lambda {
|
|
66
|
+
require "generators/poetry/skills_section"
|
|
67
|
+
files = Object.new.extend(Poetry::Generators::SkillsSection).design_skill_files
|
|
68
|
+
files.map do |rel, content|
|
|
69
|
+
{ "path" => rel, "target" => ".claude/skills/poetry-design/#{rel}", "content" => content }
|
|
70
|
+
end
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def skill_poetry_component
|
|
76
|
+
{
|
|
77
|
+
"name" => "skill-poetry-component",
|
|
78
|
+
"title" => "poetry-component skill bundle",
|
|
79
|
+
"description" => "The component-authoring Claude Code skill (anatomy / documentation / " \
|
|
80
|
+
"audit references) - the same files `bin/rails g poetry:skill` installs.",
|
|
81
|
+
"files" => lambda {
|
|
82
|
+
require "generators/poetry/skills_section"
|
|
83
|
+
files = Object.new.extend(Poetry::Generators::SkillsSection).component_skill_files
|
|
84
|
+
files.map do |rel, content|
|
|
85
|
+
{ "path" => rel, "target" => ".claude/skills/poetry-component/#{rel}", "content" => content }
|
|
86
|
+
end
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
# The scaffold override set: static .tt files whose targets mirror
|
|
92
|
+
# `poetry:scaffold_templates` exactly - Rails does the
|
|
93
|
+
# parameterization later, so the payload needs none.
|
|
94
|
+
def scaffold_templates
|
|
95
|
+
{
|
|
96
|
+
"name" => "scaffold-templates",
|
|
97
|
+
"title" => "poetry scaffold templates",
|
|
98
|
+
"description" => "The lib/templates override set that makes the STANDARD `rails g " \
|
|
99
|
+
"scaffold` emit poetry-composed views and a matching controller - " \
|
|
100
|
+
"the same files `bin/rails g poetry:scaffold_templates` copies.",
|
|
101
|
+
"files" => lambda {
|
|
102
|
+
base = Poetry::Ui.root.join(SCAFFOLD_TEMPLATES)
|
|
103
|
+
base.glob("*.tt").sort.map do |file|
|
|
104
|
+
name = file.basename.to_s
|
|
105
|
+
target = if name == "controller.rb.tt"
|
|
106
|
+
"lib/templates/rails/scaffold_controller/controller.rb.tt"
|
|
107
|
+
else
|
|
108
|
+
"lib/templates/erb/scaffold/#{name}"
|
|
109
|
+
end
|
|
110
|
+
{ "path" => name, "target" => target, "content" => file.read }
|
|
111
|
+
end
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
# The in-page GUI agent loader: the Turbo-hardened page-agent
|
|
117
|
+
# embed as a copy-in Stimulus controller - CDN-pinned script,
|
|
118
|
+
# opt-in, BYO key, and the poetry operator ground rules embedded
|
|
119
|
+
# as fallback instructions.
|
|
120
|
+
def agent_embed
|
|
121
|
+
{
|
|
122
|
+
"name" => "agent-embed",
|
|
123
|
+
"title" => "In-page agent embed",
|
|
124
|
+
"description" => "An opt-in page-agent loader for a poetry app as a Stimulus " \
|
|
125
|
+
"controller: pinned CDN script (?autoInit=false), session-only " \
|
|
126
|
+
"BYO key, Turbo-hardened (remounts on turbo:load, idle-only " \
|
|
127
|
+
"rebuild, module-init catch-up), poetry operator instructions " \
|
|
128
|
+
"built in. Wire a form with targets model/baseUrl/apiKey/status.",
|
|
129
|
+
"files" => lambda {
|
|
130
|
+
base = Poetry::Ui.root.join(SCREEN_SOURCES, "agent_embed")
|
|
131
|
+
base.glob("**/*").select(&:file?).sort.map do |file|
|
|
132
|
+
rel = file.relative_path_from(base).to_s
|
|
133
|
+
{ "path" => rel, "target" => rel, "content" => file.read }
|
|
134
|
+
end
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
def screen(name, title:, description:, dependencies:)
|
|
140
|
+
{
|
|
141
|
+
"name" => name,
|
|
142
|
+
"title" => title,
|
|
143
|
+
"description" => description,
|
|
144
|
+
"registry_dependencies" => dependencies,
|
|
145
|
+
"files" => lambda {
|
|
146
|
+
base = Poetry::Ui.root.join(SCREEN_SOURCES, name.tr("-", "_"))
|
|
147
|
+
base.glob("**/*").select(&:file?).sort.map do |file|
|
|
148
|
+
rel = file.relative_path_from(base).to_s
|
|
149
|
+
{ "path" => rel, "target" => rel, "content" => file.read }
|
|
150
|
+
end
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
end
|
|
154
|
+
end
|
|
155
|
+
end
|
|
156
|
+
end
|
|
157
|
+
end
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Poetry
|
|
4
|
+
module Ui
|
|
5
|
+
module Testing
|
|
6
|
+
# The Combobox interaction contract, both modes: open, FILTER by
|
|
7
|
+
# typing into the command input, commit an option; single-select
|
|
8
|
+
# closes on commit and the native select serializes, multiple keeps
|
|
9
|
+
# the popover open, grows chips, and serializes name[] through a
|
|
10
|
+
# native select-multiple.
|
|
11
|
+
#
|
|
12
|
+
# CONTENT RESOLVES THROUGH THE ID PAIR, document-wide: portal-on-open
|
|
13
|
+
# moves the open popup to body (docs/portal-on-open.md), so root
|
|
14
|
+
# scoping stops holding for the content and its items. The trigger
|
|
15
|
+
# (single) or the chips inline input (multiple) carries aria-controls
|
|
16
|
+
# naming the listbox; the content wrapper is its ancestor -
|
|
17
|
+
# id-anchored selectors keep Capybara's waiting semantics.
|
|
18
|
+
#
|
|
19
|
+
# @example Filtering, then committing
|
|
20
|
+
# combo = poetry_combobox("#assignee")
|
|
21
|
+
# combo.filter("Ada").select_option("Ada Lovelace")
|
|
22
|
+
# assert_equal "ada", combo.value
|
|
23
|
+
class Combobox < Tester
|
|
24
|
+
# Whether the popup is currently open.
|
|
25
|
+
#
|
|
26
|
+
# @return [Boolean]
|
|
27
|
+
def open?(wait: 0)
|
|
28
|
+
session.has_selector?("##{content_id}[data-open]", visible: :all, wait: wait)
|
|
29
|
+
rescue Capybara::ElementNotFound
|
|
30
|
+
false
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# Opens the popup (no-op when already open). via: :keyboard focuses
|
|
34
|
+
# the trigger and presses ArrowDown; :mouse presses it.
|
|
35
|
+
#
|
|
36
|
+
# @return [Combobox] self
|
|
37
|
+
def open(via: :mouse)
|
|
38
|
+
return self if open?
|
|
39
|
+
|
|
40
|
+
case via
|
|
41
|
+
when :keyboard
|
|
42
|
+
focus(trigger)
|
|
43
|
+
keys(:down)
|
|
44
|
+
else
|
|
45
|
+
press(trigger)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
session.assert_selector("##{content_id}[data-slot='combobox-content'][data-open][data-side]")
|
|
49
|
+
self
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# Escape-closes the open popup and waits for the closed state.
|
|
53
|
+
#
|
|
54
|
+
# @return [Combobox] self
|
|
55
|
+
def close
|
|
56
|
+
keys(:escape) if open?
|
|
57
|
+
session.assert_selector("##{content_id}[data-closed]", visible: :all)
|
|
58
|
+
self
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# Type into the filter input (opens first when closed). Single mode
|
|
62
|
+
# keeps the input inside the (portaled) popup; multiple keeps it
|
|
63
|
+
# inline in the chips field at home.
|
|
64
|
+
#
|
|
65
|
+
# @return [Combobox] self
|
|
66
|
+
def filter(query)
|
|
67
|
+
open
|
|
68
|
+
filter_input.set(query)
|
|
69
|
+
self
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
# Commit by exact visible text; single-select waits for the close,
|
|
73
|
+
# multiple leaves the popover up (assert on values/chips instead).
|
|
74
|
+
#
|
|
75
|
+
# @return [Combobox] self
|
|
76
|
+
def select_option(text, via: :mouse)
|
|
77
|
+
self.open(via: via)
|
|
78
|
+
|
|
79
|
+
if via == :keyboard
|
|
80
|
+
walk_highlight_to(text)
|
|
81
|
+
keys(:enter)
|
|
82
|
+
else
|
|
83
|
+
press(option(text))
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
self
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
# Single: the committed value. Multiple: the committed value list.
|
|
90
|
+
#
|
|
91
|
+
# @return [String, Array<String>]
|
|
92
|
+
def value
|
|
93
|
+
native = hidden_part("combobox-native")
|
|
94
|
+
native.multiple? ? native.value : native.value.to_s
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
# The visible chip texts (multiple mode).
|
|
98
|
+
#
|
|
99
|
+
# @return [Array<String>]
|
|
100
|
+
def chips
|
|
101
|
+
parts("combobox-chip").map(&:text)
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
# Remove a chip by its accessible text (multiple mode).
|
|
105
|
+
#
|
|
106
|
+
# @return [Combobox] self
|
|
107
|
+
def remove_chip(text)
|
|
108
|
+
press(root.find("[data-slot='combobox-chip']", text: text).find("button"))
|
|
109
|
+
self
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
private
|
|
113
|
+
|
|
114
|
+
# Bounded highlight walk: at most one pass over the items - a
|
|
115
|
+
# missing/mistyped label raises instead of arrowing forever.
|
|
116
|
+
def walk_highlight_to(text)
|
|
117
|
+
items = content.all("[data-slot='combobox-item']", visible: :all)
|
|
118
|
+
|
|
119
|
+
(items.size + 1).times do
|
|
120
|
+
return if highlighted_text == text
|
|
121
|
+
|
|
122
|
+
keys(:down)
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
raise Capybara::ElementNotFound,
|
|
126
|
+
"no option #{text.inspect} reached by ArrowDown - options: #{items.map(&:text).inspect}"
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
def trigger
|
|
130
|
+
part("combobox-trigger")
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
# The aria-controls anchor stays HOME in both modes: the trigger
|
|
134
|
+
# button (single) or the chips frame's inline input (multiple).
|
|
135
|
+
def list_id
|
|
136
|
+
@list_id ||= root.first(
|
|
137
|
+
"[data-slot='combobox-trigger'], [data-slot='combobox-chips'] [data-slot='combobox-chip-input']",
|
|
138
|
+
minimum: 1, visible: :all
|
|
139
|
+
)["aria-controls"]
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
def content_id
|
|
143
|
+
@content_id ||= session.find("##{list_id}", visible: :all)
|
|
144
|
+
.ancestor("[data-slot='combobox-content']", visible: :all)[:id]
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
def content
|
|
148
|
+
session.find("##{content_id}", visible: :all)
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
def filter_input
|
|
152
|
+
content.first("[data-slot='combobox-input']", minimum: 0, visible: :all) || part("combobox-input")
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
def option(text)
|
|
156
|
+
content.find("[data-slot='combobox-item']", text: text, exact_text: true)
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
def highlighted_text
|
|
160
|
+
content.find("[data-slot='combobox-item'][data-highlighted]", wait: 1).text
|
|
161
|
+
rescue Capybara::ElementNotFound
|
|
162
|
+
nil
|
|
163
|
+
end
|
|
164
|
+
end
|
|
165
|
+
end
|
|
166
|
+
end
|
|
167
|
+
end
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Poetry
|
|
4
|
+
module Ui
|
|
5
|
+
module Testing
|
|
6
|
+
# The Dialog interaction contract: the trigger opens, focus is
|
|
7
|
+
# trapped inside, Escape (or the close affordance) dismisses, and
|
|
8
|
+
# focus RETURNS to the trigger. The root you hand this tester is the
|
|
9
|
+
# element carrying data-component=dialog (trigger + content).
|
|
10
|
+
#
|
|
11
|
+
# @example Opening, asserting, dismissing
|
|
12
|
+
# dialog = poetry_dialog("[data-component='dialog']")
|
|
13
|
+
# dialog.open(via: :keyboard)
|
|
14
|
+
# assert_equal "Are you sure?", dialog.title
|
|
15
|
+
# dialog.close
|
|
16
|
+
class Dialog < Tester
|
|
17
|
+
# Whether the dialog is currently open.
|
|
18
|
+
#
|
|
19
|
+
# @return [Boolean]
|
|
20
|
+
def open?(wait: 0)
|
|
21
|
+
part?("dialog-content", wait: wait) &&
|
|
22
|
+
!hidden_part("dialog-content")["data-open"].nil?
|
|
23
|
+
rescue Capybara::ElementNotFound
|
|
24
|
+
false
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# Opens the dialog (no-op when already open). via: :keyboard
|
|
28
|
+
# focuses the trigger and presses Enter; :mouse presses it.
|
|
29
|
+
#
|
|
30
|
+
# @return [Dialog] self
|
|
31
|
+
def open(via: :mouse)
|
|
32
|
+
return self if open?
|
|
33
|
+
|
|
34
|
+
case via
|
|
35
|
+
when :keyboard
|
|
36
|
+
focus(trigger)
|
|
37
|
+
keys(:enter)
|
|
38
|
+
else
|
|
39
|
+
press(trigger)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
root.assert_selector("[data-slot='dialog-content'][data-open]")
|
|
43
|
+
self
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# Escape-closes the open dialog and waits for the closed state.
|
|
47
|
+
#
|
|
48
|
+
# @return [Dialog] self
|
|
49
|
+
def close
|
|
50
|
+
keys(:escape) if open?
|
|
51
|
+
root.assert_selector("[data-slot='dialog-content'][data-closed]", visible: :all)
|
|
52
|
+
self
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# The dialog's accessible title text.
|
|
56
|
+
#
|
|
57
|
+
# @return [String]
|
|
58
|
+
def title
|
|
59
|
+
part("dialog-title").text
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
private
|
|
63
|
+
|
|
64
|
+
# The trigger has no slot of its own - it is the consumer's Button
|
|
65
|
+
# wired to the dialog controller's open action (the contract).
|
|
66
|
+
def trigger
|
|
67
|
+
root.find("[data-action*='#{Poetry::Ui::Dialog::Component.stimulus_action(:open)}']",
|
|
68
|
+
match: :first)
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Poetry
|
|
4
|
+
module Ui
|
|
5
|
+
module Testing
|
|
6
|
+
# The DropdownMenu interaction contract: trigger toggles, open lands
|
|
7
|
+
# focus in the menu, arrows move data-highlighted, Enter activates
|
|
8
|
+
# the highlighted item, Escape closes with focus returned to the
|
|
9
|
+
# trigger.
|
|
10
|
+
#
|
|
11
|
+
# CONTENT RESOLVES THROUGH THE ID PAIR, document-wide: portal-on-open
|
|
12
|
+
# moves the open menu to body (docs/portal-on-open.md), so root
|
|
13
|
+
# scoping stops holding for the content and its items - the trigger's
|
|
14
|
+
# aria-controls id is the production controllers' own resolution
|
|
15
|
+
# rule, and id-anchored selectors keep Capybara's waiting semantics.
|
|
16
|
+
#
|
|
17
|
+
# @example Activating a menu item
|
|
18
|
+
# menu = poetry_dropdown_menu("#row-actions")
|
|
19
|
+
# menu.choose("Archive", via: :keyboard)
|
|
20
|
+
class Menu < Tester
|
|
21
|
+
# Whether the menu is currently open.
|
|
22
|
+
#
|
|
23
|
+
# @return [Boolean]
|
|
24
|
+
def open?(wait: 0)
|
|
25
|
+
session.has_selector?("##{content_id}[data-open]", visible: :all, wait: wait)
|
|
26
|
+
rescue Capybara::ElementNotFound
|
|
27
|
+
false
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# Opens the menu (no-op when already open). via: :keyboard focuses
|
|
31
|
+
# the trigger and presses ArrowDown; :mouse presses it.
|
|
32
|
+
#
|
|
33
|
+
# @return [Menu] self
|
|
34
|
+
def open(via: :mouse)
|
|
35
|
+
return self if open?
|
|
36
|
+
|
|
37
|
+
case via
|
|
38
|
+
when :keyboard
|
|
39
|
+
focus(trigger)
|
|
40
|
+
keys(:down)
|
|
41
|
+
else
|
|
42
|
+
press(trigger)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
session.assert_selector("##{content_id}[data-slot='dropdown-menu-content'][data-open][data-side]")
|
|
46
|
+
self
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# Escape-closes the open menu and waits for the closed state.
|
|
50
|
+
#
|
|
51
|
+
# @return [Menu] self
|
|
52
|
+
def close
|
|
53
|
+
keys(:escape) if open?
|
|
54
|
+
session.assert_selector("##{content_id}[data-closed]", visible: :all)
|
|
55
|
+
self
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# Opens first when closed; activates by exact visible text.
|
|
59
|
+
#
|
|
60
|
+
# @return [Menu] self
|
|
61
|
+
def choose(text, via: :mouse)
|
|
62
|
+
self.open(via: via)
|
|
63
|
+
|
|
64
|
+
if via == :keyboard
|
|
65
|
+
walk_highlight_to(text)
|
|
66
|
+
keys(:enter)
|
|
67
|
+
else
|
|
68
|
+
press(item(text))
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
self
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# The visible item texts (opens the menu first when closed).
|
|
75
|
+
#
|
|
76
|
+
# @return [Array<String>]
|
|
77
|
+
def items
|
|
78
|
+
open unless open?
|
|
79
|
+
content.all("[data-slot='dropdown-menu-item']").map(&:text)
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
private
|
|
83
|
+
|
|
84
|
+
# Bounded highlight walk: at most one pass over the items - a
|
|
85
|
+
# missing/mistyped label raises instead of arrowing forever.
|
|
86
|
+
def walk_highlight_to(text)
|
|
87
|
+
list = content.all("[data-slot='dropdown-menu-item']")
|
|
88
|
+
|
|
89
|
+
(list.size + 1).times do
|
|
90
|
+
return if highlighted_text == text
|
|
91
|
+
|
|
92
|
+
keys(:down)
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
raise Capybara::ElementNotFound,
|
|
96
|
+
"no option #{text.inspect} reached by ArrowDown - " \
|
|
97
|
+
"options: #{list.map(&:text).inspect}"
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def trigger
|
|
101
|
+
part("dropdown-menu-trigger")
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
# The trigger's aria-controls names the menu wherever it sits.
|
|
105
|
+
def content_id
|
|
106
|
+
@content_id ||= trigger["aria-controls"]
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def content
|
|
110
|
+
session.find("##{content_id}", visible: :all)
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
# Substring match, not exact: menu items legitimately carry more
|
|
114
|
+
# than their label (shortcut glyphs, badges).
|
|
115
|
+
def item(text)
|
|
116
|
+
content.find("[data-slot='dropdown-menu-item']", text: text, match: :first)
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def highlighted_text
|
|
120
|
+
content.find("[data-slot='dropdown-menu-item'][data-highlighted]", wait: 1).text
|
|
121
|
+
rescue Capybara::ElementNotFound
|
|
122
|
+
nil
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
end
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Poetry
|
|
4
|
+
module Ui
|
|
5
|
+
module Testing
|
|
6
|
+
# Raised by assert_poetry_controllers_registered outside Minitest.
|
|
7
|
+
class RegistrationError < StandardError; end
|
|
8
|
+
|
|
9
|
+
# The page-wide registration guard behind
|
|
10
|
+
# {Testing#poetry_unregistered_controllers}: every `data-controller`
|
|
11
|
+
# identifier with poetry's prefix must be registered on the host's
|
|
12
|
+
# Stimulus application, or the element is silently inert - Stimulus
|
|
13
|
+
# never errors on an unknown identifier, and one failed import in
|
|
14
|
+
# the controllers graph takes every poetry controller down with it.
|
|
15
|
+
#
|
|
16
|
+
# @api private
|
|
17
|
+
module Registration
|
|
18
|
+
PREFIX = "poetry--"
|
|
19
|
+
|
|
20
|
+
# The JS run in the page: `application` is an expression naming
|
|
21
|
+
# the Stimulus application (window.Stimulus in Rails' default
|
|
22
|
+
# controllers/application.js).
|
|
23
|
+
#
|
|
24
|
+
# @return [String]
|
|
25
|
+
def self.script(application)
|
|
26
|
+
<<~JS
|
|
27
|
+
(() => {
|
|
28
|
+
const application = #{application};
|
|
29
|
+
if (!application) return { error: "no Stimulus application at #{application} - the controllers module never ran (one failed import kills the whole graph)" };
|
|
30
|
+
const registry = application.router && application.router.modulesByIdentifier;
|
|
31
|
+
const registered = registry ? Array.from(registry.keys()) : (application.controllers || []).map((c) => c.identifier);
|
|
32
|
+
const known = new Set(registered);
|
|
33
|
+
const missing = new Set();
|
|
34
|
+
for (const element of document.querySelectorAll("[data-controller]")) {
|
|
35
|
+
for (const identifier of element.getAttribute("data-controller").split(/\\s+/)) {
|
|
36
|
+
if (identifier.startsWith(#{PREFIX.inspect}) && !known.has(identifier)) missing.add(identifier);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return { missing: Array.from(missing).sort(), registered: registered.filter((id) => id.startsWith(#{PREFIX.inspect})).length };
|
|
40
|
+
})()
|
|
41
|
+
JS
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# The failure text: the identifiers, and the two causes worth
|
|
45
|
+
# checking first.
|
|
46
|
+
#
|
|
47
|
+
# @return [String]
|
|
48
|
+
def self.message(result)
|
|
49
|
+
return result["error"] if result["error"]
|
|
50
|
+
|
|
51
|
+
"#{result["missing"].size} poetry controller(s) on the page are not registered on the Stimulus " \
|
|
52
|
+
"application: #{result["missing"].join(", ")} (#{result["registered"]} poetry controllers are). " \
|
|
53
|
+
"Check the importmap pins and that every poetry gem's register call runs in controllers/index.js."
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|