use leptos::callback::Callback;
#[allow(unused_imports)]
use leptos::prelude::Effect;
use leptos::prelude::{
Callable, ClassAttribute, CustomAttribute, ElementChild, GetUntracked, IntoAny, IntoView,
OnAttribute, Signal, component, view,
};
#[allow(unused_imports)]
use std::cell::Cell;
#[allow(unused_imports)]
use std::rc::Rc;
#[cfg(target_arch = "wasm32")]
type LbcSysFile = ();
#[cfg(not(target_arch = "wasm32"))]
type LbcSysFile = ();
use crate::util::{Size, TestAttr};
#[component]
pub fn File(
#[prop(into)]
name: String,
#[prop(into)]
_files: Signal<Vec<LbcSysFile>>,
_update: Callback<Vec<LbcSysFile>>,
#[prop(default = "Choose a file...".to_string().into(), into)]
selector_label: Signal<String>,
#[prop(optional, into)]
classes: Signal<String>,
#[prop(optional, into)]
has_name: Option<Signal<String>>,
#[prop(optional, into)]
right: Signal<bool>,
#[prop(optional, into)]
fullwidth: Signal<bool>,
#[prop(optional, into)]
boxed: Signal<bool>,
#[prop(optional, into)]
multiple: Signal<bool>,
#[prop(optional)]
size: Option<Size>,
#[prop(optional, into)]
test_attr: Option<TestAttr>,
) -> impl IntoView {
let mut class_parts = vec!["file".to_string()];
let extra_classes = classes.get_untracked();
if !extra_classes.trim().is_empty() {
class_parts.push(extra_classes);
}
let has_name_text = has_name.as_ref().map(|signal| signal.get_untracked());
if has_name_text.is_some() {
class_parts.push("has-name".to_string());
}
if right.get_untracked() {
class_parts.push("is-right".to_string());
}
if fullwidth.get_untracked() {
class_parts.push("is-fullwidth".to_string());
}
if boxed.get_untracked() {
class_parts.push("is-boxed".to_string());
}
if let Some(size) = size {
match size {
Size::Small => class_parts.push("is-small".to_string()),
Size::Normal => {}
Size::Medium => class_parts.push("is-medium".to_string()),
Size::Large => class_parts.push("is-large".to_string()),
}
}
let class = class_parts.join(" ");
let selector_label_text = selector_label.get_untracked();
let is_multiple = multiple.get_untracked();
let (data_testid, data_cy) = match &test_attr {
Some(attr) if attr.key == "data-testid" => (Some(attr.value.clone()), None),
Some(attr) if attr.key == "data-cy" => (None, Some(attr.value.clone())),
_ => (None, None),
};
let on_change = {
let update = _update.clone();
move |_ev: leptos::web_sys::Event| {
update.run(Vec::<LbcSysFile>::new());
}
};
view! {
<div
class=class
attr:data-testid=data_testid
attr:data-cy=data_cy
>
<label class="file-label">
<input
type="file"
class="file-input"
name=name.clone()
multiple=is_multiple
on:change=on_change
/>
<span class="file-cta">
<span class="file-icon"></span>
<span class="file-label">
{selector_label_text}
</span>
</span>
{
if let Some(file_name_text) = has_name_text {
view! { <span class="file-name">{file_name_text}</span> }.into_any()
} else {
view! { <></> }.into_any()
}
}
</label>
</div>
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::util::Size;
use leptos::prelude::RenderHtml;
fn noop_update() -> Callback<Vec<super::LbcSysFile>> {
Callback::new(|_files: Vec<super::LbcSysFile>| {})
}
#[test]
fn file_renders_base_class_and_input() {
let html = view! {
<File
name="upload"
_files=Signal::derive(|| Vec::<super::LbcSysFile>::new())
_update=noop_update()
/>
}
.to_html();
assert!(
html.contains(r#"class="file""#),
"expected base 'file' class; got: {}",
html
);
assert!(
html.contains(r#"type="file""#),
"expected file input; got: {}",
html
);
}
#[test]
fn file_applies_size_and_flags() {
let html = view! {
<File
name="upload"
_files=Signal::derive(|| Vec::<super::LbcSysFile>::new())
_update=noop_update()
size=Size::Small
right=true
fullwidth=true
boxed=true
/>
}
.to_html();
assert!(
html.contains("is-small"),
"expected is-small; got: {}",
html
);
assert!(
html.contains("is-right"),
"expected is-right; got: {}",
html
);
assert!(
html.contains("is-fullwidth"),
"expected is-fullwidth; got: {}",
html
);
assert!(
html.contains("is-boxed"),
"expected is-boxed; got: {}",
html
);
}
}
#[cfg(all(test, target_arch = "wasm32"))]
mod wasm_tests {
use super::*;
use crate::util::{Size, TestAttr};
use leptos::prelude::*;
use wasm_bindgen_test::*;
fn noop_update() -> Callback<Vec<super::LbcSysFile>> {
Callback::new(|_files: Vec<super::LbcSysFile>| {})
}
wasm_bindgen_test_configure!(run_in_browser);
#[wasm_bindgen_test]
fn file_renders_test_attr_as_data_testid() {
let html = view! {
<File
name="upload"
_files=Signal::derive(|| Vec::<super::LbcSysFile>::new())
_update=noop_update()
size=Size::Small
test_attr=TestAttr::test_id("file-test")
/>
}
.to_html();
assert!(
html.contains(r#"data-testid="file-test""#),
"expected data-testid attribute; got: {}",
html
);
}
#[wasm_bindgen_test]
fn file_no_test_attr_when_not_provided() {
let html = view! {
<File
name="upload"
_files=Signal::derive(|| Vec::<super::LbcSysFile>::new())
_update=noop_update()
/>
}
.to_html();
assert!(
!html.contains("data-testid") && !html.contains("data-cy"),
"expected no data attribute; got: {}",
html
);
}
}