use dioxus::prelude::*; use crate::domain::entities::site::{Block, PageContent, Post}; use super::{meta, server::get_posts}; #[component] pub fn Page() -> Element { let page = meta::page(); let info = page.data.info; rsx! { h2 { "Page {info.title}" } match page.data.content { PageContent::Single { content } => rsx! { PostElement { post: content } }, PageContent::Collection { kind: _, collection_id } => { rsx! { SuspenseBoundary { fallback: |_context: SuspenseContext| rsx! { "..." }, Collection { collection_id } } } } } } } #[component] pub fn Collection(collection_id: String) -> Element { let site = meta::site(); let posts = use_server_future(move || get_posts(site.info.domain.clone(), collection_id.clone()))? .suspend()?; let result = match &*posts.read() { Ok(posts) => rsx! { for post in posts.data.clone() { PostElement { post } } }, Err(err) => { rsx! { h1 { "FATAL ERROR: {err}" } } } }; result } #[component] pub fn PostElement(post: Post) -> Element { rsx! { for block in post.blocks { BlockElement { block } } } } #[component] pub fn BlockElement(block: Block) -> Element { match block { Block::Text { text } => rsx! { p { "{text}" } }, Block::Gallery { images } => rsx! { for image in images { img { src: "{image.src}", alt: "{image.caption}" } } }, } } #[cfg(test)] mod tests { use meta::SiteContext; use crate::{ domain::entities::{ cursor::Paginated, site::{Image, SiteInfo}, }, outbound::services::site::{MockSiteService, SiteServiceProvider}, }; use super::*; #[test] fn block_renders_text() { let text = "Hello, world!"; let block = Block::Text { text: text.to_string(), }; let element = rsx! { BlockElement { block } }; let elem_str = dioxus::ssr::render_element(element); assert!(elem_str.contains(text)); } #[test] fn block_gallery_renders_images() { let images = vec![ Image { src: "https://example.com/image1.jpg".to_string(), caption: "Image 1".to_string(), }, Image { src: "https://example.com/image2.jpg".to_string(), caption: "Image 2".to_string(), }, ]; let block = Block::Gallery { images }; let element = rsx! { BlockElement { block } }; let elem_str = dioxus::ssr::render_element(element); assert!(elem_str.contains("<img")); assert!(elem_str.contains("https://example.com/image1.jpg")); assert!(elem_str.contains("https://example.com/image2.jpg")); } #[test] fn post_renders_blocks() { let blocks = vec![ Block::Text { text: "Hello, world!".to_string(), }, Block::Text { text: "Something else!".to_string(), }, ]; let post = Post { blocks }; let element = rsx! { PostElement { post } }; let elem_str = dioxus::ssr::render_element(element); assert!(elem_str.contains("Hello, world!")); assert!(elem_str.contains("Something else!")); } #[test] fn collection_renders_posts() { let mut app = VirtualDom::new(|| { rsx! { Collection { collection_id: "".to_string() } } }); app.provide_root_context(SiteContext { info: SiteInfo { title: "test".to_string(), domain: "test".to_string(), pages: vec![], }, }); let mut mock_service = MockSiteService::new(); mock_service .expect_get_posts() .times(1) .returning(move |_, _, _| { Box::pin(async { Ok(Paginated { data: vec![ Post { blocks: vec![Block::Text { text: "Hello, world!".to_string(), }], }, Post { blocks: vec![Block::Text { text: "Something else!".to_string(), }], }, ], next: None, }) }) }); server_context().insert(SiteServiceProvider::with(mock_service)); app.rebuild_in_place(); let elem_str = dioxus::ssr::render(&app); println!("elem_str: {elem_str}"); assert!(elem_str.contains("Hello, world!")); assert!(elem_str.contains("Something else!")); } }