Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Move styles to sidebar #2366

Merged
merged 4 commits into from
Sep 22, 2023
Merged

Conversation

ada-x64
Copy link
Contributor

@ada-x64 ada-x64 commented Sep 12, 2023

This PR moves the datagrid style custom elements to a new tabbed sidebar called the column settings sidebar.

All active columns now have an edit button which will open the column settings. Expression columns can be edited when inactive as well. Plugins other than the datagrid will have a "styles not implemented" placeholder until we eventually add styles to them.

The PR also adds significant playwright scaffolding for UI tests that are not based on snapshots. This includes updating the yarn file to use the latest version of Playwright.

  • Create tabbed sidebar
  • Move ExprTK editor to tabbed sidebar
  • Move Yew components to tabbed sidebar
  • Hook up plugins and sidebar
  • Make the style buttons on the datagrid plugin open the style sidebar
  • NTH: Datagrid should autoscroll to currently selected column when editing.
  • Remove extraneous files
  • Add tests

KNOWN ISSUES:

  • Column settings sidebar is drawn over the plugin, so it can overlap the focused column.
  • Edit button is a bit sticky. Select a column, change something, then select another column. The edit button should move.
  • Mousedown on the active columns list edit button always opens the sidebar, even if you drag the selected column.

@texodus texodus added the enhancement Feature requests or improvements label Sep 12, 2023
@ada-x64 ada-x64 force-pushed the move-datagrid-styles-to-sidebar branch 5 times, most recently from eb78e37 to dc5e147 Compare September 19, 2023 15:33
Co-authored-by: Davis Silverman <sinistersnare@users.noreply.github.com>
Co-authored-by: Timothy Bess <timbessmail@gmail.com>
Co-authored-by: Broch Stilley <brochington@gmail.com>

Fix datagrid bugs
@ada-x64 ada-x64 force-pushed the move-datagrid-styles-to-sidebar branch from dc5e147 to f4887cb Compare September 19, 2023 18:59
Copy link
Member

@texodus texodus left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the PR! Looks good!

This looks great overall, and is a really cool feature to boot.

One issue we do need to address is default_config in plugin_config being persisted. This is likely a bug introduced in this PR, and while its mostly innocuous it is also a breaking change to the package's public API, specifically the structure of the config returned by save(). Perspective's config structure is frequently persisted in hard-to-update systems like Database tables and may be encoded in non-human-readable format, so it's important we conserve changes to this data structure.

IMO we should fix this before we merge this PR, so we can revert the test changes which assert the modified behavior.

@@ -214,6 +214,7 @@ describe_jupyter(
});

// Check default config
config.plugin_config.default_config = {};
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a bug, we should not be persisting default_config, which is a property that determines the settings for plugin_config when a field is not specified.

Copy link
Contributor Author

@ada-x64 ada-x64 Sep 20, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In order to fix this I added a default_config getter to the plugins which act as an opt-in to the style API. If this is not set, the plugins won't get styled. See style_tab.rs

#[function_component]
pub fn ColumnSettingsSidebar(p: &ColumnSettingsProps) -> Html {
let column_name = match p.selected_column.clone() {
ColumnLocator::Expr(maybe_name) => maybe_name.unwrap_or("New Column".to_string()),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be an abort - returning "New Column" shouldn't ever be reached but if it does, this error condition is just kicked down the road here where it may be difficult to debug.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ColumnLocator::Expr(None) is used when creating a new Expression column. However, we shouldn't be hardcoding the name, so I used the same function as in the expression_editor to generate the name.

{title}
on_close={p.on_close.clone()}
id_prefix="column_settings"
icon={html_template!{
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMO this is overkill - the icon data is metadata that can be passed as a prop, it's never going to be some other DOM structure, in this case the id attribute.

ColumnLocator::Expr(None) => save_expr(v, p),
ColumnLocator::Expr(Some(alias)) => update_expr(alias, &v, p),
_ => {
tracing::error!("Tried to save expression in bad state!")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AttributesTab cannot currently be instantiated for non expression columns (which this error alludes to). We can represent this illegal state in the type system and validate it at compile time by not using ColumnLocator in props, rather just Option<String>.

p.clone(),
);

let alias = yew::use_memo(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't need to be a use_memo, just calculate it in the component body. In this case it's not even conserving the clone() as you must clone into the closure.

/// This function sends the config to the plugin using its `restore` method
fn send_config<T: Serialize + Debug>(
renderer: Renderer,
presentation: Presentation,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you pass these by reference, they will not need to be cloned at the call site.

The main place we want to clone() + move into a function is when spawning the first frame on a new async stack, as the async frame cannot reference the spawning stack.

let view = view.unchecked_into();
elem.update(&view, None, None, false)
.await
.expect("Unable to call update!");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While we don't care if this fails, generally panicking in Yew is bad, as it can leave the app in an inconsistent state. ApiFuture::spawn expects a ApiResult return type which is compatible with update and correctly routes this failure to the Promise.reject() method, both reporting the failure in the console properly and avoiding panic.

/// The Viewer component will then send a
/// "perspective-toggle-column-settings" event to the plugin with the
/// currently selected column. The plugin should respond to the event to
/// highlight the selected column appropriately.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These are the API docs which are published to https://perspective.finos.org/docs/obj/perspective-viewer/ , and unlike the rest of the repo, they should be written from the point-of-view of a developer using <perspective-viewer> in their application, not a developer working on the Perspective codebase itself.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I updated the docs to something I think is more user-friendly. Let me know if it needs more changes.

})
} else {
// new expr
Some(ColumnLocator::Expr(None))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Under what conditions does this function return None? I think this is a bit over-wrapped.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should return None when there is not a currently existing column. I removed the Expr(None) case.

.and_then(|s| s.as_string())
.unwrap_or_default();
serde_json::from_str(&stringval).ok()
}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This has been updated to move the API changes from persistence to a getter.

@texodus texodus merged commit afa7438 into finos:master Sep 22, 2023
12 checks passed
@texodus texodus mentioned this pull request Oct 8, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement Feature requests or improvements
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants