Clipping Scrollable Areas On The inline-start Side
Strategy

Clipping Scrollable Areas On The inline-start Side


This is the first of its kind blog post. I simply called it “You might not know”. For this one, I want to share something about the default behavior of clipping scrollable areas.

Say we have a section with a decorative pseudo-element (The document direction is LTR).

.section::before {
  position: absolute;
  left: -20px;
  top: 5px;
  width: 150px;
  height: 75px;
  background: url("img/dice.svg") center/cover no-repeat;
}

Notice that the pseudo-element is positioned on the left side with a negative value. This isn’t causing any horizontal scrolling and you don’t need to apply overflow: hidden.

The designer has changed their mind and they want the decorative element to be on the right side, so you do the following.

.section::before {
  /* You swapped left with right */
  right: -20px;
}

Boom! We have a horizontal scrollbar. Why did that happen when the pseudo-element was moved to the right side? Wasn’t it working fine before that?

Well, the reason is that because the browser user agent will clip the overflow area of the block-start and inline-start areas. In the first example, the pseudo-element was placed on the left side with a negative margin. This is considered the inline-start, so the browser clipped it for us.

According to the CSS spec:

UAs must clip the scrollable overflow area of scroll containers on the block-start and inline-start sides of the box (thereby behaving as if they had no scrollable overflow on that side).

Right to left documents (RTL)

For a document that starts from right to left (e.g: Arabic website), the inline-start of the document will be on the right side. That means, if a pseudo-element is placed with a negative value on the right side, the browser will clip the overflow for us.

If the root has dir="rtl", the inline-start will be on the right side.

What I found interesting is that Firefox shows a horizontal scrollbar even though the direction is RTL and the element is positioned on the right. Chrome and Safari are displaying the element as expected.

I will investigate more in the behavior of Firefox. For the time being, be careful when you position elements outside their parent as a scrollbar will appear. The solution now is to use overflow-x: hidden.

I wrote an ebook

I’m excited to let you know that I wrote an ebook about Debugging CSS.

If you’re interested, head over to debuggingcss.com for a free preview.



Source link

Profiling session showing everything rendered
Strategy

Use CSS Variables instead of React Context


I’ve been riding the CSS-in-JS train for years (I was even a significant
contributor to the “movement”). It’s awesome. I’ve never been so productive
working with CSS than when I added a real programming language to it.

I’m still a fan of CSS-in-JS, and in recent years, the CSS spec has evolved and
improved a lot and modern browsers have too (unfortunately, Internet Explorer is
NOT a modern browser in this or any context). Often I’d use a ThemeProvider
(like those found in emotion), but turns out there aren’t a whole lot of
advantages to that kind of component for many use cases and there are several
disadvantages.

Let’s look at a simple example of “Dark Mode” and compare differences in API
(developer experience) and performance (user experience). We’ll keep the example
simple, and in both the before/after, we’ll be using emotion’s styled utility.
Keep in mind that with the ThemeProvider you can consume the values using the
useTheme hook, with a styled component, or with the css prop. With CSS
Variables, you can get the values in your CSS with var(--css-variable-name)
and in your JavaScript using
getComputedStyle(element).getPropertyValue('--css-variable-name') (which you
really don’t need to do…)

Ok, let’s look at some code. Here’s an approach to using emotion’s
ThemeProvider:

import * as React from 'react'

import styled from '@emotion/styled'

import {ThemeProvider} from 'emotion-theming'

const themes = {

light: {

colors: {

primary: 'deeppink',

background: 'white',

},

},

dark: {

colors: {

primary: 'lightpink',

background: 'black',

},

},

}

const PrimaryText = styled.div(({theme}) => ({

padding: 20,

color: theme.colors.primary,

backgroundColor: theme.colors.background,

}))

function ThemeToggler({theme, onClick}) {

const nextTheme = theme === 'light' ? 'dark' : 'light'

return (

<button onClick={() => onClick(nextTheme)}>

Change to {nextTheme} mode

</button>

)

}

