Merge branch 'master' into ui/styleguide
This commit is contained in:
commit
8d671b238b
27
README.md
27
README.md
@ -58,6 +58,19 @@ To start the app locally in the foreground and watch for changes:
|
||||
|
||||
script/dev_server
|
||||
|
||||
### Users
|
||||
|
||||
There are currently six mock users for development:
|
||||
|
||||
- Sam (a CCPO)
|
||||
- Amanda
|
||||
- Brandon
|
||||
- Christina
|
||||
- Dominick
|
||||
- Erica
|
||||
|
||||
To log in as one of them, navigate to `/login-dev?username=<lowercase name>`. For example `/login-dev?username=amanda`.
|
||||
|
||||
## Testing
|
||||
|
||||
To run lint, static analysis, and unit tests:
|
||||
@ -86,3 +99,17 @@ To render an icon use `{% module Icon('name') %}` in a template, where `name` is
|
||||
All icons used should be from the Noun Project, specifically [this collection](https://thenounproject.com/monstercritic/collection/tinicons-a-set-of-tiny-icons-perfect-for-ui-elemen/) if possible.
|
||||
|
||||
SVG markup should be cleaned an minified, [Svgsus](http://www.svgs.us/) works well.
|
||||
|
||||
## Deployment
|
||||
|
||||
The `/login-dev` endpoint is protected by HTTP basic auth when deployed. This can be configured for NGINX following the instructions [here](https://docs.nginx.com/nginx/admin-guide/security-controls/configuring-http-basic-authentication/). The following config should added within the main server block for the site:
|
||||
|
||||
```
|
||||
location /login-dev {
|
||||
auth_basic "Developer Access";
|
||||
auth_basic_user_file /etc/apache2/.htpasswd;
|
||||
[proxy information should follow this]
|
||||
}
|
||||
```
|
||||
|
||||
The location block will require the same proxy pass configuration as other location blocks for the app.
|
||||
|
@ -6,7 +6,7 @@ _DEV_USERS = {
|
||||
"sam": {
|
||||
"id": "164497f6-c1ea-4f42-a5ef-101da278c012",
|
||||
"first_name": "Sam",
|
||||
"last_name": "CCPO",
|
||||
"last_name": "Seeceepio",
|
||||
"atat_role": "ccpo"
|
||||
},
|
||||
|
||||
|
@ -5,3 +5,12 @@ def dev(self):
|
||||
|
||||
def matchesPath(self, href):
|
||||
return self.request.uri.startswith(href)
|
||||
|
||||
def modal(self, body):
|
||||
return self.render_string(
|
||||
"components/modal.html.to",
|
||||
body=body)
|
||||
|
||||
def modalOpen(self):
|
||||
# For now, just check a dummy URL param
|
||||
return self.get_argument("modal", False)
|
||||
|
@ -16,10 +16,11 @@ class Icon(UIModule):
|
||||
"components/icon.html.to", svg=svg.read(), name=name, classes=classes)
|
||||
|
||||
class SidenavItem(UIModule):
|
||||
def render(self, label, href, active=False, icon=None):
|
||||
def render(self, label, href, active=False, icon=None, subnav=None):
|
||||
return self.render_string(
|
||||
"navigation/_sidenav_item.html.to",
|
||||
label=label,
|
||||
href=href,
|
||||
active=active,
|
||||
icon=icon)
|
||||
icon=icon,
|
||||
subnav=subnav)
|
||||
|
@ -12,6 +12,7 @@
|
||||
@import 'elements/tables';
|
||||
@import 'elements/icons';
|
||||
@import 'elements/sidenav';
|
||||
@import 'elements/action_group';
|
||||
|
||||
@import 'components/layout';
|
||||
@import 'components/topbar';
|
||||
@ -19,6 +20,7 @@
|
||||
@import 'components/site_action';
|
||||
@import 'components/empty_state';
|
||||
@import 'components/alerts';
|
||||
@import 'components/modal';
|
||||
|
||||
@import 'sections/footer';
|
||||
@import 'sections/login';
|
||||
|
@ -44,6 +44,7 @@
|
||||
|
||||
.alert__title {
|
||||
@include h3;
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.alert__content {
|
||||
|
@ -8,6 +8,10 @@ body {
|
||||
> footer {
|
||||
margin-top: auto;
|
||||
}
|
||||
|
||||
&.modalOpen {
|
||||
overflow-y: hidden;
|
||||
}
|
||||
}
|
||||
|
||||
.global-layout {
|
||||
|
47
scss/components/_modal.scss
Normal file
47
scss/components/_modal.scss
Normal file
@ -0,0 +1,47 @@
|
||||
.modal {
|
||||
position: fixed;
|
||||
z-index: 3;
|
||||
left: 0;
|
||||
right: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
background-color: $color-overlay;
|
||||
|
||||
.modal__dialog {
|
||||
padding: $gap;
|
||||
height: 100vh;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
|
||||
@include media($medium-screen) {
|
||||
padding: $gap * 2;
|
||||
}
|
||||
|
||||
@include media($large-screen) {
|
||||
padding: $gap * 4;
|
||||
}
|
||||
|
||||
.modal__body {
|
||||
background-color: $color-white;
|
||||
padding: $gap * 2;
|
||||
flex-grow: 1;
|
||||
overflow-y: auto;
|
||||
max-width: 80rem;
|
||||
|
||||
@include media($medium-screen) {
|
||||
padding: $gap * 4;
|
||||
flex-grow: 0;
|
||||
}
|
||||
|
||||
h1, h2 {
|
||||
@include h3;
|
||||
}
|
||||
|
||||
:first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -106,6 +106,7 @@ $color-base: $color-black-light;
|
||||
$color-focus: $color-gray-light;
|
||||
$color-visited: $color-purple;
|
||||
|
||||
$color-overlay: rgba(#000, 0.5);
|
||||
$color-shadow: rgba(#000, 0.3);
|
||||
$color-transparent: rgba(#000, 0);
|
||||
|
||||
|
14
scss/elements/_action_group.scss
Normal file
14
scss/elements/_action_group.scss
Normal file
@ -0,0 +1,14 @@
|
||||
.action-group {
|
||||
display: flex;
|
||||
flex-direction: row-reverse;
|
||||
align-items: center;
|
||||
margin-top: $gap * 4;
|
||||
|
||||
.action-group__action {
|
||||
margin: 0 0 0 ($gap * 2);
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: $gap * 3;
|
||||
}
|
||||
}
|
@ -4,10 +4,6 @@
|
||||
* @source https://github.com/uswds/uswds/blob/develop/src/stylesheets/elements/_inputs.scss
|
||||
*/
|
||||
|
||||
from {
|
||||
margin-bottom: 3rem;
|
||||
}
|
||||
|
||||
select {
|
||||
border-radius: 0;
|
||||
-webkit-appearance: none;
|
||||
@ -28,3 +24,19 @@ select {
|
||||
padding-bottom: 0.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
.usa-search {
|
||||
padding-top: 2px;
|
||||
margin-right: 2rem;
|
||||
|
||||
input[type=search] {
|
||||
height: 4.4rem;
|
||||
font-size: 1.7rem;
|
||||
color: $color-black;
|
||||
}
|
||||
|
||||
button {
|
||||
min-height: 4.4rem;
|
||||
}
|
||||
|
||||
}
|
@ -43,4 +43,4 @@
|
||||
padding: 0 ($gap * 4);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -44,6 +44,7 @@
|
||||
&--active {
|
||||
@include h5;
|
||||
color: $color-primary;
|
||||
box-shadow: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -55,9 +56,21 @@
|
||||
li {
|
||||
.sidenav__link {
|
||||
@include h5;
|
||||
padding: ($gap * .75) ($gap * 3);
|
||||
padding: $gap * .75;
|
||||
padding-left: 4.5rem;
|
||||
border: 0;
|
||||
font-weight: normal;
|
||||
|
||||
.sidenav__link-icon {
|
||||
@include icon-size(12);
|
||||
flex-shrink: 0;
|
||||
margin-right: 1.5rem;
|
||||
margin-left: -3rem
|
||||
}
|
||||
|
||||
.sidenav__link-label {
|
||||
padding-left: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4,7 +4,7 @@
|
||||
* @source https://github.com/uswds/uswds/blob/develop/src/stylesheets/elements/_table.scss
|
||||
*/
|
||||
|
||||
table {
|
||||
table {
|
||||
@include panel-margin;
|
||||
min-width: 100%;
|
||||
|
||||
|
@ -9,25 +9,14 @@
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
p {
|
||||
margin: 0 0 ($gap * 2) 0;
|
||||
max-width: 45em;
|
||||
}
|
||||
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
font-family: $font-sans;
|
||||
|
||||
.usa-button {
|
||||
position: relative;
|
||||
bottom: 0.5rem;
|
||||
left: 1rem;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
h1 {
|
||||
margin-top: 0.5em;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
h2 {
|
||||
margin-top: 0;
|
||||
color: $color-gray;
|
||||
margin: ($gap * 2) 0;
|
||||
}
|
||||
|
||||
a,
|
||||
|
1
static/icons/plus.svg
Normal file
1
static/icons/plus.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" fill-rule="evenodd" clip-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="1.414"><path d="M6.966 6.992V1.009C6.988.417 7.322-.052 8.091.005c.505.074.884.488.903 1.004v5.966c2.049-.012 4.096-.002 6.143.073 1.031.113 1.292 1.968-.074 2.017H8.994v6.037c-.039 1.076-1.98 1.313-2.028 0V9.065H.898c-1.076-.039-1.313-1.972 0-2.02 2.022 0 4.045-.031 6.068-.053z" fill-rule="nonzero"/></svg>
|
After Width: | Height: | Size: 452 B |
@ -13,7 +13,7 @@
|
||||
{% end %}
|
||||
<link rel="icon" type="image/x-icon" href="/static/img/favicon.ico">
|
||||
</head>
|
||||
<body>
|
||||
<body class="{% if modalOpen() %} modalOpen{% end %}">
|
||||
{% include 'navigation/topbar.html.to' %}
|
||||
|
||||
<div class='global-layout'>
|
||||
@ -29,6 +29,8 @@
|
||||
</div>
|
||||
|
||||
{% include 'footer.html.to' %}
|
||||
|
||||
{% block modal %}{% end %}
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
7
templates/components/modal.html.to
Normal file
7
templates/components/modal.html.to
Normal file
@ -0,0 +1,7 @@
|
||||
<div class='modal'>
|
||||
<div class='modal__dialog' role='dialog' aria-modal='true'>
|
||||
<div class='modal__body'>
|
||||
{% raw body %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
@ -6,4 +6,19 @@
|
||||
|
||||
<span class="sidenav__link-label">{{label}}</span>
|
||||
</a>
|
||||
|
||||
{% if subnav and active %}
|
||||
<ul>
|
||||
{% for item in subnav %}
|
||||
<li>
|
||||
<a class="sidenav__link {% if item["active"] %}sidenav__link--active{% end %}" href="{{item["href"]}}" title="{{item["label"]}}">
|
||||
{% if "icon" in item %}
|
||||
{% module Icon(item["icon"], classes="sidenav__link-icon") %}
|
||||
{% end %}
|
||||
<span class="sidenav__link-label">{{item["label"]}}</span>
|
||||
</a>
|
||||
</li>
|
||||
{% end %}
|
||||
</ul>
|
||||
{% end %}
|
||||
</li>
|
||||
|
@ -1,10 +1,24 @@
|
||||
<div class="global-navigation sidenav global-navigation__context--{{context}}">
|
||||
<ul>
|
||||
{% if dev() %}
|
||||
{% module SidenavItem("Styleguide", href="/styleguide", icon="visible", active=matchesPath('/styleguide')) %}
|
||||
{% module SidenavItem("Styleguide",
|
||||
href="/styleguide",
|
||||
icon="visible",
|
||||
active=matchesPath('/styleguide'),
|
||||
subnav=[
|
||||
{"label":"Subnav 1", "href":"/styleguide?subnav1", "icon": "plus", "active": matchesPath('/styleguide?subnav1')},
|
||||
{"label":"Subnav 2", "href":"/styleguide?subnav2", "active": matchesPath('/styleguide?subnav2')},
|
||||
]) %}
|
||||
{% end %}
|
||||
|
||||
{% module SidenavItem("Requests", href="/requests", icon="document", active=matchesPath('/requests')) %}
|
||||
{% module SidenavItem("Requests",
|
||||
href="/requests",
|
||||
icon="document",
|
||||
active=matchesPath('/requests'),
|
||||
subnav=[
|
||||
{"label":"New Request", "href":"/requests/new", "icon": "plus", "active": matchesPath('/requests/new')},
|
||||
]
|
||||
) %}
|
||||
{% module SidenavItem("Workspaces", href="/workspaces", icon="cloud", active=matchesPath('/workspaces')) %}
|
||||
</ul>
|
||||
</div>
|
||||
|
@ -11,7 +11,7 @@
|
||||
</a>
|
||||
|
||||
<a href="/" class="topbar__link">
|
||||
<span class="topbar__link-label">Sam Seeceepio</span>
|
||||
<span class="topbar__link-label">{{ current_user["first_name"] + " " + current_user["last_name"] }}</span>
|
||||
{% module Icon('avatar', classes='topbar__link-icon') %}
|
||||
</a>
|
||||
</div>
|
||||
|
@ -3,49 +3,63 @@
|
||||
{% block content %}
|
||||
|
||||
|
||||
|
||||
{% module Alert('Pending Financial Verification',
|
||||
message="<p>Your next step is to create a Task Order (T.O.) associated with JEDI Cloud. Please consult a Contracting Officer (KO) or Contracting Officer Representative (COR) to help with this step.</p>"
|
||||
) %}
|
||||
|
||||
<div class="col col--grow">
|
||||
|
||||
<div class='panel'>
|
||||
<div class='row'>
|
||||
<div class='col col--grow col--pad'>
|
||||
<form class="usa-search usa-search-small">
|
||||
<label class="usa-sr-only" for="search-field-small">Search small</label>
|
||||
<input id="search-field-small" type="search" name="search" placeholder="Search by Order ID">
|
||||
<button type="submit">
|
||||
<span class="usa-sr-only">Search</span>
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
<div class='col col--grow col--pad'>
|
||||
<select id="filter_status" name="filter_status" required="">
|
||||
<option value="" selected disabled>Filter by status</option>
|
||||
<option value="">Active</option>
|
||||
<option value="">Pending</option>
|
||||
<option value="">Denied</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="panel">
|
||||
|
||||
<div class="panel__content">
|
||||
|
||||
<div class="panel__heading">
|
||||
<h1>Requests <a class="usa-button usa-button-secondary" href='{{ reverse_url('request_new') }}'>New Request</a></h1>
|
||||
</div>
|
||||
|
||||
<table class="usa-table-borderless" width="100%">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">Order ID</th>
|
||||
<th scope="col">Request Date</th>
|
||||
<th scope="col">Requester</th>
|
||||
<th scope="col">Total Apps</th>
|
||||
<th scope="col">Status</th>
|
||||
<th scope="col">Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for r in requests %}
|
||||
<tr>
|
||||
<th scope="row"><a href="{{ reverse_url('request_form_update', 1, r['order_id']) }}">{{ r['order_id'] }}</a>
|
||||
{% if r['is_new'] %}<span class="usa-label">New</span>
|
||||
</th>
|
||||
{% end %}
|
||||
<td>{{ r['date'] }}</td>
|
||||
<td>{{ r['full_name'] }}</td>
|
||||
<td>{{ r['app_count'] }}</td>
|
||||
<td>{{ r['status'] }}</td>
|
||||
<td><a href="">Download</a></td>
|
||||
</tr>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Order ID</th>
|
||||
<th>Request Date</th>
|
||||
<th>Requester</th>
|
||||
<th>Total Apps</th>
|
||||
<th>Status</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for r in requests %}
|
||||
<tr>
|
||||
<td><a href="{{ reverse_url('request_form_update', 1, r['order_id']) }}">{{ r['order_id'] }}</a>
|
||||
{% if r['is_new'] %}<span class="usa-label">New</span>
|
||||
</td>
|
||||
{% end %}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
<td>{{ r['date'] }}</td>
|
||||
<td>{{ r['full_name'] }}</td>
|
||||
<td>{{ r['app_count'] }}</td>
|
||||
<td>{{ r['status'] }}</td>
|
||||
<td><a href="">Download</a></td>
|
||||
</tr>
|
||||
{% end %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
@ -1,5 +1,22 @@
|
||||
{% extends "base.html.to" %}
|
||||
|
||||
{% block modal %}
|
||||
{% if modalOpen() %}
|
||||
{% apply modal %}
|
||||
<h1>A modal dialog</h1>
|
||||
|
||||
<p>We count thirty Rebel ships, Lord Vader. But they're so small they're evading our turbo-lasers! We'll have to destroy them ship to ship. Get the crews to their fighters. Luke, let me know when you're going in. I'm on my way in now... Watch yourself! There's a lot of fire coming from the right side of that deflection tower. I'm on it. Squad leaders, we've picked up a new group of signals. Enemy fighters coming your way.</p>
|
||||
<p>I hope the old man got that tractor beam out if commission, or this is going to be a real short trip. Okay, hit it! We're coming up on the sentry ships. Hold 'em off! Angle the deflector shields while I charge up the main guns! I can't believe he's gone. There wasn't anything you could have done. Come on, buddy, we're not out of this yet! You in, kid? Okay, stay sharp!</p>
|
||||
|
||||
<div class='action-group'>
|
||||
<a href='/styleguide' class='action-group__action usa-button'>Close</a>
|
||||
<a href='/styleguide' class='action-group__action'>This also closes the modal</a>
|
||||
</div>
|
||||
{% end %}
|
||||
{% end %}
|
||||
{% end %}
|
||||
|
||||
|
||||
{% block content %}
|
||||
|
||||
{% module Alert('A Warning Alert',
|
||||
@ -7,7 +24,7 @@
|
||||
<p>This is a message. It is a very important message. Please note, <strong>proper semantic markup is required</strong> here, such as paragraph tags. Don't omit paragraph tags!</p>\
|
||||
<p>Also note the same for actions below. You'll need to include the full link markup.</p>\
|
||||
",
|
||||
actions="<a href='/styleguide'>Do something</a>",
|
||||
actions="<a href='/styleguide?modal=True'>Open a Modal Dialog</a>",
|
||||
level='warning'
|
||||
) %}
|
||||
|
||||
@ -191,5 +208,10 @@
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<div class='action-group'>
|
||||
<a href='/styleguide' class='action-group__action usa-button usa-button-big'>Action Group Button</a>
|
||||
<a href='/styleguide' class='action-group__action'>Action group link</a>
|
||||
</div>
|
||||
</div>
|
||||
{% end %}
|
||||
|
Loading…
x
Reference in New Issue
Block a user