diff --git a/app/src/__registry__/all_blocks.rs b/app/src/__registry__/all_blocks.rs index a7ec0ab..3eb752c 100644 --- a/app/src/__registry__/all_blocks.rs +++ b/app/src/__registry__/all_blocks.rs @@ -97,20 +97,20 @@ impl BlockIdKebab { BlockIdKebab::Integration05 => "Icon Library with 3D Perspective Grid", BlockIdKebab::Integration06 => "Icon Library with Scrolling Rows", BlockIdKebab::Integration07 => "Icon Library with Scattered Layout", - BlockIdKebab::Login01 => "Simple Login form", - BlockIdKebab::Login02 => "Login with Social Auth", - BlockIdKebab::Login03 => "Two-Factor Authentication", - BlockIdKebab::Login04 => "Password Reset Form", - BlockIdKebab::Sidenav01 => "Simple Sidenav with grouped sections", - BlockIdKebab::Sidenav02 => "Simple Sidenav with Collapsible menus", - BlockIdKebab::Sidenav03 => "Simple Sidenav with submenus", - BlockIdKebab::Sidenav04 => "A Floating Sidenav with submenus", - BlockIdKebab::Sidenav05 => "Sidenav with Collapsible submenus", + BlockIdKebab::Login01 => "Login Form Card", + BlockIdKebab::Login02 => "Split Login with GitHub", + BlockIdKebab::Login03 => "Social Login with Email Fallback", + BlockIdKebab::Login04 => "Split Login with Social Buttons", + BlockIdKebab::Sidenav01 => "Sidenav with Grouped Sections", + BlockIdKebab::Sidenav02 => "Sidenav with Collapsible Menus", + BlockIdKebab::Sidenav03 => "Sidenav with Submenus", + BlockIdKebab::Sidenav04 => "Floating Sidenav with Submenus", + BlockIdKebab::Sidenav05 => "Sidenav with Collapsible Submenus", BlockIdKebab::Sidenav06 => "Sidenav with Dropdown Submenus", BlockIdKebab::Sidenav07 => "Collapsible Sidenav with Icons", - BlockIdKebab::Sidenav08 => "Inset Sidenav with secondary navigation", - BlockIdKebab::Sidenav09 => "Nested Sidenavs with Route-based Navigation", - BlockIdKebab::Sidenav10 => "Sidenav with search functionality", + BlockIdKebab::Sidenav08 => "Inset Sidenav with Secondary Navigation", + BlockIdKebab::Sidenav09 => "Nested Sidenav with Route-Based Navigation", + BlockIdKebab::Sidenav10 => "Sidenav with Search", BlockIdKebab::Sidenav11 => "Right-Side Sidenav", } } @@ -2068,25 +2068,25 @@ pub const ALL_INTEGRATION_BLOCKS: &[BlockEntry] = &[ pub const ALL_LOGIN_BLOCKS: &[BlockEntry] = &[ BlockEntry { block_id_str: "login01", - block_title: "Simple Login form", + block_title: "Login Form Card", block_id_kebab: BlockIdKebab::Login01, block_route: BlockRoutes::Login, }, BlockEntry { block_id_str: "login02", - block_title: "Login with Social Auth", + block_title: "Split Login with GitHub", block_id_kebab: BlockIdKebab::Login02, block_route: BlockRoutes::Login, }, BlockEntry { block_id_str: "login03", - block_title: "Two-Factor Authentication", + block_title: "Social Login with Email Fallback", block_id_kebab: BlockIdKebab::Login03, block_route: BlockRoutes::Login, }, BlockEntry { block_id_str: "login04", - block_title: "Password Reset Form", + block_title: "Split Login with Social Buttons", block_id_kebab: BlockIdKebab::Login04, block_route: BlockRoutes::Login, }, @@ -2096,31 +2096,31 @@ pub const ALL_LOGIN_BLOCKS: &[BlockEntry] = &[ pub const ALL_SIDENAV_BLOCKS: &[BlockEntry] = &[ BlockEntry { block_id_str: "sidenav01", - block_title: "Simple Sidenav with grouped sections", + block_title: "Sidenav with Grouped Sections", block_id_kebab: BlockIdKebab::Sidenav01, block_route: BlockRoutes::Sidenav, }, BlockEntry { block_id_str: "sidenav02", - block_title: "Simple Sidenav with Collapsible menus", + block_title: "Sidenav with Collapsible Menus", block_id_kebab: BlockIdKebab::Sidenav02, block_route: BlockRoutes::Sidenav, }, BlockEntry { block_id_str: "sidenav03", - block_title: "Simple Sidenav with submenus", + block_title: "Sidenav with Submenus", block_id_kebab: BlockIdKebab::Sidenav03, block_route: BlockRoutes::Sidenav, }, BlockEntry { block_id_str: "sidenav04", - block_title: "A Floating Sidenav with submenus", + block_title: "Floating Sidenav with Submenus", block_id_kebab: BlockIdKebab::Sidenav04, block_route: BlockRoutes::Sidenav, }, BlockEntry { block_id_str: "sidenav05", - block_title: "Sidenav with Collapsible submenus", + block_title: "Sidenav with Collapsible Submenus", block_id_kebab: BlockIdKebab::Sidenav05, block_route: BlockRoutes::Sidenav, }, @@ -2138,19 +2138,19 @@ pub const ALL_SIDENAV_BLOCKS: &[BlockEntry] = &[ }, BlockEntry { block_id_str: "sidenav08", - block_title: "Inset Sidenav with secondary navigation", + block_title: "Inset Sidenav with Secondary Navigation", block_id_kebab: BlockIdKebab::Sidenav08, block_route: BlockRoutes::Sidenav, }, BlockEntry { block_id_str: "sidenav09", - block_title: "Nested Sidenavs with Route-based Navigation", + block_title: "Nested Sidenav with Route-Based Navigation", block_id_kebab: BlockIdKebab::Sidenav09, block_route: BlockRoutes::Sidenav, }, BlockEntry { block_id_str: "sidenav10", - block_title: "Sidenav with search functionality", + block_title: "Sidenav with Search", block_id_kebab: BlockIdKebab::Sidenav10, block_route: BlockRoutes::Sidenav, }, diff --git a/app/src/domain/blocks/block_entry.rs b/app/src/domain/blocks/block_entry.rs index 70951b8..8da1b2b 100644 --- a/app/src/domain/blocks/block_entry.rs +++ b/app/src/domain/blocks/block_entry.rs @@ -85,3 +85,76 @@ impl BlockEntry { ALL_INTEGRATION_BLOCKS.to_vec() } } + +#[cfg(test)] +mod tests { + use super::*; + + fn titles(entries: &[BlockEntry]) -> Vec<&'static str> { + entries.iter().map(|entry| entry.block_title).collect() + } + + #[test] + fn login_block_titles_are_accurate_and_consistent() { + let blocks = BlockEntry::get_login_blocks(); + let block_titles = titles(&blocks); + + assert_eq!( + block_titles, + vec![ + "Login Form Card", + "Split Login with GitHub", + "Social Login with Email Fallback", + "Split Login with Social Buttons", + ] + ); + + for block in blocks { + assert_eq!(block.block_title, block.block_id_kebab.to_title()); + } + } + + #[test] + fn sidenav_block_titles_are_accurate_and_consistent() { + let blocks = BlockEntry::get_sidenav_blocks(); + let block_titles = titles(&blocks); + + assert_eq!( + block_titles, + vec![ + "Sidenav with Grouped Sections", + "Sidenav with Collapsible Menus", + "Sidenav with Submenus", + "Floating Sidenav with Submenus", + "Sidenav with Collapsible Submenus", + "Sidenav with Dropdown Submenus", + "Collapsible Sidenav with Icons", + "Inset Sidenav with Secondary Navigation", + "Nested Sidenav with Route-Based Navigation", + "Sidenav with Search", + "Right-Side Sidenav", + ] + ); + + for block in blocks { + assert_eq!(block.block_title, block.block_id_kebab.to_title()); + } + } + + #[test] + fn login_blocks_include_password_field_security_and_usability_features() { + let login_sources = [ + include_str!("../../../../app_crates/registry/src/blocks/login01.rs"), + include_str!("../../../../app_crates/registry/src/blocks/login02.rs"), + include_str!("../../../../app_crates/registry/src/blocks/login03.rs"), + include_str!("../../../../app_crates/registry/src/blocks/login04.rs"), + ]; + + for source in login_sources { + assert!(source.contains("autocomplete=\"current-password\"")); + assert!(source.contains("minlength=8")); + assert!(source.contains("Show password")); + assert!(source.contains("Hide password")); + } + } +} diff --git a/app_crates/registry/src/blocks/login01.rs b/app_crates/registry/src/blocks/login01.rs index 4e9ebf2..663abf8 100644 --- a/app_crates/registry/src/blocks/login01.rs +++ b/app_crates/registry/src/blocks/login01.rs @@ -1,3 +1,4 @@ +use icons::{Eye, EyeOff}; use leptos::prelude::*; use crate::ui::button::{Button, ButtonVariant}; @@ -6,11 +7,21 @@ use crate::ui::input::Input; use crate::ui::label::Label; /* - * title: Simple Login form + * title: Login Form Card */ #[component] pub fn Login01() -> impl IntoView { + let show_password = RwSignal::new(false); + let password_input_ref = NodeRef::::new(); + + let toggle_password_visibility = move |_| { + show_password.update(|value| *value = !*value); + if let Some(input) = password_input_ref.get() { + input.set_type(if show_password.get_untracked() { "text" } else { "password" }); + } + }; + view! {
@@ -28,6 +39,7 @@ pub fn Login01() -> impl IntoView { @@ -42,7 +54,45 @@ pub fn Login01() -> impl IntoView { Forgot your password?
- +
+ + +
diff --git a/app_crates/registry/src/blocks/login02.rs b/app_crates/registry/src/blocks/login02.rs index 3920862..30411a1 100644 --- a/app_crates/registry/src/blocks/login02.rs +++ b/app_crates/registry/src/blocks/login02.rs @@ -1,4 +1,4 @@ -use icons::{GalleryVerticalEnd, Github}; +use icons::{Eye, EyeOff, GalleryVerticalEnd, Github}; use leptos::prelude::*; use crate::ui::button::{Button, ButtonVariant}; @@ -6,11 +6,21 @@ use crate::ui::input::Input; use crate::ui::label::Label; /* - * title: Login with Social Auth + * title: Split Login with GitHub */ #[component] pub fn Login02() -> impl IntoView { + let show_password = RwSignal::new(false); + let password_input_ref = NodeRef::::new(); + + let toggle_password_visibility = move |_| { + show_password.update(|value| *value = !*value); + if let Some(input) = password_input_ref.get() { + input.set_type(if show_password.get_untracked() { "text" } else { "password" }); + } + }; + view! {
@@ -37,6 +47,7 @@ pub fn Login02() -> impl IntoView { @@ -48,7 +59,45 @@ pub fn Login02() -> impl IntoView { Forgot your password?
- +
+ + +
+
+