function App() {

const [theme, setTheme] = React.useState('light')

return (

<ThemeProvider theme={themes[theme]}>

<PrimaryText>This text is the primary color</PrimaryText>

<ThemeToggler

theme={theme}

onClick={(nextTheme) => setTheme(nextTheme)}

/>

</ThemeProvider>

)

}

export default App

What’s cool about this is it’s “just JavaScript” so you get all the benefits of
variables etc. But we’re not really doing all that much with this other than
passing it around through the ThemeProvider. (To be clear, the way the
ThemeProvider works is it uses React’s Context API to make the theme accessible
to all emotion components without having to pass props all over the place).

Let’s compare this with the CSS Variables approach. But before we get to that, I
need to mention that there’s no “ThemeProvider” for this. Instead, we define the
variables in regular CSS that will get applied based on a data attribute we
apply to the body. So here’s that css file:

body[data-theme='light'] {

--colors-primary: deeppink;

--colors-background: white;

}

body[data-theme='dark'] {

--colors-primary: lightpink;

--colors-background: black;

}

Alright, so with that, here’s the implementation of the exact same UI:

import * as React from 'react'

import './css-vars.css'

import styled from '@emotion/styled'

const PrimaryText = styled.div({

padding: 20,

color: 'var(--colors-primary)',

backgroundColor: 'var(--colors-background)',

})

function ThemeToggler() {

const [theme, setTheme] = React.useState('light')

const nextTheme = theme === 'light' ? 'dark' : 'light'

React.useEffect(() => {

document.body.dataset.theme = theme

}, [theme])

return (

<button onClick={() => setTheme(nextTheme)}>

Change to {nextTheme} mode

</button>

)

}

function App() {

return (

<div>

<PrimaryText>This text is the primary color</PrimaryText>

<ThemeToggler />

</div>

)

}

export default App

Let’s first compare what it’s like to use these values:

const PrimaryText = styled.div(({theme}) => ({

padding: 20,

color: theme.colors.primary,

backgroundColor: theme.colors.background,

}))

const PrimaryText = styled.div({

padding: 20,

color: 'var(--colors-primary)',

backgroundColor: 'var(--colors-background)',

})

There’s not really much of a difference from a DX (development experience)
standpoint here. One point for the CSS Variables approach is not having to
create a function that accepts the theme and returning styles (and no need to
even learn about that API).

One point for the ThemeProvider approach is if you’re using TypeScript you
could get type safety on your theme… But ummm…
Check this out:

const theme = {

colors: {

primary: 'var(--colors-primary)',

background: 'var(--colors-background)',

},

}

export {theme}

import {theme} from 'theme'

const PrimaryText = styled.div({

padding: 20,

color: theme.colors.primary,

backgroundColor: theme.colors.background,

})

BOOM. Static typing-friendly.

Either way, that’s really the only significant DX difference. Let’s consider the
UX (user experience) difference. Why don’t you play around with it:

You’ll notice there’s not really any observable UX difference, and there’s not
for this simple example. But why don’t you try this with me:

  1. pop open the React DevTools Profiler
  2. Start a profiling session (click the little circle)
  3. Click the toggle button for each
  4. Stop the profiling session
  5. Compare the two commits

Here’s what I see:

ThemeProvider:


Profiling session showing everything rendered

CSS Variables:


Profiling session showing only one component rendered

I don’t want you to get hung up on the number of milliseconds to rerender. This
isn’t a controlled benchmark (we’re in React’s dev mode for one thing). The
thing I want you to consider is how many components needed to re-render for this
change. Let’s consider the ThemeProvider approach first. The main reason for
this is the way we’ve structured our state
(we could restructure things
and improve it a little bit). But even if we restructured things, when the theme
changes, every emotion component needs to be re-rendered to account for the
theme change.

Turning to the CSS Variables approach, you’ll notice the only component that
re-rendered was our ThemeToggler component responsible for updating the
body. And yet the user experience works perfectly! This is the magic behind
CSS variables. With the ThemeProvider approach, we have to update the styles
of every component, and then the browser paints those updates. But with the CSS
Variables approach, we update the styles to a single component (the body) and
then the browser paints those updates. The browser paint should theoretically
take the same amount of work on the part of the browser, so the only difference
is how much work we’re making the browser do to have React re-render all our
components and get emotion to update the styles of every component.

