forked from mirrors/thatmattlove-hyperglass
add pre-submit input validation
This commit is contained in:
parent
0ccd2b83ad
commit
87f065a2ac
5 changed files with 93 additions and 14 deletions
|
|
@ -2,13 +2,14 @@
|
|||
|
||||
{% block content %}
|
||||
<div id="hg-parent" class="mx-auto">
|
||||
<div class="container animsition mw-lg-75 mw-xl-50" data-animsition-out-class="fade-out-right" data-animsition-in-class="fade-in-left"
|
||||
id="hg-form">
|
||||
<div class="container animsition mw-lg-75 mw-xl-50" data-animsition-out-class="fade-out-right"
|
||||
data-animsition-in-class="fade-in-left" id="hg-form">
|
||||
<div class="container-fluid d-flex justify-content-center mb-4 text-center">
|
||||
{% import "templates/title.html.j2" as title %}
|
||||
{{ title.title(branding, primary_asn, size_title="h1", size_subtitle="h4") }}
|
||||
{% import "templates/title.html.j2" as title %}
|
||||
{{ title.title(branding, primary_asn, size_title="h1", size_subtitle="h4") }}
|
||||
</div>
|
||||
<form onsubmit="return false" name="queryform" id="lgForm" action="?" method="POST">
|
||||
<form class="needs-validation" onsubmit="return false" name="queryform" id="lgForm" action="?" method="POST"
|
||||
novalidate>
|
||||
<div class="form-row mb-4">
|
||||
<div class="col-md col-sm-12 mb-4 mb-md-0">
|
||||
<select multiple class="form-control form-control-lg hg-select" id="location" data-live-search="true"
|
||||
|
|
@ -25,8 +26,8 @@
|
|||
</select>
|
||||
</div>
|
||||
<div class="col-md col-sm-12">
|
||||
<select class="form-control form-control-lg hg-select" id="query_type"
|
||||
title="{{ branding.text.query_type }}" data-live-search="true">
|
||||
<select class="form-control form-control-lg hg-select custom-select-lg" id="query_type"
|
||||
title="{{ branding.text.query_type }}" data-live-search="true" required>
|
||||
{% if features.bgp_route.enable %}
|
||||
<option id="bgp_route" value="bgp_route" data-display-name="{{ branding.text.bgp_route }}">
|
||||
{{ branding.text.bgp_route }}</option>
|
||||
|
|
@ -51,10 +52,11 @@
|
|||
</div>
|
||||
</div>
|
||||
<div class="form-row mb-4">
|
||||
<div class="col">
|
||||
<div class="col" id="hg-target-container">
|
||||
<div class="input-group input-group-lg">
|
||||
<input class="form-control" type="text" placeholder="{{ branding.text.query_placeholder }}"
|
||||
aria-label="{{ branding.text.query_placeholder }}" aria-describedby="query_target" id="query_target">
|
||||
aria-label="{{ branding.text.query_placeholder }}" aria-describedby="query_target" id="query_target"
|
||||
required>
|
||||
<div class="input-group-append" id="hg-target-append">
|
||||
<button class="btn btn-primary" id="hg-submit-button" type="submit">
|
||||
<div id="hg-submit-icon">
|
||||
|
|
|
|||
|
|
@ -24,6 +24,20 @@ env = jinja2.Environment(
|
|||
)
|
||||
|
||||
|
||||
def render_frontend_config():
|
||||
"""
|
||||
Renders user config to JSON file so front end config can be used by
|
||||
Javascript
|
||||
"""
|
||||
rendered_frontend_file = hyperglass_root.joinpath("static/frontend.json")
|
||||
try:
|
||||
with rendered_frontend_file.open(mode="w") as frontend_file:
|
||||
frontend_file.write(params.json())
|
||||
except jinja2.exceptions as frontend_error:
|
||||
logger.error(f"Error rendering front end config: {frontend_error}")
|
||||
raise HyperglassError(frontend_error)
|
||||
|
||||
|
||||
def render_theme():
|
||||
"""Renders Jinja2 template to Sass file"""
|
||||
rendered_theme_file = hyperglass_root.joinpath("static/theme.sass")
|
||||
|
|
@ -63,6 +77,12 @@ def build_assets():
|
|||
|
||||
def render_assets():
|
||||
"""Controller function for rendering sass theme elements and building web assets"""
|
||||
try:
|
||||
logger.debug("Rendering front end config...")
|
||||
render_frontend_config()
|
||||
logger.debug("Rendered front end config")
|
||||
except HyperglassError as frontend_error:
|
||||
raise HyperglassError(frontend_error)
|
||||
try:
|
||||
logger.debug("Rendering theme elements...")
|
||||
render_theme()
|
||||
|
|
|
|||
2
hyperglass/static/.gitignore
vendored
2
hyperglass/static/.gitignore
vendored
|
|
@ -7,3 +7,5 @@ old/
|
|||
yarn-error.log
|
||||
theme.sass
|
||||
test*
|
||||
frontend.js
|
||||
frontend.json
|
||||
|
|
|
|||
|
|
@ -6,11 +6,14 @@ const bootstrap = require('bootstrap');
|
|||
const selectpicker = require('bootstrap-select');
|
||||
const animsition = require('animsition');
|
||||
const ClipboardJS = require('clipboard');
|
||||
const frontEndConfig = require('./frontend.json');
|
||||
|
||||
const inputMessages = frontEndConfig.messages;
|
||||
const queryLocation = $('#location');
|
||||
const queryType = $('#query_type');
|
||||
const queryTarget = $('#query_target');
|
||||
const queryTargetAppend = $('#hg-target-append');
|
||||
const submitIcon = $('#hg-submit-icon');
|
||||
const resultsContainer = $('#hg-results');
|
||||
const formContainer = $('#hg-form');
|
||||
const resultsAccordion = $('#hg-accordion');
|
||||
|
|
@ -20,6 +23,16 @@ const footerTermsBtn = $('#hg-footer-terms-btn');
|
|||
const footerCreditBtn = $('#hg-footer-credit-btn');
|
||||
const footerPopoverTemplate = '<div class="popover mw-sm-75 mw-md-50 mw-lg-25" role="tooltip"><div class="arrow"></div><h3 class="popover-header"></h3><div class="popover-body"></div></div>';
|
||||
|
||||
class InputInvalid extends Error {
|
||||
constructor(validationMsg, invalidField, fieldContainer) {
|
||||
super(validationMsg, invalidField, fieldContainer);
|
||||
this.name = this.constructor.name;
|
||||
this.message = validationMsg;
|
||||
this.field = invalidField;
|
||||
this.container = fieldContainer;
|
||||
}
|
||||
}
|
||||
|
||||
const resetResults = () => {
|
||||
queryLocation.selectpicker('deselectAll');
|
||||
queryLocation.selectpicker('val', '');
|
||||
|
|
@ -27,6 +40,7 @@ const resetResults = () => {
|
|||
queryTarget.val('');
|
||||
resultsContainer.animsition('out', formContainer, '#');
|
||||
resultsContainer.hide();
|
||||
$('.hg-info-btn').remove();
|
||||
formContainer.show();
|
||||
formContainer.animsition('in');
|
||||
backButton.addClass('d-none');
|
||||
|
|
@ -93,7 +107,6 @@ $(document).ready(() => {
|
|||
outDuration: 800,
|
||||
transition: (url) => { window.location.href = url; },
|
||||
});
|
||||
|
||||
formContainer.animsition('in');
|
||||
});
|
||||
|
||||
|
|
@ -117,7 +130,7 @@ const queryApp = (queryType, queryTypeName, locationList, queryTarget) => {
|
|||
|
||||
$('#hg-results-title').html(resultsTitle);
|
||||
|
||||
$('#hg-submit-icon').empty().removeClass('hg-loading').html('<i class="remixicon-search-line"></i>');
|
||||
submitIcon.empty().removeClass('hg-loading').html('<i class="remixicon-search-line"></i>');
|
||||
|
||||
$.each(locationList, (n, loc) => {
|
||||
const locationName = $(`#${loc}`).data('display-name');
|
||||
|
|
@ -227,15 +240,51 @@ const queryApp = (queryType, queryTypeName, locationList, queryTarget) => {
|
|||
});
|
||||
};
|
||||
|
||||
$(document).on('InvalidInputEvent', (e, domField) => {
|
||||
console.log('event triggered');
|
||||
const errorField = $(domField);
|
||||
console.log(errorField);
|
||||
if (errorField.hasClass('is-invalid')) {
|
||||
console.log('has class');
|
||||
errorField.on('keyup', () => {
|
||||
console.log('keyup');
|
||||
errorField.removeClass('is-invalid');
|
||||
errorField.nextAll('.invalid-feedback').remove();
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
// Submit Form Action
|
||||
$('#lgForm').submit((event) => {
|
||||
event.preventDefault();
|
||||
$('#hg-submit-icon').empty().html('<i class="remixicon-loader-4-line"></i>').addClass('hg-loading');
|
||||
submitIcon.empty().html('<i class="remixicon-loader-4-line"></i>').addClass('hg-loading');
|
||||
const queryType = $('#query_type').val();
|
||||
const queryTypeTitle = $(`#${queryType}`).data('display-name');
|
||||
const queryLocation = $('#location').val();
|
||||
const queryTarget = $('#query_target').val();
|
||||
|
||||
try {
|
||||
// message, thing to circle in red, place to put error text
|
||||
if (!queryTarget) {
|
||||
const queryTargetContainer = $('#query_target');
|
||||
throw new InputInvalid(inputMessages.no_input, queryTargetContainer, queryTargetContainer.parent());
|
||||
}
|
||||
if (!queryType) {
|
||||
const queryTypeContainer = $('#query_type').next('.dropdown-toggle');
|
||||
throw new InputInvalid(inputMessages.no_query_type, queryTypeContainer, queryTypeContainer.parent());
|
||||
}
|
||||
if (queryLocation === undefined || queryLocation.length === 0) {
|
||||
const queryLocationContainer = $('#location').next('.dropdown-toggle');
|
||||
throw new InputInvalid(inputMessages.no_location, queryLocationContainer, queryLocationContainer.parent());
|
||||
}
|
||||
} catch (err) {
|
||||
err.field.addClass('is-invalid');
|
||||
err.container.append(`<div class="invalid-feedback px-1">${err.message}</div>`);
|
||||
submitIcon.empty().removeClass('hg-loading').html('<i class="remixicon-search-line"></i>');
|
||||
$(document).trigger('InvalidInputEvent', err.field);
|
||||
return false;
|
||||
}
|
||||
const queryTypeTitle = $(`#${queryType}`).data('display-name');
|
||||
queryApp(queryType, queryTypeTitle, queryLocation, queryTarget);
|
||||
$('#hg-form').animsition('out', $('#hg-results'), '#');
|
||||
$('#hg-form').hide();
|
||||
|
|
|
|||
|
|
@ -267,6 +267,11 @@
|
|||
margin-left: 1rem
|
||||
|
||||
// Fixes input group issue where button is 1px taller than the input element (default is 2px)
|
||||
|
||||
#hg-submit-button
|
||||
border-top-right-radius: .3rem
|
||||
border-bottom-right-radius: .3rem
|
||||
|
||||
.input-group-lg > .form-control:not(textarea),
|
||||
.input-group-lg > .custom-select,
|
||||
.bootstrap-select.form-control-lg .dropdown-toggle
|
||||
|
|
@ -431,4 +436,5 @@
|
|||
.modal-title
|
||||
padding-bottom: 1rem !important
|
||||
|
||||
.popover
|
||||
.form-control.is-invalid, .form-control.is-valid
|
||||
background-image: unset !important
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue