-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #1 from devonChurch/feature/throttle-optimisation
Feature: "Load Control" optimisation
- Loading branch information
Showing
8 changed files
with
256 additions
and
102 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
# What 👋 | ||
|
||
A _quick_ description outlining the context of this _issue_. | ||
|
||
### _Example:_ | ||
|
||
> Improve the current _login widget_ form validation. | ||
## Where 🔍 | ||
|
||
An overview of _"avenues"_ that are influenced/affected by this request. | ||
|
||
### _Example:_ | ||
|
||
> - This relates to the larger [#123](#) _WCAG 2 AA_ compliance issue. | ||
> - The _login widget_ can be found on the core [`/login`](#) screen. | ||
## Why 🤔 | ||
|
||
Dispel any ambiguity around why this _issue_ needs a resolution. | ||
|
||
### _Example:_ | ||
|
||
> It has become a core requirement to the business that our high traffic pages get immediate treatment from an accessibility perspective. This is to ensure that we comply with the _WCAG 2 AA_ specifications and maintain our compliance rating. | ||
> | ||
> Currently our _login widget_ is falling short of an optimal _user_ experience and needs urgent attention given its significance to our application. | ||
## How 💡 | ||
|
||
Ideas/leads/breadcrumbs around how to proceed with resolving the _issue_. | ||
|
||
### _Example:_ | ||
|
||
> - [@jared](#) has put together a validation flow in which will act as a reference during the development phase. | ||
> ![new-design](https://user-images.githubusercontent.com/15273233/52896073-95cf1280-3227-11e9-996d-3b9872f4f6c0.png) | ||
> | ||
> - [@sarah](#) is the _project owner_ for the _login widget_ and can help from a timing perspective. | ||
> - [@tim](#) has recently added [`redux-form`](https://redux-form.com) to then _sign up_ page and says that it will be helpful in this scenario too. | ||
## Note 📋 | ||
|
||
Any information that does not fit into the above categories giving extra context to the _issue_. | ||
|
||
### _Example:_ | ||
|
||
> It would be nice at some stage to _pull_ these validation _"patterns"_ out into their own _global_ reference for everyone to use. In that regard, we can make a subsequent _issue_ that leverages this work as part of a refactor. | ||
## Demo 📺 | ||
|
||
Bring clarity to the _issue_ with visual aids: | ||
|
||
- **Screenshots:** `cmd` + `shift` + `4` _(MacOS)_. | ||
- **Gifs:** [GIPHY Capture](https://giphy.com/apps/giphycapture) _(free/MacOS)_. | ||
- **Code Snippets:** [Carbon](https://carbon.now.sh/) _(free)_. | ||
|
||
### _Example:_ | ||
|
||
> Validation message appears at the bottom of the widget and has no affiliation to the inputs that need addressing. The messaging is also ambiguous and offers **no context** to the _user_. | ||
> | ||
> ![form-before](https://user-images.githubusercontent.com/15273233/52890596-749c0100-31ea-11e9-94d4-588b914a4fde.gif) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
# What 👋 | ||
|
||
A _quick_ description outlining the context of this _pull request_. | ||
|
||
### _Example:_ | ||
|
||
> An update to the _client-side_ validation system for the _login widget_. | ||
## Where 🔍 | ||
|
||
An overview of _"avenues"_ that are influenced/affected by this work. | ||
|
||
### _Example:_ | ||
|
||
> - This resolves issue [`#123`](#) and [`#456`](#). | ||
> - The _login widget_ can be found on the core [`/login`](#) screen. | ||
> - Leverages the [`redux-form`](https://redux-form.com) implementation from pull request [`#789`](#). | ||
> - This validation enhancement will hide behind feature flag `[LOGIN.VALIDATION]`. | ||
## Why 🤔 | ||
|
||
Dispel any ambiguity around why this bug/feature/enhancement was required. | ||
|
||
### _Example:_ | ||
|
||
> Although the current validation system worked from a technical perspective, there were concerns round _user_ accessibility _(specifically message location and content)_ which would impact our **WCAG 2 AA** compliance rating. | ||
## How 💡 | ||
|
||
Background on the changes/choices made to fulfill this _pull request_. | ||
|
||
### _Example:_ | ||
|
||
> - `redux-form` has a [built in validation system](https://redux-form.com/8.1.0/examples/syncvalidation/) that fits our needs. | ||
> - The integration requires that our form `<input />` elements conform to the [`<Field />`](https://redux-form.com/8.1.0/docs/api/field.md/) abstraction _(which I created a simple HOC to achieve)_. | ||
> - `redux-form` creates its own entry _(and format)_ in the `redux` _"store"_ so there were several references in the application that needed to be updated to the new state schema. | ||
## Note 📋 | ||
|
||
Any information that does not fit into the above categories giving extra context to the _pull request_. | ||
|
||
### _Example:_ | ||
|
||
> We endeavor to move this validation pattern into our [stand alone component architecture](#) next sprint. The _login widget_ is our initial test pilot _(to validate our validation enhancements)_. | ||
## Demo 📺 | ||
|
||
Bring clarity to the code with visual aids: | ||
|
||
- **Screenshots:** `cmd` + `shift` + `4` _(MacOS)_. | ||
- **Gifs:** [GIPHY Capture](https://giphy.com/apps/giphycapture) _(free/MacOS)_. | ||
- **Code Snippets:** [Carbon](https://carbon.now.sh/) _(free)_. | ||
|
||
If applicable, a **before** and **after** representation of your work is preferred. | ||
|
||
### _Example:_ | ||
|
||
> ### Before 👎 🙁 | ||
> | ||
> Global _invalidation_ message at the bottom of the form is visually discrete and uninformative. | ||
> | ||
> ![form-before](https://user-images.githubusercontent.com/15273233/52890596-749c0100-31ea-11e9-94d4-588b914a4fde.gif) | ||
> | ||
> ### After 👍 🙂 | ||
> | ||
> Individual _invalid_ messages on a per/input basis. Message _plus_ the `<input />` itself has an error aesthetic. | ||
> | ||
> ![form-after](https://user-images.githubusercontent.com/15273233/52890599-7796f180-31ea-11e9-9b7b-af84a1107391.gif) |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
import React, { useEffect, useRef } from "react"; | ||
|
||
export const LoadControl = (callback) => { | ||
// Throttler - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // | ||
let throttleId; | ||
const createThrottle = (action) => (throttleId = window.requestAnimationFrame(action)); | ||
const removeThrottle = () => (throttleId = window.cancelAnimationFrame(throttleId)); | ||
const checkIsThrottling = () => Boolean(throttleId); | ||
|
||
// Debouncer - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // | ||
const DEBOUNCE_MILLISECONDS = 100; | ||
let debounceId; | ||
const createDebounce = (...args) => | ||
(debounceId = window.setTimeout(() => callback(...args), DEBOUNCE_MILLISECONDS)); | ||
const removeDebounce = () => window.clearTimeout(debounceId); | ||
|
||
// Depending on the current "load controlled" situation we want to begin a | ||
// throttle sequence or defer the callback to a debounced scenario. | ||
const createLoadControl = () => (...args) => { | ||
if (checkIsThrottling()) { | ||
// If we are already throttling - the callback is STILL IMPORTANT. If the | ||
// throttle finishes but misses the final user input then we could potential | ||
// have the <input /> and <Swatch /> UI out of sync. In this case we create | ||
// a debounced, which will wait a period of time then run the supplied callback. | ||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - | ||
// We do not want to stack callbacks and have them ALL run once their timeout | ||
// expires. We ONLY care about the last supplied callback. In that regard, | ||
// we destroy the preceding debounced setup and create a new one. This keep | ||
// pushing out the time to run the callback while the thriller is still | ||
// running. | ||
removeDebounce(); | ||
createDebounce(...args); | ||
} else { | ||
// If there is NO throttler instance then this is a "fresh" call to "load | ||
// control". Here we run the callback inside of a requestAnimationCall so | ||
// that its run when the browser has the capability to do so. | ||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - | ||
// We ONLY want to run ONE callback per CPU cycle. In that regard we STOP | ||
// callbacks from stacking by creating and removing the RAF reference so | ||
// that ONLY one is running at a time BEFORE and AFTER the callback runs. | ||
createThrottle(() => { | ||
callback(...args); | ||
removeThrottle(); | ||
}); | ||
} | ||
}; | ||
|
||
const cleanUpLoadControl = () => { | ||
removeThrottle(); | ||
removeDebounce(); | ||
}; | ||
|
||
return [createLoadControl, cleanUpLoadControl]; | ||
}; | ||
|
||
export const useLoadControl = (callback) => { | ||
const loadControl = useRef(); | ||
|
||
useEffect(() => { | ||
const [createLoadControl, cleanUpLoadControl] = LoadControl(callback); | ||
loadControl.current = createLoadControl(); | ||
return cleanUpLoadControl; | ||
}, []); | ||
|
||
// If the useEffect system has not been setup yet (happens in the first tick(s)) | ||
// then we just fall back to the vanilla callback until the "load control" | ||
// enrichment is complete. | ||
return loadControl.current || callback; | ||
}; |
Oops, something went wrong.