Merge pull request #233 from dod-ccpo/ui/ws-role-picker
UI/Selector Vue component
This commit is contained in:
commit
1c73c1a191
@ -102,3 +102,31 @@ COMPLETION_DATE_RANGES = [
|
|||||||
("3-6 months", "3-6 months"),
|
("3-6 months", "3-6 months"),
|
||||||
("Above 12 months", "Above 12 months"),
|
("Above 12 months", "Above 12 months"),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
WORKSPACE_ROLES = [
|
||||||
|
(
|
||||||
|
"owner",
|
||||||
|
"Workspace Owner",
|
||||||
|
"Can add, edit, deactivate access to all projects, environments, and members. Can view budget reports. Can start and edit JEDI Cloud requests.",
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"admin",
|
||||||
|
"Administrator",
|
||||||
|
"Can add and edit projects, environments, members, but cannot deactivate. Cannot view budget reports or JEDI Cloud requests.",
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"developer",
|
||||||
|
"Developer",
|
||||||
|
"Can view only the projects and environments they are granted access to. Can also view members associated with each environment.",
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"billing_auditor",
|
||||||
|
"Billing Auditor",
|
||||||
|
"Can view only the projects and environments they are granted access to. Can also view budgets and reports associated with the workspace.",
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"security_auditor",
|
||||||
|
"Security Auditor",
|
||||||
|
"Can view only the projects and environments they are granted access to. Can also view activity logs.",
|
||||||
|
),
|
||||||
|
]
|
||||||
|
@ -6,6 +6,8 @@ from wtforms.validators import Required, Email, Length
|
|||||||
from atst.forms.validators import IsNumber
|
from atst.forms.validators import IsNumber
|
||||||
from atst.forms.fields import SelectField
|
from atst.forms.fields import SelectField
|
||||||
|
|
||||||
|
from .data import WORKSPACE_ROLES
|
||||||
|
|
||||||
|
|
||||||
class NewMemberForm(Form):
|
class NewMemberForm(Form):
|
||||||
|
|
||||||
@ -14,15 +16,5 @@ class NewMemberForm(Form):
|
|||||||
email = EmailField("Email Address", validators=[Required(), Email()])
|
email = EmailField("Email Address", validators=[Required(), Email()])
|
||||||
dod_id = StringField("DOD ID", validators=[Required(), Length(min=10), IsNumber()])
|
dod_id = StringField("DOD ID", validators=[Required(), Length(min=10), IsNumber()])
|
||||||
workspace_role = SelectField(
|
workspace_role = SelectField(
|
||||||
"Workspace Role",
|
"Workspace Role", choices=WORKSPACE_ROLES, validators=[Required()], default=""
|
||||||
choices=[
|
|
||||||
("", "Select a Role"),
|
|
||||||
("admin", "Admin"),
|
|
||||||
("billing_auditor", "Billing Auditor"),
|
|
||||||
("ccpo", "CCPO"),
|
|
||||||
("developer", "Developer"),
|
|
||||||
("owner", "Owner"),
|
|
||||||
("security_auditor", "Security Auditor"),
|
|
||||||
],
|
|
||||||
validators=[Required()],
|
|
||||||
)
|
)
|
||||||
|
44
js/components/selector.js
Normal file
44
js/components/selector.js
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
import { VPopover } from 'v-tooltip'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'selector',
|
||||||
|
|
||||||
|
components: {
|
||||||
|
VPopover
|
||||||
|
},
|
||||||
|
|
||||||
|
props: {
|
||||||
|
choices: Array,
|
||||||
|
defaultLabel: String,
|
||||||
|
initialErrors: Array,
|
||||||
|
initialChoice: {
|
||||||
|
type: String,
|
||||||
|
default: null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
data: function () {
|
||||||
|
return {
|
||||||
|
value: this.initialChoice || null,
|
||||||
|
showError: (this.initialErrors && this.initialErrors.length) || false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
label: function () {
|
||||||
|
return this.value
|
||||||
|
? this.choices.find((choice) => {
|
||||||
|
return this.value === choice[0]
|
||||||
|
})[1]
|
||||||
|
: this.defaultLabel
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
change: function (e) {
|
||||||
|
this.value = e.target.value
|
||||||
|
this.showError = false
|
||||||
|
setTimeout(() => this.$refs.popover.hide(), 300)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
@ -13,6 +13,7 @@ import financial from './components/forms/financial'
|
|||||||
import toggler from './components/toggler'
|
import toggler from './components/toggler'
|
||||||
import NewProject from './components/forms/new_project'
|
import NewProject from './components/forms/new_project'
|
||||||
import Modal from './mixins/modal'
|
import Modal from './mixins/modal'
|
||||||
|
import selector from './components/selector'
|
||||||
|
|
||||||
Vue.use(VTooltip)
|
Vue.use(VTooltip)
|
||||||
|
|
||||||
@ -28,7 +29,8 @@ const app = new Vue({
|
|||||||
DetailsOfUse,
|
DetailsOfUse,
|
||||||
poc,
|
poc,
|
||||||
financial,
|
financial,
|
||||||
NewProject
|
NewProject,
|
||||||
|
selector
|
||||||
},
|
},
|
||||||
mounted: function() {
|
mounted: function() {
|
||||||
const modalOpen = document.querySelector("#modalOpen")
|
const modalOpen = document.querySelector("#modalOpen")
|
||||||
|
@ -33,6 +33,8 @@
|
|||||||
@import 'components/progress_menu.scss';
|
@import 'components/progress_menu.scss';
|
||||||
@import 'components/search_bar';
|
@import 'components/search_bar';
|
||||||
@import 'components/forms';
|
@import 'components/forms';
|
||||||
|
@import 'components/selector';
|
||||||
|
|
||||||
|
|
||||||
@import 'sections/login';
|
@import 'sections/login';
|
||||||
@import 'sections/request_approval';
|
@import 'sections/request_approval';
|
||||||
|
54
styles/components/_selector.scss
Normal file
54
styles/components/_selector.scss
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
.selector {
|
||||||
|
max-width: 30em;
|
||||||
|
|
||||||
|
.trigger {
|
||||||
|
display: block !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
legend {
|
||||||
|
@include h4;
|
||||||
|
padding-bottom: $gap / 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.selector__button {
|
||||||
|
width: 100%;
|
||||||
|
height: $input-height;
|
||||||
|
margin: 0;
|
||||||
|
padding: $input-padding-vertical 0.7em;
|
||||||
|
line-height: $input-line-height;
|
||||||
|
color: $color-base;
|
||||||
|
font-size: $base-font-size;
|
||||||
|
font-weight: $font-normal;
|
||||||
|
text-align: left;
|
||||||
|
border-width: 1px;
|
||||||
|
border-style: solid;
|
||||||
|
border-color: $color-gray;
|
||||||
|
border-radius: 0;
|
||||||
|
box-sizing: border-box;
|
||||||
|
background-color: $color-white;
|
||||||
|
background-image: none, url('#{$image-path}/arrow-both.svg'), url('#{$image-path}/arrow-both.png');
|
||||||
|
background-position: right 1.3rem center;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-size: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tooltip {
|
||||||
|
&.popover {
|
||||||
|
.popover-inner {
|
||||||
|
padding: 0;
|
||||||
|
|
||||||
|
.block-list {
|
||||||
|
margin: 0;
|
||||||
|
|
||||||
|
label {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.popover-arrow {
|
||||||
|
background-color: $color-white;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -106,6 +106,8 @@
|
|||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
&::before {
|
&::before {
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
|
margin-right: 0;
|
||||||
|
margin-left: $gap * 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
|
@ -37,7 +37,8 @@
|
|||||||
|
|
||||||
input,
|
input,
|
||||||
textarea,
|
textarea,
|
||||||
select {
|
select,
|
||||||
|
.selector__button {
|
||||||
border-color: $state-color;
|
border-color: $state-color;
|
||||||
border-width: $border-width;
|
border-width: $border-width;
|
||||||
}
|
}
|
||||||
@ -72,6 +73,10 @@
|
|||||||
position: relative;
|
position: relative;
|
||||||
clear: both;
|
clear: both;
|
||||||
|
|
||||||
|
dd {
|
||||||
|
font-weight: normal;
|
||||||
|
}
|
||||||
|
|
||||||
.icon-validation {
|
.icon-validation {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 100%;
|
top: 100%;
|
||||||
@ -105,7 +110,8 @@
|
|||||||
|
|
||||||
input,
|
input,
|
||||||
textarea,
|
textarea,
|
||||||
select {
|
select,
|
||||||
|
.selector__button {
|
||||||
@include line-max;
|
@include line-max;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
|
@ -68,7 +68,6 @@
|
|||||||
background: $color;
|
background: $color;
|
||||||
color: black;
|
color: black;
|
||||||
padding: 24px;
|
padding: 24px;
|
||||||
border-radius: 5px;
|
|
||||||
box-shadow: 0 5px 30px rgba(black, .1);
|
box-shadow: 0 5px 30px rgba(black, .1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
64
templates/components/selector.html
Normal file
64
templates/components/selector.html
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
{% from "components/icon.html" import Icon %}
|
||||||
|
|
||||||
|
{# expects a wtforms SelectField instance #}
|
||||||
|
{% macro Selector(field, default_label='Select an option') -%}
|
||||||
|
|
||||||
|
<selector
|
||||||
|
v-bind:choices='{{ field.choices | tojson }}'
|
||||||
|
default-label='{{ default_label }}'
|
||||||
|
{% if field.data %}initial-choice='{{ field.data }}'{% endif %}
|
||||||
|
{% if field.errors %}v-bind:initial-errors='{{ field.errors }}'{% endif %}
|
||||||
|
inline-template>
|
||||||
|
|
||||||
|
<fieldset v-bind:class="['selector usa-input', { 'usa-input--error': initialErrors }]">
|
||||||
|
<v-popover v-bind:container='false' ref='popover'>
|
||||||
|
<legend>
|
||||||
|
{{ field.label | striptags }}
|
||||||
|
|
||||||
|
{% if field.description %}
|
||||||
|
<span class='usa-input__help'>{{ field.description | safe }}</span>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
<span v-show='showError'>{{ Icon('alert',classes="icon-validation") }}</span>
|
||||||
|
</legend>
|
||||||
|
|
||||||
|
<button class='selector__button' type='button' v-html='label'></button>
|
||||||
|
|
||||||
|
<span v-show='showError'>{{ Icon('alert',classes="icon-validation") }}</span>
|
||||||
|
|
||||||
|
<input type='hidden' name='{{ field.name }}' v-bind:value='value'/>
|
||||||
|
|
||||||
|
<template v-if='showError'>
|
||||||
|
<span v-if='initialErrors' v-for='error in initialErrors' class='usa-input__message' v-html='error'></span>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template slot='popover'>
|
||||||
|
<div class='block-list'>
|
||||||
|
<ul>
|
||||||
|
<li v-for='choice in choices' class='block-list__item block-list__item--selectable'>
|
||||||
|
<template v-if='choice[0] !== ""'>
|
||||||
|
<input
|
||||||
|
type='radio'
|
||||||
|
v-bind:id="'{{ field.name }}_' + choice[0]"
|
||||||
|
v-bind:value='choice[0]'
|
||||||
|
v-bind:checked='value === choice[0]'
|
||||||
|
v-on:change='change'/>
|
||||||
|
<label v-bind:for="'{{ field.name }}_' + choice[0]">
|
||||||
|
<template v-if='choices[2]'>
|
||||||
|
<dl>
|
||||||
|
<dt v-html='choice[1]'></dt>
|
||||||
|
<dd v-html='choice[2]'></dd>
|
||||||
|
</dl>
|
||||||
|
</template>
|
||||||
|
<span v-else v-html='choice[1]'>
|
||||||
|
</label>
|
||||||
|
</template>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</v-popover>
|
||||||
|
</fieldset>
|
||||||
|
</selector>
|
||||||
|
|
||||||
|
{%- endmacro %}
|
@ -3,6 +3,7 @@
|
|||||||
{% from "components/icon.html" import Icon %}
|
{% from "components/icon.html" import Icon %}
|
||||||
{% from "components/text_input.html" import TextInput %}
|
{% from "components/text_input.html" import TextInput %}
|
||||||
{% from "components/options_input.html" import OptionsInput %}
|
{% from "components/options_input.html" import OptionsInput %}
|
||||||
|
{% from "components/selector.html" import Selector %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
|
|
||||||
@ -20,7 +21,7 @@
|
|||||||
{{ TextInput(form.last_name) }}
|
{{ TextInput(form.last_name) }}
|
||||||
{{ TextInput(form.email,placeholder='jane@mail.mil', validation='email') }}
|
{{ TextInput(form.email,placeholder='jane@mail.mil', validation='email') }}
|
||||||
{{ TextInput(form.dod_id,placeholder='10-digit number on the back of the CAC', validation='dodId') }}
|
{{ TextInput(form.dod_id,placeholder='10-digit number on the back of the CAC', validation='dodId') }}
|
||||||
{{ OptionsInput(form.workspace_role) }}
|
{{ Selector(form.workspace_role) }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user