Last active
March 7, 2020 03:23
-
-
Save JUSTINMKAUFMAN/d0b53a1d639b6ba95d1ceb75f492d944 to your computer and use it in GitHub Desktop.
BannerConfigurator
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
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 |
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
/** | |
* 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); |
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
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-polyfill/6.13.0/polyfill.min.js"></script> |
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
@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