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

Add components for field summary statistics #5

Open
tomwayson opened this issue Apr 21, 2020 · 8 comments
Open

Add components for field summary statistics #5

tomwayson opened this issue Apr 21, 2020 · 8 comments

Comments

@tomwayson
Copy link

tomwayson commented Apr 21, 2020

The purpose of this issue is to think through some of the issues that will come up when trying to design stencil components that can be used both outside and inside the Hub Ember app.

Here's an example of the summary statistic card on a Hub site:

image

In the Hub, this is implemented as a single Ember component that is responsible for both fetching the summary data from the service, and also displaying the data in the page layout. The API (i.e. settings) for that card component are:

{
    "component": {
        "name": "summary-statistic-card",
        "settings": {
            "url": "https://services1.arcgis.com/0MSEUqKaxRlEPj5g/arcgis/rest/services/Drug_Overdose_Death_Data/FeatureServer/0",
            "statFieldName": "Number_of_Deaths2015",
            "statFieldType": "esriFieldTypeDouble",
            "statType": "count",
            "statValueAlign": "left",
            "statValueColor": "#ff0000",
            "decimalPlaces": 2,
            "title": "Drug Overdose Deaths",
            "titleAlign": "left",
            "leadingText": "",
            "trailingText": "...",
            "trailingTextAlign": "left",
            "formatNumberGroupings": true,
            "showAsCurrency": false,
            "currencyCode": "USD",
            "where": "STATE like '%VA%'",
            "itemId": "add4efc0b4d543d8b98108f58457aa3c",
            "itemTitle": "Government Services",
            "layerId": 0,
            "layerName": "Museums"
        }
    },
    "width": 4
}

I propose that we split up the responsibilities of that single Ember component into two Stencil components:

  • <hub-field-statistic>: a high level provider component to fetch the data
  • <hub-statistic>: a lower-level presentational component to show the data.

At least the <hub-statistic> can be used in both the Ember app as well as externally.

Here's a first pass at the <hub-statistic> API (based on the above screenshot):

<hub-statistic
  value=1134
  color="#000"
  decimal-places=0
  name="NHV Confirmed Cases"
>
  <div slot="description">This Information is Updated Daily</div>
  <div slot="details">Source: <a rel="nofollow" href="https://covid-19-1-newhavenct.hub.arcgis.com/datasets/a085f0a76f6b44efbdd185bf40243688_1?showData=true">Cases_public</a></div>
</hub-statistic>

In addition to the attributes above, we'd need additional ones to be able to override the defaults:

  align="right"
  name-align="center"
  trailing-label="%"
  description-align="right"
  use-groupings=false
  currency="USD"

It feels like we should be using CSS (variables?) for at least the alignment, and perhaps the color. Though, color in particular is often specific to each instance of the component (i.e. red bad, green good).

I also wonder about using slots, or at least multiple slots, is right for description and details. I think so.

As for the <hub-field-statistic>, it would need these attributes:

<!-- NOTE: could pass layer-url instead of item-id and layer-id -->
<hub-field-statistic
  item-id="a085f0a76f6b44efbdd185bf40243688"
  layer-id="1"
  field="confirmed"
  statistic="max"
  where="name='NewHaven'"
></hub-field-statistic>

It would fetch the data and then render a <hub-statistic> with value={this.state.value}. It could also provide a default value for the details slot, (i.e. the "Source" link), but we'll need to handle a way to provide translations for "Source" and if we want to that link to point to the Hub site's dataset page (instead of the service URl or the AGO item page), then we'll need additional props (like site-url).

One annoyance is that in order to allow consumers to style the wrapped <hub-statistic> component, <hub-field-statistic> would have to expose and pass down basically all of the child component's props other than value (i.e. prop drilling). It's not ideal, but it's manageable.

In the Ember app we can get around that by using a provider component that yields just the data and then applying the display settings directly to the child component.

@tomwayson
Copy link
Author

One other issue is that the current Ember component also handles the loading (spinner) and error (bootstrap "danger" alert) states, but shows the title the whole time. In order to match this behavior, I think we'd have to move the title (i.e. name out of <hub-statistic>) since there' s nothing like higher order components (HOC) in Stencil. That's OK in this case, b/c a HOC is kind of overkill for what amounts to a styled <div>. I would propose we just expose a CSS class. So anyone that wanted to use <hub-statistic> outside of <hub-field-statistic> would just do something like:

<div class="hub-statistic-title">NHV Confirmed Cases</div>
<hub-statistic
  value=1134
  color="#000"
  decimal-places=0
>
  <div slot="description">This Information is Updated Daily</div>
  <div slot="details">Source: <a rel="nofollow" href="https://covid-19-1-newhavenct.hub.arcgis.com/datasets/a085f0a76f6b44efbdd185bf40243688_1?showData=true">Cases_public</a></div>
</hub-statistic>

I suppose at least the details and possibly description could be handled the same way. Or conversely, perhaps name could be a slot too. Would have to play around w/ this.

@tomwayson
Copy link
Author

Finally, in Hub sites, the statistic font-size is adjusted based on the width of the card. As a first pass, I the easiest thing to do is have <hub-statistic> expose some way for the parent to set the font-size (prop, or CSS variable), and then for now at least we could keep the resizing logic in the Hub. If we can figure a way to move it to the Stencil components that will work reliably in all use cases, we can do that later.

@tomwayson
Copy link
Author

cc @ajturner @mjuniper @mikeringrose

@tomwayson
Copy link
Author

Re: using CSS variables instead of props for styles like alignment, font-size, and maybe color:

It seems that CSS variables are best suited to theming, where the style needs to be applied to all instances of a component. The issue I'm struggling with is how to handle instance-specific styles (i.e. set the font-size for only this instance of a component), especially when they need to be set dynamically via JS.

I know it's possible to use the cascade to statically style a specific instance, like:

.unique-parent-component-class hub-statistic {
  --hub-statistic-align: 'center';
  --hub-statistic-title-align: 'center';
  --hub-statistic-description-align: 'right';
  --hub-statistic-font-size: 32px;
}

However, in the Hub we'd need to be able to set those values via JS in response to changes in the Ember component's settings. I know it's possible to set CSS variables via JS, but I think there are some gotchas and/or limitations (IE). I don't know if you can set them for a specific instance of a component, especially if you wouldn't even necessarily have a unique class (we don't typically generate unique classes for Ember components).

@mjuniper do you think there is a robust way to set CSS variables via JS for a specific instance?

We can always decide to use component props for instance-specific styles, but it feels icky to use props for styles (imagine invoking this w/ align="center" font-size="32px") . I guess the question is, is it any more icky than a CSS variable solution (if there are any)?

@mjuniper
Copy link

In general, I definitely think it is preferable to handle the styling via css vs passing props.

Based on your discussion above regarding css custom props, I'm beginning to think we don't use shadow dom. I'm pretty sure that sidesteps all that.

There's also this: https://stenciljs.com/docs/styling#ie-support
I would need to play around with it but I think that means we could not scope styles to a specific selector in ie.

@tomwayson
Copy link
Author

tomwayson commented Apr 21, 2020

Thanks @mjuniper.

It looks like you can easily set CSS variables in an element's style (no CSS class needed). For example, I added the following the <hub-statistic>'s CSS file:

.ss-value {
  font-size: var(--ss-font-size, 72px);
}

And then I was able to inspect the element and set a value for that variable on the <hub-statistic> element in developer tools:

image

And then I was able to also set it via JS:

var el = document.querySelector('hub-statistic');
el.style.setProperty('--ss-font-size', '52px')

Which also worked.

It'd be good to verify that via Ember's template binding, but it looks like we can meet all the requirements using CSS variables...

... at least in Chrome. I would assume there will be significant limitations and/or blockers in IE, even w/ a polyfill.

We definitely need to test this scenario (setting CSS variables on a component instance) when you look into the competing IE polyfills.

@mjuniper
Copy link

Yeah, that's what I meant - I am not absolutely certain but I believe that will not work in ie with stencil's polyfill.

On the other hand if we use the polyfill I included to support site theming via css custom props, we can apply custom props to a particular element. Something like this:

cssVars({
    rootElement: document.querySelector('hub-statistic'),
    { 'ss-font-size': '52px' }
});

@ajturner
Copy link
Contributor

ajturner commented May 6, 2020

Thanks for the great discussion @mjuniper @tomwayson. I'm sorry that I missed the notifications to respond sooner.

Styling

First, on styling I think we consider following calcite-components pattern with general style options set via enumerated @Prop. For example Calcite Component, Button, Notice and more all have scale='s'|'m'|'l'.

Additionally they have

  • color="blue" | "dark" | "light" | "red"
  • appearance="clear" | "outline" | "solid" | "transparent"
  • theme='dark'|'light'

For our components we should determine if we can use one of these semantically relevant properties - for example does the Summary Stat have small | medium | large for font sizes. But for our theme we would instead rely on CSS variables.

For loading, Calcite Card and others have an explicit @Prop as well that allow the component design to determine how loading is handled (e.g. show the title but spinner below?)

Naming

I agree that we have two major types of components: Presentation and 'Smart'. (short article on naming)

To make it clear up front should we consider providing a consistent suffix to dumb presentation components, such as <hub-statistic-view> (presentation) and <hub-statistic> or <hub-statistic-card> for smart, data-driven components.

Looking at other examples

  • <hub-content-card-view title=...> <hub-content-card item-id=..>
  • <hub-download-view title=...> <hub-download-card item-id=..>
  • <hub-survey-view title=...> <hub-survey-card item-id=..>
  • <hub-event-view title=...> <hub-event-card item-id=..>

Properties

I don't think that we need to be shy about the number of properties on components. So long as they have good defaults we can make it both easy to use and flexible to configure. Look for example at AirBnB React calendar components has a dozen wrapper variants and ~50 props.

For the summary stat, we could have a 1-1 mapping between the card editor, card config JSON and the component props.

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

3 participants