This can have negative performance implications as your app grows. Considering
the DX isn’t objectively better or worse either way, but the UX is quite
possibly better with CSS Variables, I feel comfortable recommending CSS
Variables over using Context to share a theme like this.

Oh, and consider also that CSS Variables are part of the browser spec and the
ThemeProvider isn’t. That’s another solid point for CSS Variables 😉

This is one standard I’d suggest you embrace.

One last piece of nuance here. What if you not only want to change styles but
also want to change component implementations based on the theme? In this
case…
YOU CAN DO BOTH!
The benefit of doing this is the only components that need to consume the
context value are those that need to render differently based on the theme
(which is likely a small subset). Most components can use the css variables for
styling purposes only, so you’ll still get the aforementioned benefits. If you
don’t need components to render differently based on the theme then I wouldn’t
bother with this, but if you do, then it’s pretty simple to do. There are
several ways to accomplish this, I’ve done one implementation in
the codesandbox.
Enjoy exploring that.

CSS variables (custom properties) are awesome. Give them a look. Good luck!





Source link

A green blob with four edges that vary in size and shape.
Strategy

Three Ways to Blob with CSS and SVG


Blobs are the smooth, random, jelly-like shapes that have a whimsical quality and are just plain fun. They can be used as illustration elements and background effects on the web.

So, how are they made? Just crack open an illustration app and go for it, right? Sure, that’s cool. But we’re in a post here on CSS-Tricks, and it would be much more fun to look at the possibilities we have to do this with CSS and SVG — two of our favorite ingredients!

We actually have a few ways to go about blobs. Let’s check them out.

Drawing circles in SVG

Let’s start easy. We can draw SVG in something like Illustrator, Sketch, Figma or whatever, but we’re going to draw in SVG code instead.

SVG makes it pretty trivial to draw a circle, thanks to the appropriately named <circle> element:

<circle cx="100" cy="100" r="40" fill="red" />

Those funky attributes? They make sense once you break them down:

  • cx defines the x-coordinate of center of circle.
  • cy defines the y-coordinate.
  • r is the radius.
  • fill is used to fill the shape with color.

That snippet creates a circle with a 40px radius with its center at 100px on the x-axis and 100px on the y-axis. The coordinates start from the upper-left corner of the parent container.

Let’s create multiple overlapping circles like this:

<svg height="300" width="300">
  <circle cx="80" cy="80" r="40" fill="red" />
  <circle cx="120" cy="80" r="40" fill="red" />
  <circle cx="150" cy="80" r="40" fill="red" />
  <circle cx="150" cy="120" r="40" fill="red" />
  <circle cx="100" cy="100" r="40" fill="red" />
</svg> 

<svg> acts as the art board where all the different shapes and figures are drawn. So, its height and width indicates the size in which the whole drawing needs to be enclosed. If some part of figure is out of bounds of the SVG’s size, then that part will be truncated.

But blobs aren’t always so perfectly… round. We can mix things up by using <ellipse> instead of <circle>:

<ellipse cx="200" cy="80" rx="100" ry="50" fill="red" />

This is nearly identical to the circle except the change in tag name and two radii values to define the horizontal (rx) and vertical (ry) radii separately. The funny thing is that we can still get a perfect circle if we want if the radii values are the same. So, in a sense, <ellipse> is a little more versatile.

And, if all you need is a circle, we could probably lean on CSS without SVG at all. Any box element can become a circle or ellipse with border-radius.

.circle {
  border-radius: 50%;
  height: 50px;
  width: 50px;
}

…but more on that later.

Freestyling with SVG paths

Thanks to SVG’s <path> tag, we can create any kind of shape. It is like drawing with a pencil or pen. You start from a point and draw lines, curves, shapes and close the loop.

There are many data parameters in path for different tasks like:

  • M – Moving to the point
  • L – Drawing line
  • C – Drawing a curve
  • Q – Bézier curve
  • Z – Closing the path

Chris has a super thorough guide that explains these parameters in great detail.

We just need the curve (C) parameter for the actual drawing. But we’ll also be moving the starting point and closing the path, so we’ll reach for the M and Z parameters as well.

This is a random blobby shape I put together using SVG’s <path> element.

Ready to break this down? Coordinates play a big role in <path> so what we’re about to look at will look like Google Maps data barfed inside our code. But it makes a lot more sense when we know what they’re doing.

Take this…

<svg xmlns="http://www.w3.org/2000/svg">
  <path
    fill="#24A148"
    d=""
  />
</svg>

Here, the d attribute stores the path data. It holds information containing where the drawing starts, what direction it moves, what shape it follows, and where it ends. For example:

<path d="M 10 10 C 20 20, 40 20, 50 10" stroke="black" fill="transparent"/>

It shows that our path starts from coordinates 10 10, indicated by the M that precedes them. Then, it establishes a Cubic Bézier curve (C) with two control points. Bézier curves are like handles on the both ends of a path that control the curviness between them. We have two Bézier “handles”: one for starting position (20 20) of the curve and another for ending position (40 20).

Let’s use this knowledge to design our blob. The blob I drew is actually a bit complex, with a number of curves and control points. It doesn’t help that many of the coordinates aren’t full integers. But, still, now that we know what the <path> ‘s d parameter does and the attributes it uses to draw points along the path, it doesn’t look quite as scary.

But, hey, I get it. That’s a lot of code to not only write by hand, but get exactly right as well. I wouldn’t fault you for using something like this tool to generate the code for you.

Gooey effects with CSS and SVG filters

SVG path is complex. Right? What if I present you a way to convert many custom shapes (which you can create through divs) into gooey blobs? Here’s the idea. We’re going to create two rectangles that intersect. They’re the same color, but have a little transparency to darken where they intersect.

Then we’re going to leverage SVG’s blurring features to smudge the rectangles, creating an extra gooey blob with softer edges. The two intersecting rectangles will turn into this –

Let’s first understand how filters work in SVG. They are declared using <filter> on HTML elements or other SVG elements, like circle.

circle {
  filter: url("#id_of_filter");
}

<filter> is basically a wrapper for the actual filter effects, that include:

  • <feGaussianBlur>
  • <feImage>
  • <feMerge>
  • <feColorMatrix>
  • Many more… Get the complete list here.

Our blob is blurred and colored, so that’s why we’re going to put <feGaussianBlur> and <feColorMatrix> to use.

<feGaussianBlur> takes multiple attributes, but we are only interested in two of them: how much blur we want and where we want it. The standard deviation (stdDeviation) and in properties align with those needs, respectively.

in accepts one of two values:

  • SourceGraphic – Blurs the entire shape
  • SourceAlpha – Blurs the alpha value, and is used to create shadow effects

After playing around a bit, here’s where I landed on the <feGaussianBlur> effect:

<feGaussianBlur in="SourceGraphic" stdDeviation="30" />

This goes right in the HTML markup with an ID that we call on the parent element of our blob:

<!-- The SVG filter -->
<svg style="position: absolute; width: 0; height: 0;">
  <filter id="goo">
    <feGaussianBlur in="SourceGraphic" stdDeviation="30" />
  </filter>
</svg>

<!-- The blob -->
<div class="hooks-main">
  <div></div>
  <div></div>
</div>

The filter doesn’t actually render, even though it’s in the markup. Instead, we reference it as a CSS filter on the blob’s parent element:

/* Blob parent element */
.hooks-main {
  position: absolute;
  width: 100%;
  height: 100%;
  filter: url("#goo&amp");
  overflow: hidden;
}

This isn’t done just yet. The blur is scattered and the element’s shape lost its boundary and color. We need a bulging effect with blur on the boundaries and a solid color to fill the shape. This is where our next SVG filter, <feColorMatrix>, comes into play.

There are two <feColorMatrix> attributes we want:

  • in – Indicates where the effect is applied, just like <feGaussianBlur>.
  • values – A matrix of four rows and five columns.

The values attribute bears a little more nuance. It holds a matrix that gets multiplied with the color and alpha values of each pixel and generates a new color value for that pixel. Mathematically speaking:

new pixel color value = ( values matrix ) × ( current pixel color value )

Let’s get a little numbers nerdy here. In that equation, values matrix is equal to:

[F-red1 F-green1 F-blue1 F-alpha1 F-constant1
 F-red2 F-green2 F-blue2 F-alpha2 F-constant2
 F-red3 F-green3 F-blue3 F-alpha3 F-constant3
 F-red4 F-green4 F-blue4 F-alpha4 F-constant4]

Here, F-red means a fraction of red in pixels, with a value ranging from 0 to 1. F-constant is some constant value to add (or subtract) from color value.

Breaking this down further… We have a color pixel with an RGBA value of rgba(214, 232, 250, 1). To convert it into a new color, we will multiply it with our values matrix.

Values Matrix Color Pixel (RGBA) New Color (RGBA)
[1 0 0 0 0
 0 1 0 0 0
 0 0 1 0 0
 0 0 0 1 0
 0 0 0 0 1]
× [214
 232
 250
 1
 1]
= [ 214x1 + 232x0 + 250x0 + 1x0 + 1x1
      214x0 + 232x1 + 250x0 + 1x0 + 1x1
      214x0 + 232x0 + 250x1 + 1x0 + 1x1
      214x0 + 232x0 + 250x0 + 1x1 + 1x1
      214x0 + 232x0 + 250x0 + 1x0 + 1x1 ]
= [214
  232
  250
 1
  1]

The pixel value didn’t change because we multiplied it by the identity matrix, but if you change the values of the matrix, then its pixel value will change too. Learn more about values matrix from MDN documentation.

In our case, these values seem to work pretty well:

<filter id="goo">
  <feGaussianBlur in="SourceGraphic" stdDeviation="30" />
  <feColorMatrix
    in="blur"
    values="1 0 0 0 0 
            0 1 0 0 0 
            0 0 1 0 0 
            0 0 0 30 -7"
  />
</filter>

I’ve added few more styles in the blob to stretch it from the corner.

Try to use these filter values in other shapes and let me know how they work out for you in the comments.

Using CSS border-radius

We teased this earlier, but now let’s get to the CSS border-radius property. It can also create blob-like shape, thanks to it’s ability to smooth out the corners of an element. This is possible because each corner radius is divided into two radii, one for each edge. That’s why we can have more shapes apart from circle and ellipse.

You might be used to using border-radius as a shorthand for all four corners of an element:

.rounded {
  border-radius: 25%;
}

That’s a nice way to get uniformity for all of the corners. But blobs aren’t so uniform. We want some corners to be rounder than others to get some that looks gooey. That’s why we go for the constituent properties of border-radius, like:

.element {
  border-top-left-radius: 70% 60%;
  border-top-right-radius: 30% 40%;
  border-bottom-right-radius: 30% 60%;
  border-bottom-left-radius: 70% 40%;
}

And see how each properties takes two values? That’s one for each edge of the corner, giving us a lot of flexibility to curve an element into interesting shapes. Then we can drop in a background color, fill it up with a gradient, or even set a box-shadow on it to get a neat effect.



Source link

r/webdev - Form it
Strategy

Form it’s not displayed properly : webdev


Hello guys, I’m trying to make a student exam system and I’m having a problem with displaying the correct grade and the exam for the students.

My problem is that instead of 2 exams, I can only see one exam in the view results page. Also, the score it’s computed wrong(it should be 6/10 instead of 6/4), and in the database everything seems fine

Main page it’s fine, I can see the 2 exams listed:

r/webdev - Form it's not displayed properly

dashboard view

but on the View result page I can only see one exam and I don’t understand why…

r/webdev - Form it's not displayed properly

view result page

Exams DB:

r/webdev - Form it's not displayed properly

exams db

Exam answers for the asd user:

r/webdev - Form it's not displayed properly

asd user answer

Here’s my code: <?php include “mysql-connect.php”; date_default_timezone_set – Pastebin.com

What am I doing wrong? Thanks!



Source link

r/graphic_design - Packaging Dieline Help Needed
Strategy

Packaging Dieline Help Needed : graphic_design


Hi, could someone help me understand where the two sides of this Tetra Aseptic 1 Liter line up to form the final package? I have some diagonal lines that are running across the back seam, and the client wants them to line up as perfectly as they can.

I’m a little confused, so bear with me.. I believe 0 (far left) is bleed, trimmed in between 0&1, the leftmost line on 1 wraps around to meet up between 4&5.

But then when I look at prior art (blue and pink boxes below the numbers—I’m assuming this lines up perfectly but I can’t be sure) the trim between 0&1 wraps around to 1/20th inch inside panel #3. This seems rather odd.

Can anyone help explain which two lines butt up to each other?

TIA

r/graphic_design - Packaging Dieline Help Needed



Source link

r/webdev - Can someone explain me the login behind this ?
Strategy

Can someone explain me the login behind this ? : webdev


I am trying to open https://developer.spotify.com/dashboard/But when I do, the only thing that renders is this

r/webdev - Can someone explain me the login behind this ?

Website on my laptop

But around a week back, I used to get a fully rendered site, like this one

r/webdev - Can someone explain me the login behind this ?

How actually the site should look

I rendered this site successfully by using VPN but on my normal network, I always get the site as in the first picture. Can anyone help me understand why this is happening ?



Source link

Cover image of statistics
Strategy

How to Design a Flexible Solution for Custom Dashboards and …


Trying to build a solution that is simple, fast, scalable, highly available yet flexible to handle custom requests from your clients? A solution that can serve a group of customers or can dedicatedly be deployed for a single customer in a multi-tenant environment? Then, this post is for you. 

Cover image of statistics

Why Do We Need This?

No matter how vast or fine the solution you provide to your clients, there are always custom requirements. Some requests can be fulfilled with small tweaks but others can take a big effort and a complete development cycle. For a SaaS solution, these requests are very common and always a challenging task. Dashboards and Reports are the most common areas for these types of requests where every customer needs data that make more sense to them and in the format they understand better.

History Behind:

We have tried a few solutions over the years to deliver Dashboards and Reports, but they were never up to the expectations. 


First Solution

It all started a few years back when we published the first solution. It was implemented using Java, MySQL, JavaScript, and elycharts (an open-source javascript charting library). The publisher was sending the data on two different services, which were processing and storing the data in MySQL tables. For every dashboard and report, data was being fetched from MySQL (yeah, even for the live data we used MySQL as a queue). Both independent dashboard and reporting applications were serving the data that ‘we’ think makes sense to the customers but the solution has multiple drawbacks and limitations. The major ones are —

  1. Data discrepancy on dashboards and reports due to different data sources.
  2. Very frequent queries(polling) on the database just to keep refreshing the dashboard views.
  3. Custom requirements need implementation of new views, APIs, and deployment of the application to ship the changes.
  4. UI freeze due to frequent data refreshes API hit and view rendering.

Second Solution

To overcome the challenges we tried integrating with a third-party UI provider for dashboards. We also did some changes around data sources, data categorization, data polling, etc.

To solve data discrepancy, we divided the data into two categories live vs delayed. Earlier we were trying to present the whole data on dashboards as soon as possible and to do that, we were managing a different data source for dashboards and putting a heavy read load on it.

But when we took a closer look at the data, not all the KPIs required to be presented as live. Very few KPIs that help supervisors to take dynamic decisions can be presented quickly and the rest of the data can be presented with some delay and we really do not need to keep refreshing it every few seconds. So, for the live data, we implemented the WebSockets using Node and for delayed data, we started polling from the reporting database every 2–3 minutes.

