add column and alias are separate
This commit is contained in:
@@ -2,8 +2,8 @@
|
||||
//!
|
||||
//! The pages share a state type and an action switcher, so what differs
|
||||
//! between them here is only which panel they render. The column vocabulary
|
||||
//! is threaded through the two that need it, because askama resolves those
|
||||
//! fields on the struct rather than on `page`.
|
||||
//! is threaded through the add-column templates, because askama resolves
|
||||
//! those fields on the struct rather than on `page`.
|
||||
|
||||
use askama::Template;
|
||||
|
||||
@@ -15,25 +15,25 @@ use crate::{
|
||||
|
||||
use super::state::TableDefinitionPageState;
|
||||
|
||||
/// GET /admin/tables/columns
|
||||
/// GET /admin/tables/columns/add
|
||||
#[derive(Template)]
|
||||
#[template(path = "pages/admin/table_definition/columns.html")]
|
||||
struct ColumnsPage<'a> {
|
||||
#[template(path = "pages/admin/table_definition/add_columns.html")]
|
||||
struct AddColumnsPage<'a> {
|
||||
nav: Nav,
|
||||
page: &'a TableDefinitionPageState,
|
||||
column_types: Vec<String>,
|
||||
temporal_types: Vec<String>,
|
||||
gtin_types: Vec<String>,
|
||||
currency_codes: &'static [&'static str],
|
||||
/// False, as on the fragment: the page embeds both, and the outcome is
|
||||
/// reported once, at the top.
|
||||
/// False, as on the fragment: the page reports the outcome once, at the
|
||||
/// top of the panel containing this fragment.
|
||||
standalone_column_panel: bool,
|
||||
}
|
||||
|
||||
/// The `#table-panel` swap on the columns page.
|
||||
/// The `#table-panel` swap on the add-columns page.
|
||||
#[derive(Template)]
|
||||
#[template(path = "pages/admin/table_definition/columns_panel.html")]
|
||||
struct ColumnsFragment<'a> {
|
||||
#[template(path = "pages/admin/table_definition/add_columns_panel.html")]
|
||||
struct AddColumnsFragment<'a> {
|
||||
nav: Nav,
|
||||
page: &'a TableDefinitionPageState,
|
||||
column_types: Vec<String>,
|
||||
@@ -58,6 +58,22 @@ struct ColumnPanelFragment<'a> {
|
||||
standalone_column_panel: bool,
|
||||
}
|
||||
|
||||
/// GET /admin/tables/presentation
|
||||
#[derive(Template)]
|
||||
#[template(path = "pages/admin/table_definition/presentation.html")]
|
||||
struct PresentationPage<'a> {
|
||||
nav: Nav,
|
||||
page: &'a TableDefinitionPageState,
|
||||
}
|
||||
|
||||
/// The `#table-panel` swap on the column-presentation page.
|
||||
#[derive(Template)]
|
||||
#[template(path = "pages/admin/table_definition/presentation_panel.html")]
|
||||
struct PresentationFragment<'a> {
|
||||
nav: Nav,
|
||||
page: &'a TableDefinitionPageState,
|
||||
}
|
||||
|
||||
/// GET /admin/tables/delete
|
||||
#[derive(Template)]
|
||||
#[template(path = "pages/admin/table_definition/delete.html")]
|
||||
@@ -111,8 +127,8 @@ struct HistoryPage<'a> {
|
||||
page: &'a TableDefinitionPageState,
|
||||
}
|
||||
|
||||
pub(crate) fn render_columns_page(page: &TableDefinitionPageState) -> String {
|
||||
render(&ColumnsPage {
|
||||
pub(crate) fn render_add_columns_page(page: &TableDefinitionPageState) -> String {
|
||||
render(&AddColumnsPage {
|
||||
nav: page.nav.clone(),
|
||||
page,
|
||||
// Asking the draft, so the picker can only ever offer what the draft
|
||||
@@ -125,8 +141,8 @@ pub(crate) fn render_columns_page(page: &TableDefinitionPageState) -> String {
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn render_columns_fragment(page: &TableDefinitionPageState) -> String {
|
||||
render(&ColumnsFragment {
|
||||
pub(crate) fn render_add_columns_fragment(page: &TableDefinitionPageState) -> String {
|
||||
render(&AddColumnsFragment {
|
||||
nav: page.nav.clone(),
|
||||
page,
|
||||
column_types: page.columns.offered_types(),
|
||||
@@ -147,6 +163,20 @@ pub(crate) fn render_column_panel(page: &TableDefinitionPageState) -> String {
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn render_presentation_page(page: &TableDefinitionPageState) -> String {
|
||||
render(&PresentationPage {
|
||||
nav: page.nav.clone(),
|
||||
page,
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn render_presentation_fragment(page: &TableDefinitionPageState) -> String {
|
||||
render(&PresentationFragment {
|
||||
nav: page.nav.clone(),
|
||||
page,
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn render_delete_page(page: &TableDefinitionPageState) -> String {
|
||||
render(&DeletePage {
|
||||
nav: page.nav.clone(),
|
||||
@@ -262,7 +292,7 @@ mod tests {
|
||||
error: None,
|
||||
sql: None,
|
||||
generated: Vec::new(),
|
||||
active: "columns",
|
||||
active: "presentation",
|
||||
}
|
||||
}
|
||||
|
||||
@@ -273,16 +303,18 @@ mod tests {
|
||||
let mut state = page();
|
||||
|
||||
for (active, html) in [
|
||||
("columns", render_columns_page(&state)),
|
||||
("add-columns", render_add_columns_page(&state)),
|
||||
("presentation", render_presentation_page(&state)),
|
||||
("delete", render_delete_page(&state)),
|
||||
] {
|
||||
state.active = active;
|
||||
assert!(!html.contains("Template error"), "{html}");
|
||||
}
|
||||
|
||||
let html = render_columns_page(&state);
|
||||
let html = render_presentation_page(&state);
|
||||
for route in [
|
||||
"/admin/tables/columns?profile=billing",
|
||||
"/admin/tables/columns/add?profile=billing",
|
||||
"/admin/tables/presentation?profile=billing",
|
||||
"/admin/tables/delete?profile=billing",
|
||||
"/admin/profiles/copy?profile=billing",
|
||||
"/admin/tables/from-template?profile=billing",
|
||||
@@ -308,7 +340,7 @@ mod tests {
|
||||
assert!(html.contains("/admin/tables/delete"));
|
||||
assert!(html.contains("Type <code>invoice</code> to confirm"));
|
||||
// The other writes are links in the switcher, not forms on the page.
|
||||
assert!(!html.contains("/admin/tables/presentation"));
|
||||
assert!(!html.contains(r#"hx-post="/admin/tables/presentation""#));
|
||||
assert!(!html.contains("/admin/profiles/copy?profile=billing\" method"));
|
||||
}
|
||||
|
||||
@@ -368,13 +400,16 @@ mod tests {
|
||||
assert!(html.contains("backend's own"));
|
||||
assert!(!html.contains(r#"name="confirm_table_name""#));
|
||||
|
||||
let html = render_columns_page(&state);
|
||||
assert!(!html.contains("/admin/tables/presentation"));
|
||||
let html = render_add_columns_page(&state);
|
||||
assert!(!html.contains(r#"id="column-form""#));
|
||||
|
||||
let html = render_presentation_page(&state);
|
||||
assert!(!html.contains(r#"name="aliases""#));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn column_presentation_posts_stable_ids_aliases_and_order_controls() {
|
||||
let html = render_columns_page(&page());
|
||||
let html = render_presentation_page(&page());
|
||||
|
||||
assert!(html.contains(r#"hx-post="/admin/tables/presentation""#), "{html}");
|
||||
assert!(html.contains(r#"name="column_ids" value="1""#), "{html}");
|
||||
@@ -383,6 +418,17 @@ mod tests {
|
||||
assert!(html.contains(r#"name="action" value="save""#), "{html}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn adding_columns_and_presentation_are_separate_pages() {
|
||||
let add_html = render_add_columns_page(&page());
|
||||
assert!(add_html.contains(r#"id="column-form""#), "{add_html}");
|
||||
assert!(!add_html.contains(r#"name="aliases""#), "{add_html}");
|
||||
|
||||
let presentation_html = render_presentation_page(&page());
|
||||
assert!(presentation_html.contains(r#"name="aliases""#), "{presentation_html}");
|
||||
assert!(!presentation_html.contains(r#"id="column-form""#), "{presentation_html}");
|
||||
}
|
||||
|
||||
/// The profile-wide pages need only a profile, and say so by still
|
||||
/// rendering with no table selected.
|
||||
#[test]
|
||||
@@ -429,7 +475,7 @@ mod tests {
|
||||
let html = render_column_panel(&state);
|
||||
|
||||
assert!(!html.contains("Template error"), "{html}");
|
||||
assert!(html.contains("/admin/tables/columns/builder"));
|
||||
assert!(html.contains("/admin/tables/columns/add/builder"));
|
||||
// Escaped, because it is an attribute: `&` is what a browser reads
|
||||
// back as the `&` separating the two parameters.
|
||||
assert!(html.contains("?profile=billing&table=invoice"));
|
||||
@@ -548,10 +594,10 @@ mod tests {
|
||||
#[test]
|
||||
fn a_failure_is_shown_as_a_dialog_as_well_as_an_alert() {
|
||||
let mut state = page();
|
||||
assert!(!render_columns_fragment(&state).contains(r#"role="dialog""#));
|
||||
assert!(!render_add_columns_fragment(&state).contains(r#"role="dialog""#));
|
||||
|
||||
state.error = Some("That column already exists.".to_string());
|
||||
let html = render_columns_fragment(&state);
|
||||
let html = render_add_columns_fragment(&state);
|
||||
assert!(html.contains(r#"role="dialog""#));
|
||||
// Once in the inline alert, once in the dialog — and not a third time
|
||||
// from the column panel the fragment embeds.
|
||||
@@ -580,7 +626,7 @@ mod tests {
|
||||
state.status = Some("1 column added to `invoice`.".to_string());
|
||||
state.sql = Some("ALTER TABLE \"billing\".\"invoice\" ADD COLUMN …".to_string());
|
||||
|
||||
let html = render_columns_fragment(&state);
|
||||
let html = render_add_columns_fragment(&state);
|
||||
|
||||
assert!(html.contains("ALTER TABLE"));
|
||||
assert!(html.contains("1 column added"));
|
||||
@@ -591,7 +637,7 @@ mod tests {
|
||||
let mut state = page();
|
||||
state.selection.profile = "__global".to_string();
|
||||
|
||||
let html = render_columns_page(&state);
|
||||
let html = render_presentation_page(&state);
|
||||
|
||||
assert!(!html.contains("Template error"), "{html}");
|
||||
assert!(html.contains("Global — all profiles"));
|
||||
|
||||
Reference in New Issue
Block a user