mirror of
https://github.com/leptos-rs/leptos
synced 2024-11-10 06:44:17 +00:00
parent
8bdb427133
commit
7bce4de682
14 changed files with 689 additions and 21 deletions
|
@ -38,7 +38,7 @@ pub fn Stories(cx: Scope) -> impl IntoView {
|
||||||
let (pending, set_pending) = create_signal(cx, false);
|
let (pending, set_pending) = create_signal(cx, false);
|
||||||
|
|
||||||
let hide_more_link =
|
let hide_more_link =
|
||||||
move || pending() || stories.read(cx).unwrap_or(None).unwrap_or_default().len() < 28;
|
move |cx| pending() || stories.read(cx).unwrap_or(None).unwrap_or_default().len() < 28;
|
||||||
|
|
||||||
view! {
|
view! {
|
||||||
cx,
|
cx,
|
||||||
|
@ -65,9 +65,12 @@ pub fn Stories(cx: Scope) -> impl IntoView {
|
||||||
}}
|
}}
|
||||||
</span>
|
</span>
|
||||||
<span>"page " {page}</span>
|
<span>"page " {page}</span>
|
||||||
|
<Transition
|
||||||
|
fallback=move || view! { cx, <p>"Loading..."</p> }
|
||||||
|
>
|
||||||
<span class="page-link"
|
<span class="page-link"
|
||||||
class:disabled=hide_more_link
|
class:disabled=move || hide_more_link(cx)
|
||||||
aria-hidden=hide_more_link
|
aria-hidden=move || hide_more_link(cx)
|
||||||
>
|
>
|
||||||
<a href=move || format!("/{}?page={}", story_type(), page() + 1)
|
<a href=move || format!("/{}?page={}", story_type(), page() + 1)
|
||||||
aria-label="Next Page"
|
aria-label="Next Page"
|
||||||
|
@ -75,6 +78,7 @@ pub fn Stories(cx: Scope) -> impl IntoView {
|
||||||
"more >"
|
"more >"
|
||||||
</a>
|
</a>
|
||||||
</span>
|
</span>
|
||||||
|
</Transition>
|
||||||
</div>
|
</div>
|
||||||
<main class="news-list">
|
<main class="news-list">
|
||||||
<div>
|
<div>
|
||||||
|
|
|
@ -79,9 +79,9 @@ where
|
||||||
cfg_if! {
|
cfg_if! {
|
||||||
if #[cfg(any(feature = "csr", feature = "hydrate"))] {
|
if #[cfg(any(feature = "csr", feature = "hydrate"))] {
|
||||||
if context.ready() {
|
if context.ready() {
|
||||||
orig_child(cx).into_view(cx)
|
Fragment::lazy(Box::new(|| vec![orig_child(cx).into_view(cx)])).into_view(cx)
|
||||||
} else {
|
} else {
|
||||||
fallback().into_view(cx)
|
Fragment::lazy(Box::new(|| vec![fallback().into_view(cx)])).into_view(cx)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
use leptos_reactive::signal_prelude::*;
|
use leptos_reactive::signal_prelude::*;
|
||||||
|
@ -108,7 +108,9 @@ where
|
||||||
let orig_child = Rc::clone(&orig_child);
|
let orig_child = Rc::clone(&orig_child);
|
||||||
move || {
|
move || {
|
||||||
HydrationCtx::continue_from(current_id.clone());
|
HydrationCtx::continue_from(current_id.clone());
|
||||||
DynChild::new(move || orig_child(cx))
|
Fragment::lazy(Box::new(move || {
|
||||||
|
vec![DynChild::new(move || orig_child(cx)).into_view(cx)]
|
||||||
|
}))
|
||||||
.into_view(cx)
|
.into_view(cx)
|
||||||
.render_to_string(cx)
|
.render_to_string(cx)
|
||||||
.to_string()
|
.to_string()
|
||||||
|
@ -119,11 +121,13 @@ where
|
||||||
let current_id = current_id.clone();
|
let current_id = current_id.clone();
|
||||||
move || {
|
move || {
|
||||||
HydrationCtx::continue_from(current_id.clone());
|
HydrationCtx::continue_from(current_id.clone());
|
||||||
DynChild::new(move || orig_child(cx))
|
Fragment::lazy(Box::new(move || {
|
||||||
|
vec![DynChild::new(move || orig_child(cx)).into_view(cx)]
|
||||||
|
}))
|
||||||
.into_view(cx)
|
.into_view(cx)
|
||||||
.into_stream_chunks(cx)
|
.into_stream_chunks(cx)
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
// return the fallback for now, wrapped in fragment identifier
|
// return the fallback for now, wrapped in fragment identifier
|
||||||
|
|
13
leptos/tests/test_examples/suspense-tests/.gitignore
vendored
Normal file
13
leptos/tests/test_examples/suspense-tests/.gitignore
vendored
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
# Generated by Cargo
|
||||||
|
# will have compiled files and executables
|
||||||
|
/target/
|
||||||
|
pkg
|
||||||
|
|
||||||
|
# These are backup files generated by rustfmt
|
||||||
|
**/*.rs.bk
|
||||||
|
|
||||||
|
# node e2e test tools and outputs
|
||||||
|
node_modules/
|
||||||
|
test-results/
|
||||||
|
end2end/playwright-report/
|
||||||
|
playwright/.cache/
|
78
leptos/tests/test_examples/suspense-tests/Cargo.toml
Normal file
78
leptos/tests/test_examples/suspense-tests/Cargo.toml
Normal file
|
@ -0,0 +1,78 @@
|
||||||
|
[package]
|
||||||
|
name = "leptos_start"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
crate-type = ["cdylib", "rlib"]
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
actix-files = { version = "0.6", optional = true }
|
||||||
|
actix-web = { version = "4", optional = true, features = ["macros"] }
|
||||||
|
console_error_panic_hook = "0.1"
|
||||||
|
console_log = "1"
|
||||||
|
cfg-if = "1"
|
||||||
|
leptos = { path = "../../..", default-features = false, features = ["serde"] }
|
||||||
|
leptos_actix = { path = "../../../../integrations/actix", optional = true }
|
||||||
|
leptos_router = { path = "../../../../router", default-features = false }
|
||||||
|
log = "0.4"
|
||||||
|
simple_logger = "4"
|
||||||
|
wasm-bindgen = "0.2"
|
||||||
|
serde = "1.0.159"
|
||||||
|
tokio = { version = "1.27.0", features = ["time"], optional = true }
|
||||||
|
|
||||||
|
[features]
|
||||||
|
hydrate = ["leptos/hydrate", "leptos_router/hydrate"]
|
||||||
|
ssr = [
|
||||||
|
"dep:actix-files",
|
||||||
|
"dep:actix-web",
|
||||||
|
"dep:leptos_actix",
|
||||||
|
"leptos/ssr",
|
||||||
|
"leptos_router/ssr",
|
||||||
|
"dep:tokio",
|
||||||
|
]
|
||||||
|
|
||||||
|
[package.metadata.leptos]
|
||||||
|
# The name used by wasm-bindgen/cargo-leptos for the JS/WASM bundle. Defaults to the crate name
|
||||||
|
output-name = "leptos_start"
|
||||||
|
# The site root folder is where cargo-leptos generate all output. WARNING: all content of this folder will be erased on a rebuild. Use it in your server setup.
|
||||||
|
site-root = "target/site"
|
||||||
|
# The site-root relative folder where all compiled output (JS, WASM and CSS) is written
|
||||||
|
# Defaults to pkg
|
||||||
|
site-pkg-dir = "pkg"
|
||||||
|
# The IP and port (ex: 127.0.0.1:3000) where the server serves the content. Use it in your server setup.
|
||||||
|
site-addr = "127.0.0.1:3000"
|
||||||
|
# The port to use for automatic reload monitoring
|
||||||
|
reload-port = 3001
|
||||||
|
# [Optional] Command to use when running end2end tests. It will run in the end2end dir.
|
||||||
|
# [Windows] for non-WSL use "npx.cmd playwright test"
|
||||||
|
# This binary name can be checked in Powershell with Get-Command npx
|
||||||
|
end2end-cmd = "npx playwright test"
|
||||||
|
end2end-dir = "end2end"
|
||||||
|
# The browserlist query used for optimizing the CSS.
|
||||||
|
browserquery = "defaults"
|
||||||
|
# Set by cargo-leptos watch when building with that tool. Controls whether autoreload JS will be included in the head
|
||||||
|
watch = false
|
||||||
|
# The environment Leptos will run in, usually either "DEV" or "PROD"
|
||||||
|
env = "DEV"
|
||||||
|
# The features to use when compiling the bin target
|
||||||
|
#
|
||||||
|
# Optional. Can be over-ridden with the command line parameter --bin-features
|
||||||
|
bin-features = ["ssr"]
|
||||||
|
|
||||||
|
# If the --no-default-features flag should be used when compiling the bin target
|
||||||
|
#
|
||||||
|
# Optional. Defaults to false.
|
||||||
|
bin-default-features = false
|
||||||
|
|
||||||
|
# The features to use when compiling the lib target
|
||||||
|
#
|
||||||
|
# Optional. Can be over-ridden with the command line parameter --lib-features
|
||||||
|
lib-features = ["hydrate"]
|
||||||
|
|
||||||
|
# If the --no-default-features flag should be used when compiling the lib target
|
||||||
|
#
|
||||||
|
# Optional. Defaults to false.
|
||||||
|
lib-default-features = false
|
||||||
|
|
||||||
|
[workspace]
|
21
leptos/tests/test_examples/suspense-tests/LICENSE
Normal file
21
leptos/tests/test_examples/suspense-tests/LICENSE
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2022 henrik
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
61
leptos/tests/test_examples/suspense-tests/README.md
Normal file
61
leptos/tests/test_examples/suspense-tests/README.md
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
<picture>
|
||||||
|
<source srcset="https://raw.githubusercontent.com/leptos-rs/leptos/main/docs/logos/Leptos_logo_Solid_White.svg" media="(prefers-color-scheme: dark)">
|
||||||
|
<img src="https://raw.githubusercontent.com/leptos-rs/leptos/main/docs/logos/Leptos_logo_RGB.svg" alt="Leptos Logo">
|
||||||
|
</picture>
|
||||||
|
|
||||||
|
# Leptos Starter Template
|
||||||
|
|
||||||
|
This is a template for use with the [Leptos](https://github.com/leptos-rs/leptos) web framework and the [cargo-leptos](https://github.com/akesson/cargo-leptos) tool.
|
||||||
|
|
||||||
|
## Creating your template repo
|
||||||
|
|
||||||
|
If you don't have `cargo-leptos` installed you can install it with
|
||||||
|
|
||||||
|
`cargo install cargo-leptos`
|
||||||
|
|
||||||
|
Then run
|
||||||
|
|
||||||
|
`cargo leptos new --git leptos-rs/start`
|
||||||
|
|
||||||
|
to generate a new project template.
|
||||||
|
|
||||||
|
`cd {projectname}`
|
||||||
|
|
||||||
|
to go to your newly created project.
|
||||||
|
|
||||||
|
Of course you should explore around the project structure, but the best place to start with your application code is in `src/app.rs`.
|
||||||
|
|
||||||
|
## Running your project
|
||||||
|
|
||||||
|
`cargo leptos watch`
|
||||||
|
|
||||||
|
## Installing Additional Tools
|
||||||
|
|
||||||
|
By default, `cargo-leptos` uses `nightly` Rust, `cargo-generate`, and `sass`. If you run into any trouble, you may need to install one or more of these tools.
|
||||||
|
|
||||||
|
1. `rustup toolchain install nightly --allow-downgrade` - make sure you have Rust nightly
|
||||||
|
2. `rustup default nightly` - setup nightly as default, or you can use rust-toolchain file later on
|
||||||
|
3. `rustup target add wasm32-unknown-unknown` - add the ability to compile Rust to WebAssembly
|
||||||
|
4. `cargo install cargo-generate` - install `cargo-generate` binary (should be installed automatically in future)
|
||||||
|
5. `npm install -g sass` - install `dart-sass` (should be optional in future)
|
||||||
|
|
||||||
|
## Executing a Server on a Remote Machine Without the Toolchain
|
||||||
|
After running a `cargo leptos build --release` the minimum files needed are:
|
||||||
|
|
||||||
|
1. The server binary located in `target/server/release`
|
||||||
|
2. The `site` directory and all files within located in `target/site`
|
||||||
|
|
||||||
|
Copy these files to your remote server. The directory structure should be:
|
||||||
|
```text
|
||||||
|
leptos_start
|
||||||
|
site/
|
||||||
|
```
|
||||||
|
Set the following enviornment variables (updating for your project as needed):
|
||||||
|
```text
|
||||||
|
LEPTOS_OUTPUT_NAME="leptos_start"
|
||||||
|
LEPTOS_SITE_ROOT="site"
|
||||||
|
LEPTOS_SITE_PKG_DIR="pkg"
|
||||||
|
LEPTOS_SITE_ADDR="127.0.0.1:3000"
|
||||||
|
LEPTOS_RELOAD_PORT="3001"
|
||||||
|
```
|
||||||
|
Finally, run the server binary.
|
74
leptos/tests/test_examples/suspense-tests/end2end/package-lock.json
generated
Normal file
74
leptos/tests/test_examples/suspense-tests/end2end/package-lock.json
generated
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
{
|
||||||
|
"name": "end2end",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"lockfileVersion": 2,
|
||||||
|
"requires": true,
|
||||||
|
"packages": {
|
||||||
|
"": {
|
||||||
|
"name": "end2end",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"license": "ISC",
|
||||||
|
"devDependencies": {
|
||||||
|
"@playwright/test": "^1.28.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@playwright/test": {
|
||||||
|
"version": "1.28.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.28.0.tgz",
|
||||||
|
"integrity": "sha512-vrHs5DFTPwYox5SGKq/7TDn/S4q6RA1zArd7uhO6EyP9hj3XgZBBM12ktMbnDQNxh/fL1IUKsTNLxihmsU38lQ==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@types/node": "*",
|
||||||
|
"playwright-core": "1.28.0"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"playwright": "cli.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@types/node": {
|
||||||
|
"version": "18.11.9",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.9.tgz",
|
||||||
|
"integrity": "sha512-CRpX21/kGdzjOpFsZSkcrXMGIBWMGNIHXXBVFSH+ggkftxg+XYP20TESbh+zFvFj3EQOl5byk0HTRn1IL6hbqg==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"node_modules/playwright-core": {
|
||||||
|
"version": "1.28.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.28.0.tgz",
|
||||||
|
"integrity": "sha512-nJLknd28kPBiCNTbqpu6Wmkrh63OEqJSFw9xOfL9qxfNwody7h6/L3O2dZoWQ6Oxcm0VOHjWmGiCUGkc0X3VZA==",
|
||||||
|
"dev": true,
|
||||||
|
"bin": {
|
||||||
|
"playwright": "cli.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@playwright/test": {
|
||||||
|
"version": "1.28.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.28.0.tgz",
|
||||||
|
"integrity": "sha512-vrHs5DFTPwYox5SGKq/7TDn/S4q6RA1zArd7uhO6EyP9hj3XgZBBM12ktMbnDQNxh/fL1IUKsTNLxihmsU38lQ==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"@types/node": "*",
|
||||||
|
"playwright-core": "1.28.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@types/node": {
|
||||||
|
"version": "18.11.9",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.9.tgz",
|
||||||
|
"integrity": "sha512-CRpX21/kGdzjOpFsZSkcrXMGIBWMGNIHXXBVFSH+ggkftxg+XYP20TESbh+zFvFj3EQOl5byk0HTRn1IL6hbqg==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"playwright-core": {
|
||||||
|
"version": "1.28.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.28.0.tgz",
|
||||||
|
"integrity": "sha512-nJLknd28kPBiCNTbqpu6Wmkrh63OEqJSFw9xOfL9qxfNwody7h6/L3O2dZoWQ6Oxcm0VOHjWmGiCUGkc0X3VZA==",
|
||||||
|
"dev": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
{
|
||||||
|
"name": "end2end",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "",
|
||||||
|
"main": "index.js",
|
||||||
|
"scripts": {},
|
||||||
|
"keywords": [],
|
||||||
|
"author": "",
|
||||||
|
"license": "ISC",
|
||||||
|
"devDependencies": {
|
||||||
|
"@playwright/test": "^1.28.0"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,107 @@
|
||||||
|
import type { PlaywrightTestConfig } from "@playwright/test";
|
||||||
|
import { devices } from "@playwright/test";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read environment variables from file.
|
||||||
|
* https://github.com/motdotla/dotenv
|
||||||
|
*/
|
||||||
|
// require('dotenv').config();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* See https://playwright.dev/docs/test-configuration.
|
||||||
|
*/
|
||||||
|
const config: PlaywrightTestConfig = {
|
||||||
|
testDir: "./tests",
|
||||||
|
/* Maximum time one test can run for. */
|
||||||
|
timeout: 30 * 1000,
|
||||||
|
expect: {
|
||||||
|
/**
|
||||||
|
* Maximum time expect() should wait for the condition to be met.
|
||||||
|
* For example in `await expect(locator).toHaveText();`
|
||||||
|
*/
|
||||||
|
timeout: 5000,
|
||||||
|
},
|
||||||
|
/* Run tests in files in parallel */
|
||||||
|
fullyParallel: true,
|
||||||
|
/* Fail the build on CI if you accidentally left test.only in the source code. */
|
||||||
|
forbidOnly: !!process.env.CI,
|
||||||
|
/* Retry on CI only */
|
||||||
|
retries: process.env.CI ? 2 : 0,
|
||||||
|
/* Opt out of parallel tests on CI. */
|
||||||
|
workers: process.env.CI ? 1 : undefined,
|
||||||
|
/* Reporter to use. See https://playwright.dev/docs/test-reporters */
|
||||||
|
reporter: "html",
|
||||||
|
/* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
|
||||||
|
use: {
|
||||||
|
/* Maximum time each action such as `click()` can take. Defaults to 0 (no limit). */
|
||||||
|
actionTimeout: 0,
|
||||||
|
/* Base URL to use in actions like `await page.goto('/')`. */
|
||||||
|
// baseURL: 'http://localhost:3000',
|
||||||
|
|
||||||
|
/* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
|
||||||
|
trace: "on-first-retry",
|
||||||
|
},
|
||||||
|
|
||||||
|
/* Configure projects for major browsers */
|
||||||
|
projects: [
|
||||||
|
{
|
||||||
|
name: "chromium",
|
||||||
|
use: {
|
||||||
|
...devices["Desktop Chrome"],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
name: "firefox",
|
||||||
|
use: {
|
||||||
|
...devices["Desktop Firefox"],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
name: "webkit",
|
||||||
|
use: {
|
||||||
|
...devices["Desktop Safari"],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
/* Test against mobile viewports. */
|
||||||
|
// {
|
||||||
|
// name: 'Mobile Chrome',
|
||||||
|
// use: {
|
||||||
|
// ...devices['Pixel 5'],
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// name: 'Mobile Safari',
|
||||||
|
// use: {
|
||||||
|
// ...devices['iPhone 12'],
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
|
||||||
|
/* Test against branded browsers. */
|
||||||
|
// {
|
||||||
|
// name: 'Microsoft Edge',
|
||||||
|
// use: {
|
||||||
|
// channel: 'msedge',
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// name: 'Google Chrome',
|
||||||
|
// use: {
|
||||||
|
// channel: 'chrome',
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
],
|
||||||
|
|
||||||
|
/* Folder for test artifacts such as screenshots, videos, traces, etc. */
|
||||||
|
// outputDir: 'test-results/',
|
||||||
|
|
||||||
|
/* Run your local dev server before starting the tests */
|
||||||
|
// webServer: {
|
||||||
|
// command: 'npm run start',
|
||||||
|
// port: 3000,
|
||||||
|
// },
|
||||||
|
};
|
||||||
|
|
||||||
|
export default config;
|
|
@ -0,0 +1,9 @@
|
||||||
|
import { test, expect } from "@playwright/test";
|
||||||
|
|
||||||
|
test("homepage has title and links to intro page", async ({ page }) => {
|
||||||
|
await page.goto("http://localhost:3000/");
|
||||||
|
|
||||||
|
await expect(page).toHaveTitle("Welcome to Leptos");
|
||||||
|
|
||||||
|
await expect(page.locator("h1")).toHaveText("Welcome to Leptos!");
|
||||||
|
});
|
219
leptos/tests/test_examples/suspense-tests/src/app.rs
Normal file
219
leptos/tests/test_examples/suspense-tests/src/app.rs
Normal file
|
@ -0,0 +1,219 @@
|
||||||
|
use leptos::*;
|
||||||
|
use leptos_router::*;
|
||||||
|
|
||||||
|
#[server(OneSecondFn "/api")]
|
||||||
|
async fn one_second_fn(query: ()) -> Result<(), ServerFnError> {
|
||||||
|
tokio::time::sleep(tokio::time::Duration::from_secs(1)).await;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[server(TwoSecondFn "/api")]
|
||||||
|
async fn two_second_fn(query: ()) -> Result<(), ServerFnError> {
|
||||||
|
tokio::time::sleep(tokio::time::Duration::from_secs(2)).await;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[component]
|
||||||
|
pub fn App(cx: Scope) -> impl IntoView {
|
||||||
|
let style = r#"
|
||||||
|
nav {
|
||||||
|
display: flex;
|
||||||
|
width: 100%;
|
||||||
|
justify-content: space-around;
|
||||||
|
}
|
||||||
|
|
||||||
|
[aria-current] {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
"#;
|
||||||
|
view! {
|
||||||
|
cx,
|
||||||
|
<style>{style}</style>
|
||||||
|
<Router>
|
||||||
|
<nav>
|
||||||
|
<A href="/out-of-order">"Out-of-Order"</A>
|
||||||
|
<A href="/in-order">"In-Order"</A>
|
||||||
|
<A href="/async">"Async"</A>
|
||||||
|
</nav>
|
||||||
|
<main>
|
||||||
|
<Routes>
|
||||||
|
<Route
|
||||||
|
path=""
|
||||||
|
view=|cx| view! { cx, <Redirect path="/out-of-order"/> }
|
||||||
|
/>
|
||||||
|
// out-of-order
|
||||||
|
<Route
|
||||||
|
path="out-of-order"
|
||||||
|
view=|cx| view! { cx,
|
||||||
|
<SecondaryNav/>
|
||||||
|
<h1>"Out-of-Order"</h1>
|
||||||
|
<Outlet/>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<Route path="" view=|cx| view! { cx, <Nested/> }/>
|
||||||
|
<Route path="single" view=|cx| view! { cx, <Single/> }/>
|
||||||
|
<Route path="parallel" view=|cx| view! { cx, <Parallel/> }/>
|
||||||
|
<Route path="inside-component" view=|cx| view! { cx, <InsideComponent/> }/>
|
||||||
|
</Route>
|
||||||
|
// in-order
|
||||||
|
<Route
|
||||||
|
path="in-order"
|
||||||
|
ssr=SsrMode::InOrder
|
||||||
|
view=|cx| view! { cx,
|
||||||
|
<SecondaryNav/>
|
||||||
|
<h1>"In-Order"</h1>
|
||||||
|
<Outlet/>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<Route path="" view=|cx| view! { cx, <Nested/> }/>
|
||||||
|
<Route path="single" view=|cx| view! { cx, <Single/> }/>
|
||||||
|
<Route path="parallel" view=|cx| view! { cx, <Parallel/> }/>
|
||||||
|
<Route path="inside-component" view=|cx| view! { cx, <InsideComponent/> }/>
|
||||||
|
</Route>
|
||||||
|
// async
|
||||||
|
<Route
|
||||||
|
path="async"
|
||||||
|
ssr=SsrMode::Async
|
||||||
|
view=|cx| view! { cx,
|
||||||
|
<SecondaryNav/>
|
||||||
|
<h1>"Async"</h1>
|
||||||
|
<Outlet/>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<Route path="" view=|cx| view! { cx, <Nested/> }/>
|
||||||
|
<Route path="single" view=|cx| view! { cx, <Single/> }/>
|
||||||
|
<Route path="parallel" view=|cx| view! { cx, <Parallel/> }/>
|
||||||
|
<Route path="inside-component" view=|cx| view! { cx, <InsideComponent/> }/>
|
||||||
|
</Route>
|
||||||
|
</Routes>
|
||||||
|
</main>
|
||||||
|
</Router>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[component]
|
||||||
|
fn SecondaryNav(cx: Scope) -> impl IntoView {
|
||||||
|
view! { cx,
|
||||||
|
<nav>
|
||||||
|
<A href="" exact=true>"Nested"</A>
|
||||||
|
<A href="single">"Single"</A>
|
||||||
|
<A href="parallel">"Parallel"</A>
|
||||||
|
<A href="inside-component">"Inside Component"</A>
|
||||||
|
</nav>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[component]
|
||||||
|
fn Nested(cx: Scope) -> impl IntoView {
|
||||||
|
let one_second = create_resource(cx, || (), one_second_fn);
|
||||||
|
let two_second = create_resource(cx, || (), two_second_fn);
|
||||||
|
|
||||||
|
view! { cx,
|
||||||
|
<div>
|
||||||
|
<Suspense fallback=|| "Loading 1...">
|
||||||
|
"One Second: "
|
||||||
|
{move || {
|
||||||
|
one_second.read(cx).map(|_| "Loaded 1!")
|
||||||
|
}}
|
||||||
|
<br/><br/>
|
||||||
|
<Suspense fallback=|| "Loading 2...">
|
||||||
|
"Two Second: "
|
||||||
|
{move || {
|
||||||
|
two_second.read(cx).map(|_| "Loaded 2!")
|
||||||
|
}}
|
||||||
|
</Suspense>
|
||||||
|
</Suspense>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[component]
|
||||||
|
fn Parallel(cx: Scope) -> impl IntoView {
|
||||||
|
let one_second = create_resource(cx, || (), one_second_fn);
|
||||||
|
let two_second = create_resource(cx, || (), two_second_fn);
|
||||||
|
let (count, set_count) = create_signal(cx, 0);
|
||||||
|
|
||||||
|
view! { cx,
|
||||||
|
<div>
|
||||||
|
<Suspense fallback=|| "Loading 1...">
|
||||||
|
"One Second: "
|
||||||
|
{move || {
|
||||||
|
one_second.read(cx).map(move |_| view! { cx,
|
||||||
|
"Loaded 1"
|
||||||
|
<button on:click=move |_| set_count.update(|n| *n += 1)>
|
||||||
|
{count}
|
||||||
|
</button>
|
||||||
|
})
|
||||||
|
}}
|
||||||
|
</Suspense>
|
||||||
|
<br/><br/>
|
||||||
|
<Suspense fallback=|| "Loading 2...">
|
||||||
|
"Two Second: "
|
||||||
|
{move || {
|
||||||
|
two_second.read(cx).map(move |_| view! { cx,
|
||||||
|
"Loaded 2"
|
||||||
|
<button on:click=move |_| set_count.update(|n| *n += 1)>
|
||||||
|
{count}
|
||||||
|
</button>
|
||||||
|
})
|
||||||
|
}}
|
||||||
|
</Suspense>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[component]
|
||||||
|
fn Single(cx: Scope) -> impl IntoView {
|
||||||
|
let one_second = create_resource(cx, || (), one_second_fn);
|
||||||
|
let (count, set_count) = create_signal(cx, 0);
|
||||||
|
|
||||||
|
view! { cx,
|
||||||
|
<div>
|
||||||
|
<Suspense fallback=|| "Loading 1...">
|
||||||
|
"One Second: "
|
||||||
|
{move || {
|
||||||
|
one_second.read(cx).map(|_| "Loaded 1!")
|
||||||
|
}}
|
||||||
|
</Suspense>
|
||||||
|
<p>"Children following " <code>"<Suspense/>"</code> " should hydrate properly."</p>
|
||||||
|
<div>
|
||||||
|
<button on:click=move |_| set_count.update(|n| *n += 1)>
|
||||||
|
{count}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[component]
|
||||||
|
fn InsideComponent(cx: Scope) -> impl IntoView {
|
||||||
|
let (count, set_count) = create_signal(cx, 0);
|
||||||
|
|
||||||
|
view! { cx,
|
||||||
|
<div>
|
||||||
|
<p><code>"<Suspense/>"</code> " inside another component should work."</p>
|
||||||
|
<InsideComponentChild/>
|
||||||
|
<p>"Children following " <code>"<Suspense/>"</code> " should hydrate properly."</p>
|
||||||
|
<div>
|
||||||
|
<button on:click=move |_| set_count.update(|n| *n += 1)>
|
||||||
|
{count}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[component]
|
||||||
|
fn InsideComponentChild(cx: Scope) -> impl IntoView {
|
||||||
|
let one_second = create_resource(cx, || (), one_second_fn);
|
||||||
|
view! { cx,
|
||||||
|
<Suspense fallback=|| "Loading 1...">
|
||||||
|
"One Second: "
|
||||||
|
{move || {
|
||||||
|
one_second.read(cx).map(|_| "Loaded 1!")
|
||||||
|
}}
|
||||||
|
</Suspense>
|
||||||
|
}
|
||||||
|
}
|
23
leptos/tests/test_examples/suspense-tests/src/lib.rs
Normal file
23
leptos/tests/test_examples/suspense-tests/src/lib.rs
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
pub mod app;
|
||||||
|
use cfg_if::cfg_if;
|
||||||
|
|
||||||
|
cfg_if! {
|
||||||
|
if #[cfg(feature = "hydrate")] {
|
||||||
|
|
||||||
|
use wasm_bindgen::prelude::wasm_bindgen;
|
||||||
|
|
||||||
|
#[wasm_bindgen]
|
||||||
|
pub fn hydrate() {
|
||||||
|
use app::*;
|
||||||
|
use leptos::*;
|
||||||
|
|
||||||
|
// initializes logging using the `log` crate
|
||||||
|
_ = console_log::init_with_level(log::Level::Debug);
|
||||||
|
console_error_panic_hook::set_once();
|
||||||
|
|
||||||
|
leptos::mount_to_body(move |cx| {
|
||||||
|
view! { cx, <App/> }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
42
leptos/tests/test_examples/suspense-tests/src/main.rs
Normal file
42
leptos/tests/test_examples/suspense-tests/src/main.rs
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
#[cfg(feature = "ssr")]
|
||||||
|
#[actix_web::main]
|
||||||
|
async fn main() -> std::io::Result<()> {
|
||||||
|
use actix_files::Files;
|
||||||
|
use actix_web::*;
|
||||||
|
use leptos::*;
|
||||||
|
use leptos_actix::{generate_route_list, LeptosRoutes};
|
||||||
|
use leptos_start::app::*;
|
||||||
|
|
||||||
|
let conf = get_configuration(None).await.unwrap();
|
||||||
|
let addr = conf.leptos_options.site_addr;
|
||||||
|
// Generate the list of routes in your Leptos App
|
||||||
|
let routes = generate_route_list(|cx| view! { cx, <App/> });
|
||||||
|
|
||||||
|
OneSecondFn::register().unwrap();
|
||||||
|
TwoSecondFn::register().unwrap();
|
||||||
|
|
||||||
|
HttpServer::new(move || {
|
||||||
|
let leptos_options = &conf.leptos_options;
|
||||||
|
let site_root = &leptos_options.site_root;
|
||||||
|
|
||||||
|
App::new()
|
||||||
|
.route("/api/{tail:.*}", leptos_actix::handle_server_fns())
|
||||||
|
.leptos_routes(
|
||||||
|
leptos_options.to_owned(),
|
||||||
|
routes.to_owned(),
|
||||||
|
|cx| view! { cx, <App/> },
|
||||||
|
)
|
||||||
|
.service(Files::new("/", site_root))
|
||||||
|
//.wrap(middleware::Compress::default())
|
||||||
|
})
|
||||||
|
.bind(addr)?
|
||||||
|
.run()
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "ssr"))]
|
||||||
|
pub fn main() {
|
||||||
|
// no client-side main function
|
||||||
|
// unless we want this to work with e.g., Trunk for pure client-side testing
|
||||||
|
// see lib.rs for hydration function instead
|
||||||
|
}
|
|
@ -219,8 +219,8 @@ fn fragments_to_chunks(
|
||||||
<template id="{fragment_id}f">{html}</template>
|
<template id="{fragment_id}f">{html}</template>
|
||||||
<script>
|
<script>
|
||||||
var id = "{fragment_id}";
|
var id = "{fragment_id}";
|
||||||
var open;
|
var open = undefined;
|
||||||
var close;
|
var close = undefined;
|
||||||
var walker = document.createTreeWalker(document.body, NodeFilter.SHOW_COMMENT);
|
var walker = document.createTreeWalker(document.body, NodeFilter.SHOW_COMMENT);
|
||||||
while(walker.nextNode()) {{
|
while(walker.nextNode()) {{
|
||||||
if(walker.currentNode.textContent == `suspense-open-${{id}}`) {{
|
if(walker.currentNode.textContent == `suspense-open-${{id}}`) {{
|
||||||
|
|
Loading…
Reference in a new issue