Although after implementing this solution we were left with handling custom requirement challenges and integration issues with the third party. But overall we reached into more bad conditions as handling custom requirements was still a big challenge and their implementation was impacting the whole server. Also, due to third-party involvement, every requirement and fix turnaround time increased a lot too. As a result, we have to start thinking about other solutions even before onboarding all our customers to this new solution.


Third Solution

Keeping all the problems in mind I started thinking to make a flexible solution where I can —

  • Create on-demand Dashboards and Reports.
  • Add new data widgets.
  • Avoid server downtime for new requests.
  • Maintain sanity of the application.
  • No impact on other customers running on the same server (in a multitenant system).
  • Live data can be presented without any delay.
  • Cumulative data can be presented without any discrepancies.

And started looking for all the possible solutions. After doing some research and brainstorming I noted few points that can help me achieve the goal. Let’s have a look at some major points and how they help to solve the problem statements.

  • Configuration-based views for Dashboards, Reports, and widgets to handle custom requirements.
  • Manage widgets-based data queries in the database to avoid server downtime.
  • A common API to fetch the data for every widget to make the solution flexible and avoid new development for every new view.
  • Independent UI application with the capabilities of handling the configuration, and flexible enough to add new presentation formats so we can easily add new features on UI without asking for any deployment and downtime.
  • Even on the dashboard, not all the data needs to be presented live. Data can be processed and presented based on the priority (Live vs. Delayed). This helps to avoid putting unnecessary load on the application and databases.
  • Using WebSockets for handling streaming data to present live data without any delay.
  • A common data source for both Dashboards and Reports to avoid data discrepancies. Other than streaming data rest of the data can be present with a fixed refresh interval on dashboards.

Implementation

To implement configuration based view and data queries, I designed the following schema —

  • Views (Capture Dashboard or Report specific settings).
  • Widgets (Capture widget-specific configuration).
  • Data Queries (Capture SELECT queries for every cumulative widget that returns the data in the required format).
  • View_Widgets (Manages the mapping of views and widgets).
  • User_Settings (Manages user preferences).

Entity Diagram

Entity Diagram

Every widget either gets the data from the database or receives it from WebSocket. To feed the data from the database we implemented a generic query that takes the widgetId and applied filters as request parameters to fetch the data. Based on the widgetId, API figures out the query to be executed. Each query has the placeholders for all the possible filters and returns the data in the required format.

To handle the UI part, we implemented an Angular-based application and hosted it on S3. For tabular data presentation, we used ag-grid and for graphical views, we used the echarts library. For rendering every type of widget only basic implementation was done inside the project and major configuration and properties were passed through the database.

Complete Solution and Functioning

Here is a broad picture of the complete solution. The user opens up the browser for data monitoring. As soon as the user tries to access the Dashboard or Reports all the available views get listed (based on the user’s access list).

User to event publisher flowchart

When the user clicks on a specific Dashboard or Report, an API with the request parameter view_id gets hit. This API fetches configuration for that particular View (Report or Dashboard) and all the mapped widgets. Every widget’s configuration has the data endpoint API and filters to subscribe from View to fetch the data. Based on the request params, the server identifies the data query to be executed and returns the data as explained above.

Dashboard to application flowchart

With a simple form, we can insert new entries into the configuration tables. As these entries are managed into a customer-specific database it won’t impact the other customer. There is no downtime required as the application is running while we are adding the new configurations. There are no changes required on the applications and we just need to test the data query if it returns the expected data and has no performance impacts. List of available Dashboards and Reports getting render on UI dynamically from configuration so we can deliver on-demand Dashboards, Reports, and widgets. 

Final Result

Final result image 1

Final result image 2

More on the Solution

I know there are few more topics that will help to understand the complete solution but I am giving it a break here with a high-level explanation of the overall system. I will explain more about the following topics in my future blogs:

  • WebSocket implementation.
  • Generic API and Dynamic Filters.
  • Multitenancy.
  • Response data formats.
  • Configurations stored in database.
  • Role/ACL.

All these are part of the complete solution and will take another chapter to explain. I will link back here once they are available.

Scope of Improvements

Live data presentation with complex calculations without application changes. (Will explain along with WebSockets).



Source link