Skip to content

Instantly share code, notes, and snippets.

@JUSTINMKAUFMAN
Last active March 7, 2020 03:23
Show Gist options
  • Save JUSTINMKAUFMAN/d0b53a1d639b6ba95d1ceb75f492d944 to your computer and use it in GitHub Desktop.
Save JUSTINMKAUFMAN/d0b53a1d639b6ba95d1ceb75f492d944 to your computer and use it in GitHub Desktop.
BannerConfigurator
BannerConfigurator
------------------
Parse form into JSON payload.
section.content
h1.content__heading 98point6 Patient Client Banner
p.content__lede Configure a banner to appear at the top of the screen on our mobile apps
form.content__form.banner-form
.testing
p * Required Field
.banner-form__input-group
label.banner-form__label(
for="id"
) Unique Identifier*
input#id.banner-form__input.banner-form__input--text(
name="id"
type="text"
value="1b684350-600b-11ea-bc55-0242ac130003"
)
.banner-form__input-group
label.banner-form__label(
for="messageText"
) Message*
textarea#message.banner-form__input.banner-form__input--textarea(
name="messageText"
rows="6"
cols="65"
)
.banner-form__input-group
label.banner-form__label(
for="hasAction"
) Does the banner have an action button?
select#hasAction.banner-form__input.banner-form__input--select(
name="hasAction"
)
option No
option Yes
.banner-form__input-group
label.banner-form__label(
for="actionTitle"
) Action Title
input#actionTitle.banner-form__input.banner-form__input--text(
name="actionTitle"
type="text"
)
.banner-form__input-group
label.banner-form__label(
for="actionURL"
) Action URL
input#actionURL.banner-form__input.banner-form__input--text(
name="actionURL"
type="text"
)
.banner-form__input-group
label.banner-form__label(
for="userDismissible"
) Can banner be dismissed by user?
select#userDismissible.banner-form__input.banner-form__input--select(
name="userDismissible"
)
option Yes
option No
//- A hidden input to demonstrate that these are grabbed as well.
input(
name="secret"
type="hidden"
value="1b3a9374-1a8e-434e-90ab-21aa7b9b80e7"
)
button.banner-form__button(
type="submit"
) Submit
.results
h2.results__heading Flag Payload
pre.results__display-wrapper
code.results__display
/**
* Checks that an element has a non-empty `name` and `value` property.
* @param {Element} element the element to check
* @return {Bool} true if the element is an input, false if not
*/
const isValidElement = element => {
return element.name && element.value;
};
/**
* Checks if an element’s value can be saved (e.g. not an unselected checkbox).
* @param {Element} element the element to check
* @return {Boolean} true if the value should be added, false if not
*/
const isValidValue = element => {
return (!['checkbox', 'radio'].includes(element.type) || element.checked);
};
/**
* Checks if an input is a checkbox, because checkboxes allow multiple values.
* @param {Element} element the element to check
* @return {Boolean} true if the element is a checkbox, false if not
*/
const isCheckbox = element => element.type === 'checkbox';
/**
* Checks if an input is a `select` with the `multiple` attribute.
* @param {Element} element the element to check
* @return {Boolean} true if the element is a multiselect, false if not
*/
const isMultiSelect = element => element.options && element.multiple;
/**
* Retrieves the selected options from a multi-select as an array.
* @param {HTMLOptionsCollection} options the options for the select
* @return {Array} an array of selected option values
*/
const getSelectValues = options => [].reduce.call(options, (values, option) => {
return option.selected ? values.concat(option.value) : values;
}, []);
/**
* A more verbose implementation of `formToJSON()` to explain how it works.
*
* NOTE: This function is unused, and is only here for the purpose of explaining how
* reducing form elements works.
*
* @param {HTMLFormControlsCollection} elements the form elements
* @return {Object} form data as an object literal
*/
const formToJSON_deconstructed = elements => {
// This is the function that is called on each element of the array.
const reducerFunction = (data, element) => {
// Add the current field to the object.
data[element.name] = element.value;
// For the demo only: show each step in the reducer’s progress.
console.log(JSON.stringify(data));
return data;
};
// This is used as the initial value of `data` in `reducerFunction()`.
const reducerInitialValue = {};
// To help visualize what happens, log the inital value, which we know is `{}`.
console.log('Initial `data` value:', JSON.stringify(reducerInitialValue));
// Now we reduce by `call`-ing `Array.prototype.reduce()` on `elements`.
const formData = [].reduce.call(elements, reducerFunction, reducerInitialValue);
// The result is then returned for use elsewhere.
return formData;
};
/**
* Retrieves input data from a form and returns it as a JSON object.
* @param {HTMLFormControlsCollection} elements the form elements
* @return {Object} form data as an object literal
*/
const formToJSON = elements => [].reduce.call(elements, (data, element) => {
// Make sure the element has the required properties and should be added.
if (isValidElement(element) && isValidValue(element)) {
/*
* Some fields allow for more than one value, so we need to check if this
* is one of those fields and, if so, store the values as an array.
*/
if (isCheckbox(element)) {
data[element.name] = (data[element.name] || []).concat(element.value);
} else if (isMultiSelect(element)) {
data[element.name] = getSelectValues(element);
} else {
data[element.name] = element.value;
}
}
return data;
}, {});
/**
* A handler function to prevent default submission and run our custom script.
* @param {Event} event the submit event triggered by the user
* @return {void}
*/
const handleFormSubmit = event => {
event.preventDefault();
const data = formToJSON(form.elements);
const dataContainer = document.getElementsByClassName('results__display')[0];
// Use `JSON.stringify()` to make the output valid, human-readable JSON.
dataContainer.textContent = JSON.stringify(data, null, " ");
// ...this is where we’d actually do something with the form data...
};
const handleActionOptionChanged = event => {
updateActionState();
};
function updateActionState() {
const setting = event.target.value;
if (setting == 'Yes') {
actionTitle.disabled = false;
actionURL.disabled = false;
} else {
actionTitle.disabled = true;
actionTitle.value = '';
actionURL.disabled = true;
actionURL.value = '';
}
}
const form = document.getElementsByClassName('banner-form')[0];
form.addEventListener('submit', handleFormSubmit);
const actionSelector = document.getElementById('hasAction');
const actionTitle = document.getElementById('actionTitle');
const actionURL = document.getElementById('actionURL');
actionSelector.addEventListener('input', handleActionOptionChanged);
document.addEventListener('DOMContentLoaded', (event) => {
updateActionState();
});
// document.addEventListener('DOMContentLoaded', handleActionOptionChanged);
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-polyfill/6.13.0/polyfill.min.js"></script>
@use postcss-nested;
@use postcss-simple-vars;
@use postcss-cssnext;
$color-lightest: #f9fdfe;
$color-gray-light: #cdcfcf;
$color-gray-medium: #686a69;
$color-gray-dark: #414643;
$color-darkest: #2a2f2c;
/* A simple reset. */
*,::before,::after {
margin: 0;
box-sizing: border-box;
}
/* Heydon Pickering’s lobotomized owl. Details: https://bit.ly/1H7MXUD */
*+* {
margin-top: 1rem;
}
/* Set up fonts, colors and all that jazz. */
body {
background: $color-lightest;
color: $color-gray-medium;
font-family: 'Open Sans', sans-serif;
font-size: 18px;
line-height: 1.75;
text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
/* Headings use a different font because they’re hipsters. */
h1,h2 {
color: $color-darkest;
font-family: Lato, sans-serif;
font-weight: 300;
line-height: 1.125;
}
/* Set up general layout rules for outer containers. */
.content,.results {
width: 90vw;
max-width: 550px;
margin: 8vh auto;
}
.content {
&__heading {
font-size: 125%;
}
&__lede {
margin-top: 0.5rem;
font-size: 87.5%;
}
}
.results {
&__heading {
font-size: 110%;
}
&__display-wrapper {
margin-top: 1rem;
padding: 0.5rem 1rem;
background: $color-lightest;
border: 1px solid $color-gray-light;
overflow-x: scroll;
}
}
.banner-form {
position: relative;
display: block;
margin: 0;
padding: 1rem 0 2rem;
border-top: 1px solid $color-gray-light;
border-bottom: 1px solid $color-gray-light;
overflow: hidden;
&__input-group {
margin-top: 0.25rem;
padding: 0.5rem 1rem;
}
&__label {
display: block;
color: $color-gray-dark;
font-family: Lato, sans-serif;
font-size: 75%;
line-height: 1.125;
&--checkbox-group {
display: inline-block;
margin-right: 1rem;
font-size: 75%;
}
&--checkbox,&--radio {
display: inline-block;
margin-left: 0.25rem;
}
}
&__input {
display: block;
margin-top: 0;
padding: 0.5rem 0.75rem;
border: 1px solid $color-gray-light;
width: 100%;
font-family: 'Open Sans', sans-serif;
font-size: 1rem;
transition: 150ms border-color linear;
&--checkbox,&--radio {
display: inline-block;
width: auto;
&~& {
margin-left: 1rem;
}
}
&:focus,&:active {
border-color: $color-gray-medium;
outline: 0;
}
}
&__button {
display: block;
margin: 0.5rem 1rem 0;
padding: 0 1rem 0.125rem;
background-color: $color-gray-medium;
border: 0;
color: $color-lightest;
font-family: lato, sans-serif;
font-size: 100%;
letter-spacing: 0.05em;
line-height: 1.5;
text-transform: uppercase;
transition: 150ms all linear;
&:hover,&:active,&:focus {
background: $color-darkest;
cursor: pointer;
outline: 0;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment