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

Frames' space allocation behavior can be generalized #601

Open
8 tasks
alexeyraspopov opened this issue Jan 11, 2022 · 0 comments
Open
8 tasks

Frames' space allocation behavior can be generalized #601

alexeyraspopov opened this issue Jan 11, 2022 · 0 comments

Comments

@alexeyraspopov
Copy link
Member

For every distinct type of frame (XYFrame, NetworkFrame, OrdinalFrame) there is Responsive* and Spark* variants which main goal is to define particular space allocation behavior while the original frame assumes strict size being defined by the author. The need for explicit size definition doesn't exist without a reason: both SVG and Canvas require the size to be defined before they render the content within. Physical size being a type of visual values directly affects how exactly the content within should be rendered.

(following rationale is based on my experience and expectations in developing web UIs)

A piece of data visualization is often not a single unit of content on the web page: one might need a column or two of text content, or the viz is a part of application UI, which has plenty of controls, other sources of information and quite possibly a couple of more pieces of data viz. Before a developer steps into adding a data visualization piece there already plenty of work that is done. Given the variety of the devices used for browsing the internet one can never assume a single since of a viewport the app should work within. When you get to adding SVG or Canvas that start requiring to specify very explicit sizes, this is where you can get stuck figuring out all the possible scenarios and outcomes of choosing the wrong size.

This type of consideration never exists when you work with typical text or illustrations content and use flexbox or grid layout. Either the content fluidly defines the behavior of container, or the containers (based on its context) defines how children suppose to allocate space within. Spark* and Responsive* frames seem to be fitting this sort of inversion of control scenarios: despite having a content within (e.g. XYFrame) that was initially working only with predefined size, Responsive frame expects parent container in user space to define how the content should behave thus delegating the control over to the user.

Having this sort of control outside of any library or framework means the user has all the potential and flexibility of existing tools and their experience. This means no artificial limitation to how the library or framework can be integrated in an existing UI.

I still need to learn more about Spark* frames but I'd make a wild guess that the same reasoning is applicable to them as well.

Generalized behavior

Basically, the question is: what if we assume responsive behavior as a default space allocation behavior? size prop can still exist if the strict size is needed. The main reason for this is that it would mean top level Semiotic components are easily integrate-able into whatever UI users already have: notebooks, applications, web documents. It means no additional handling or size control needed within Semiotic components, as well as special API: the implementation of necessary space allocation behavior happens in the most basic way of how things behave on the web page. The users don't need to learn about additional semantics since they just use the basics of the environment they're working with. This also means that the documentation can be simplified to describe the common way of doing things.

It might be too verbose, so here is another look on this problem: if we assume responsive behavior as a default, we still can define a visualization frame that has static size, just because this is how the platform works and we don't even need a special top level API for this.

There plenty of small implementation details that go into this idea, I'll be trying to define them here:

  • useElementSize() is moved to a separate module so it can be shared between frames
  • Frames use their wrappers to compute size if the prop is not provided
  • Frames inline prop to to apply inline block behavior

Implementation

A lot of small implementation details that need to considered and some that I may not be familiar with, but in my mind the described behavior comes down to the following:

function XYFrame(props) {
  let ref = useRef();
  let size = useElementSize(ref);

  // as an option we can pass down some more styles applied to the wrapper
  // however it might be advised to just to keep those on the user side
  let style = { display: props.inline ? "inline-block" : "block", ...props.style };
  
  // in the same way as it happens now, computing all props that Frame needs
  // and the size is already available on this level, regardless of frame type
  let computedProps = { /* whatever <Frame /> needs */ }
  
  return (
    <div style={style} ref={ref}>
      {/* to ensure we don't render Frame with [0, 0] as it would be just waste of time */}
      {size != null ? <Frame size={size} {...computedProps} /> : null}
    </div>
  )
}

Quite possibly we can get rid of the notion of SpanOrDiv as a result of these changes.

Benefits

  • Smaller API surface area
    • on its own, it means users won't need to return to docs once they realize they need fluid version of a frame
    • no ambiguity and questioning "what is the difference"
    • no "Spark" frame that assumes understanding of a term "sparklines" in data viz context (or am I the only one?)
  • Users getting all the flexibility of their grid solution and practices (no more workarounds)

It is worth discussing the rationale behind suggested behavior. One might say that generalization can go even further and Semiotic could just export Frame as the only piece of API needed. Yet, it is about the amount of concepts necessary to get the task done.

Drawbacks

Things like SparkNetworkFrame may include very specific defaults for its original frame implementation which means there are plenty of cases where things can't really "just work".

  • More testing for Spark frames needed before considering generalizing this behavior

How do we teach it

I believe the suggested behavior streamlines the teaching aspect of Semiotic. Frames become no different from other flexible/static containers (e.g. images) and the same rules apply: you either set an explicit size, or assume the fluid container to define available space. If an inline frame behavior needed, one simply set the style to "display: inline-block" and the rest of expected rules apply. I guess it still sounds as over simplification, so having a quick guide with visual examples should definitely help.

  • A guide exists explaining fluid layout and showing examples of responsive frame and inline frames usage

Breaking changes

The suggested behavior means not only some parts of API being removed but also the fact that frames no longer have default size. While I don't expect much of users to be relying on the default size, it is still reasonable to mention this change in changelog and migration guide. The basic suggestion can be something like "if you Semiotic v1 frame doesn't have size prop, it means it uses the default value, so set it to [500, 500] when migrating to avoid layout issues". More testing possibly required.

  • Changelog mentions removed top level components
  • Changelog mentions removed default size value
  • Migration guide explains that size should be defined if it wasn't to avoid breaking existing visualizations

(the issue includes definitions of done that we can use to track related pull requests and the whole initiative progress)

@emeeks, @willingc, looking forward to your feedback and questions. I'm thinking about opening a draft PR with some preliminary implementation changes, to see how far we can actually go.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant