Merge pull request #8314 from hashicorp/f-ui/scaling-ux

UI: Scaling UI UX Improvements
This commit is contained in:
Michael Lange
2020-07-01 09:21:08 -07:00
committed by GitHub
10 changed files with 101 additions and 7 deletions

View File

@@ -9,7 +9,7 @@ const ESC = 27;
@classic
@classNames('stepper-input')
@classNameBindings('class', 'disabled:is-disabled')
@classNameBindings('class', 'disabled:is-disabled', 'disabled:tooltip', 'disabled:multiline')
export default class StepperInput extends Component {
min = 0;
max = 10;
@@ -40,9 +40,13 @@ export default class StepperInput extends Component {
@action
setValue(e) {
const newValue = Math.min(this.max, Math.max(this.min, e.target.value));
this.set('internalValue', newValue);
this.update(this.internalValue);
if (e.target.value !== '') {
const newValue = Math.floor(Math.min(this.max, Math.max(this.min, e.target.value)));
this.set('internalValue', newValue);
this.update(this.internalValue);
} else {
e.target.value = this.internalValue;
}
}
@action
@@ -52,6 +56,15 @@ export default class StepperInput extends Component {
}
}
@action
selectValue(e) {
e.target.select();
}
@action focusInput() {
this.element.querySelector('.stepper-input-input').focus();
}
update(value) {
debounce(this, sendUpdateAction, value, this.debounce);
}

View File

@@ -1,4 +1,5 @@
import Component from '@ember/component';
import { inject as service } from '@ember/service';
import { computed, action } from '@ember/object';
import { alias, oneWay } from '@ember/object/computed';
import { debounce } from '@ember/runloop';
@@ -10,12 +11,21 @@ import { lazyClick } from '../helpers/lazy-click';
@tagName('tr')
@classNames('task-group-row', 'is-interactive')
export default class TaskGroupRow extends Component {
@service can;
taskGroup = null;
debounce = 500;
@oneWay('taskGroup.count') count;
@alias('taskGroup.job.runningDeployment') runningDeployment;
@computed('runningDeployment')
get tooltipText() {
if (this.can.cannot('scale job')) return "You aren't allowed to scale task groups";
if (this.runningDeployment) return 'You cannot scale task groups during a deployment';
return undefined;
}
onClick() {}
click(event) {

View File

@@ -14,6 +14,7 @@ export default class TaskGroupController extends Controller.extend(
WithNamespaceResetting
) {
@service userSettings;
@service can;
queryParams = [
{
@@ -50,6 +51,13 @@ export default class TaskGroupController extends Controller.extend(
@alias('listSorted') listToSearch;
@alias('listSearched') sortedAllocations;
@computed('model.job.runningDeployment')
get tooltipText() {
if (this.can.cannot('scale job')) return "You aren't allowed to scale task groups";
if (this.model.job.runningDeployment) return 'You cannot scale task groups during a deployment';
return undefined;
}
@action
gotoAllocation(allocation) {
this.transitionToRoute('allocations.allocation', allocation);

View File

@@ -16,6 +16,7 @@
display: flex;
align-self: center;
padding-right: 0.75em;
cursor: pointer;
}
.stepper-input-input {

View File

@@ -12,6 +12,7 @@
max-width: 250px;
color: $white;
font-size: $size-7;
font-weight: $weight-normal;
overflow: hidden;
text-overflow: ellipsis;
line-height: 1.25;

View File

@@ -1,4 +1,7 @@
<label data-test-stepper-label class="stepper-input-label">{{yield}}</label>
<label
data-test-stepper-label
class="stepper-input-label"
onClick={{action "focusInput"}}>{{yield}}</label>
<input
data-test-stepper-input
type="number"
@@ -7,11 +10,13 @@
value={{internalValue}}
disabled={{disabled}}
class="stepper-input-input"
onFocus={{action "selectValue"}}
onKeyDown={{action "resetTextInput"}}
onChange={{action "setValue"}}>
<button
data-test-stepper-decrement
role="button"
aria-label="decrement"
class="stepper-input-stepper button {{class}}"
disabled={{or disabled (lte internalValue min)}}
onclick={{action "decrement"}}>
@@ -20,6 +25,7 @@
<button
data-test-stepper-increment
role="button"
aria-label="increment"
class="stepper-input-stepper button {{class}}"
disabled={{or disabled (gte internalValue max)}}
onclick={{action "increment"}}>

View File

@@ -8,11 +8,12 @@
{{#if taskGroup.scaling}}
<div
data-test-scale-controls
class="button-bar is-shadowless is-text bumper-left {{if (cannot "scale job") "tooltip"}}"
aria-label={{if (cannot "scale job") "You aren't allowed to scale task groups"}}>
class="button-bar is-shadowless is-text bumper-left {{if (or runningDeployment (cannot "scale job")) "tooltip multiline"}}"
aria-label={{tooltipText}}>
<button
data-test-scale="decrement"
role="button"
aria-label="decrement"
class="button is-xsmall is-light"
disabled={{or isMinimum runningDeployment (cannot "scale job")}}
onclick={{action "countDown"}}>
@@ -21,6 +22,7 @@
<button
data-test-scale="increment"
role="button"
aria-label="increment"
class="button is-xsmall is-light"
disabled={{or isMaximum runningDeployment (cannot "scale job")}}
onclick={{action "countUp"}}>

View File

@@ -12,6 +12,7 @@
{{#if model.scaling}}
<StepperInput
data-test-task-group-count-stepper
aria-label={{tooltipText}}
@min={{model.scaling.min}}
@max={{model.scaling.max}}
@value={{model.count}}

View File

@@ -164,4 +164,55 @@ module('Integration | Component | stepper input', function(hooks) {
await StepperInput.input.esc();
assert.equal(StepperInput.input.value, this.value);
});
test('clicking the label focuses in the input', async function(assert) {
this.setProperties(commonProperties());
await render(commonTemplate);
await StepperInput.clickLabel();
const input = find('[data-test-stepper-input]');
assert.equal(document.activeElement, input);
});
test('focusing the input selects the input value', async function(assert) {
this.setProperties(commonProperties());
await render(commonTemplate);
await StepperInput.input.focus();
assert.equal(
window
.getSelection()
.toString()
.trim(),
this.value.toString()
);
});
test('entering a fractional value floors the value', async function(assert) {
this.setProperties(commonProperties());
const newValue = 3.14159;
await render(commonTemplate);
await StepperInput.input.fill(newValue);
await settled();
assert.equal(StepperInput.input.value, Math.floor(newValue));
assert.ok(this.onChange.calledWith(Math.floor(newValue)));
});
test('entering an invalid value reverts the value', async function(assert) {
this.setProperties(commonProperties());
const newValue = 'NaN';
await render(commonTemplate);
await StepperInput.input.fill(newValue);
await settled();
assert.equal(StepperInput.input.value, this.value);
assert.notOk(this.onChange.called);
});
});

View File

@@ -14,6 +14,7 @@ export default scope => ({
scope,
label: text('[data-test-stepper-label]'),
clickLabel: clickable('[data-test-stepper-label]'),
input: {
scope: '[data-test-stepper-input]',