forked from spinnaker/deck
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(google): add support for partnerMetadata in GCE servergroup (spi…
…nnaker#10150) * feat(google): add support for partnerMetadata in GCE servergroup * feat(google): fix prettier issues * feat(google): render JSON object as its string representation * feat(google): update tests with textarea * feat(google): fix prettier issue * feat(google): move display logic to controller * feat(google): fix tests * feat(google): expect JSON string in test * feat(google): use formatValueForDisplay in ng-model * feat(google): define json-text directive to handle view * feat(google): add space to JSON format * feat(google): remove hiddenKeys test
- Loading branch information
1 parent
14977d1
commit 6ce262a
Showing
9 changed files
with
297 additions
and
0 deletions.
There are no files selected for viewing
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
56 changes: 56 additions & 0 deletions
56
packages/core/src/forms/mapObjectEditor/mapObjectEditor.component.html
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
<form name="mapObjectEditor"> | ||
<div class="sm-label-left" ng-if="$ctrl.label"> | ||
<b>{{ $ctrl.label }}</b> | ||
</div> | ||
<input class="form-control input-sm" ng-model="$ctrl.model" ng-if="$ctrl.isParameterized" /> | ||
<table class="table table-condensed packed tags {{ $ctrl.tableClass }}" ng-if="!$ctrl.isParameterized"> | ||
<thead> | ||
<tr ng-if="!$ctrl.labelsLeft"> | ||
<th ng-bind="$ctrl.keyLabel"></th> | ||
<th ng-bind="$ctrl.valueLabel"></th> | ||
<th></th> | ||
</tr> | ||
</thead> | ||
<tbody> | ||
<tr ng-repeat="pair in $ctrl.backingModel" ng-if="!$ctrl.hiddenKeys.includes(pair.key)"> | ||
<td class="table-label" ng-if="$ctrl.labelsLeft"> | ||
<b>{{ $ctrl.keyLabel }}</b> | ||
</td> | ||
<td> | ||
<input | ||
class="form-control input input-sm" | ||
type="text" | ||
name="{{ $index }}" | ||
ng-model="pair.key" | ||
validate-unique="pair.checkUnique" | ||
/> | ||
<div class="error-message" ng-if="mapObjectEditor[$index].$error.validateUnique">Duplicate key</div> | ||
</td> | ||
<td class="table-label" ng-if="$ctrl.labelsLeft"> | ||
<b>{{ $ctrl.valueLabel }}</b> | ||
</td> | ||
<td> | ||
<textarea json-text class="form-control input input-sm" ng-model="pair.value" rows="4"></textarea> | ||
</td> | ||
<td> | ||
<div class="form-control-static"> | ||
<a href ng-click="$ctrl.removeField($index)"> | ||
<span class="glyphicon glyphicon-trash"></span> | ||
<span class="sr-only">Remove field</span> | ||
</a> | ||
</div> | ||
</td> | ||
</tr> | ||
</tbody> | ||
<tfoot> | ||
<tr> | ||
<td colspan="{{ $ctrl.columnCount }}"> | ||
<button class="btn btn-block btn-sm add-new" ng-click="$ctrl.addField()"> | ||
<span class="glyphicon glyphicon-plus-sign"></span> | ||
{{ $ctrl.addButtonLabel }} | ||
</button> | ||
</td> | ||
</tr> | ||
</tfoot> | ||
</table> | ||
</form> |
113 changes: 113 additions & 0 deletions
113
packages/core/src/forms/mapObjectEditor/mapObjectEditor.component.js
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,113 @@ | ||
'use strict'; | ||
|
||
import * as angular from 'angular'; | ||
import { isString } from 'lodash'; | ||
|
||
import { CORE_VALIDATION_VALIDATEUNIQUE_DIRECTIVE } from '../../validation/validateUnique.directive'; | ||
|
||
import './mapObjectEditor.component.less'; | ||
|
||
export const CORE_FORMS_MAPOBJECTEDITOR_MAPOBJECTEDITOR_COMPONENT = 'spinnaker.core.forms.mapObjectEditor.component'; | ||
export const name = CORE_FORMS_MAPOBJECTEDITOR_MAPOBJECTEDITOR_COMPONENT; // for backwards compatibility | ||
angular | ||
.module(CORE_FORMS_MAPOBJECTEDITOR_MAPOBJECTEDITOR_COMPONENT, [CORE_VALIDATION_VALIDATEUNIQUE_DIRECTIVE]) | ||
.directive('jsonText', function () { | ||
return { | ||
restrict: 'A', | ||
require: 'ngModel', | ||
link: function (scope, element, attr, ngModel) { | ||
function into(input) { | ||
return JSON.parse(input); | ||
} | ||
function out(data) { | ||
return JSON.stringify(data, null, 2); | ||
} | ||
ngModel.$parsers.push(into); | ||
ngModel.$formatters.push(out); | ||
}, | ||
}; | ||
}) | ||
.component('mapObjectEditor', { | ||
bindings: { | ||
model: '=', | ||
keyLabel: '@', | ||
valueLabel: '@', | ||
addButtonLabel: '@', | ||
allowEmpty: '=?', | ||
onChange: '&', | ||
labelsLeft: '<?', | ||
label: '@', | ||
hiddenKeys: '<', | ||
}, | ||
controller: [ | ||
'$scope', | ||
function ($scope) { | ||
this.backingModel = []; | ||
|
||
const modelKeys = () => Object.keys(this.model); | ||
|
||
this.addField = () => { | ||
this.backingModel.push({ key: '', value: {}, checkUnique: modelKeys() }); | ||
// do not fire the onChange event, since no values have been committed to the object | ||
}; | ||
|
||
this.removeField = (index) => { | ||
this.backingModel.splice(index, 1); | ||
this.synchronize(); | ||
this.onChange(); | ||
}; | ||
|
||
// Clears existing values from model, then replaces them | ||
this.synchronize = () => { | ||
if (this.isParameterized) { | ||
return; | ||
} | ||
const modelStart = JSON.stringify(this.model); | ||
const allKeys = this.backingModel.map((pair) => pair.key); | ||
modelKeys().forEach((key) => delete this.model[key]); | ||
this.backingModel.forEach((pair) => { | ||
if (pair.key && (this.allowEmpty || pair.value)) { | ||
try { | ||
// Parse value if it is a valid JSON object | ||
this.model[pair.key] = JSON.parse(pair.value); | ||
} catch (e) { | ||
// If value is not a valid JSON object, just store the raw value | ||
this.model[pair.key] = pair.value; | ||
} | ||
} | ||
// include other keys to verify no duplicates | ||
pair.checkUnique = allKeys.filter((key) => pair.key !== key); | ||
}); | ||
if (modelStart !== JSON.stringify(this.model)) { | ||
this.onChange(); | ||
} | ||
}; | ||
|
||
// In Angular 1.7 Directive bindings were removed in the constructor, default values now must be instantiated within $onInit | ||
// See https://docs.angularjs.org/guide/migration#-compile- and https://docs.angularjs.org/guide/migration#migrate1.5to1.6-ng-services-$compile | ||
this.$onInit = () => { | ||
// Set default values for optional fields | ||
this.onChange = this.onChange || angular.noop; | ||
this.keyLabel = this.keyLabel || 'Key'; | ||
this.valueLabel = this.valueLabel || 'Value'; | ||
this.addButtonLabel = this.addButtonLabel || 'Add Field'; | ||
this.allowEmpty = this.allowEmpty || false; | ||
this.labelsLeft = this.labelsLeft || false; | ||
this.tableClass = this.label ? '' : 'no-border-top'; | ||
this.columnCount = this.labelsLeft ? 5 : 3; | ||
this.model = this.model || {}; | ||
this.isParameterized = isString(this.model); | ||
this.hiddenKeys = this.hiddenKeys || []; | ||
|
||
if (this.model && !this.isParameterized) { | ||
modelKeys().forEach((key) => { | ||
this.backingModel.push({ key: key, value: this.model[key] }); | ||
}); | ||
} | ||
}; | ||
|
||
$scope.$watch(() => JSON.stringify(this.backingModel), this.synchronize); | ||
}, | ||
], | ||
templateUrl: require('./mapObjectEditor.component.html'), | ||
}); |
9 changes: 9 additions & 0 deletions
9
packages/core/src/forms/mapObjectEditor/mapObjectEditor.component.less
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
map-object-editor { | ||
.table.no-border-top { | ||
border-top: 2px solid var(--color-white); | ||
|
||
.table-label { | ||
padding: 0.8rem 0 0 1rem; | ||
} | ||
} | ||
} |
85 changes: 85 additions & 0 deletions
85
packages/core/src/forms/mapObjectEditor/mapObjectEditor.component.spec.js
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
'use strict'; | ||
|
||
describe('Component: mapObjectEditor', function () { | ||
var scope; | ||
|
||
beforeEach(window.module(require('./mapObjectEditor.component').name)); | ||
|
||
beforeEach( | ||
window.inject(function ($rootScope, $compile) { | ||
scope = $rootScope.$new(); | ||
this.compile = $compile; | ||
}), | ||
); | ||
|
||
it('initializes with provided values', function () { | ||
scope.model = { foo: { bar: 'baz' }, bah: 11 }; | ||
let dom = this.compile('<map-object-editor model="model"></map-object-editor>')(scope); | ||
scope.$digest(); | ||
|
||
expect(dom.find('input').length).toBe(2); | ||
expect(dom.find('textarea').length).toBe(2); | ||
|
||
expect(dom.find('input').get(0).value).toBe('foo'); | ||
expect(dom.find('textarea').get(0).value).toBe(JSON.stringify({ bar: 'baz' }, null, 2)); | ||
expect(dom.find('input').get(1).value).toBe('bah'); | ||
expect(dom.find('textarea').get(1).value).toBe('11'); | ||
}); | ||
|
||
describe('adding new entries', function () { | ||
it('creates a new row in the table, but does not synchronize to model', function () { | ||
scope.model = {}; | ||
let dom = this.compile('<map-object-editor model="model"></map-object-editor>')(scope); | ||
scope.$digest(); | ||
dom.find('button').click(); | ||
expect(dom.find('tbody tr').length).toBe(1); | ||
expect(dom.find('input').length).toBe(1); | ||
expect(dom.find('textarea').length).toBe(1); | ||
}); | ||
|
||
it('does not flag multiple new rows without keys as having duplicate keys', function () { | ||
scope.model = {}; | ||
let dom = this.compile('<map-object-editor model="model"></map-object-editor>')(scope); | ||
scope.$digest(); | ||
dom.find('button').click(); | ||
dom.find('button').click(); | ||
|
||
expect(dom.find('tbody tr').length).toBe(2); | ||
expect(dom.find('input').length).toBe(2); | ||
expect(dom.find('textarea').length).toBe(2); | ||
|
||
expect(dom.find('.error-message').length).toBe(0); | ||
}); | ||
}); | ||
|
||
describe('removing entries', function () { | ||
it('removes the entry when the trash can is clicked', function () { | ||
scope.model = { foo: { bar: 'baz' } }; | ||
let dom = this.compile('<map-object-editor model="model"></map-object-editor>')(scope); | ||
scope.$digest(); | ||
|
||
expect(dom.find('input').length).toBe(1); | ||
expect(dom.find('textarea').length).toBe(1); | ||
|
||
dom.find('a').click(); | ||
|
||
expect(dom.find('tbody tr').length).toBe(0); | ||
expect(dom.find('input').length).toBe(0); | ||
expect(dom.find('textarea').length).toBe(0); | ||
expect(scope.model.foo).toBeUndefined(); | ||
}); | ||
}); | ||
|
||
describe('duplicate key handling', function () { | ||
it('provides a warning when a duplicate key is entered', function () { | ||
scope.model = { a: { bar: 'baz' }, b: '2' }; | ||
let dom = this.compile('<map-object-editor model="model"></map-object-editor>')(scope); | ||
scope.$digest(); | ||
|
||
$(dom.find('input')[1]).val('a').trigger('input'); | ||
scope.$digest(); | ||
|
||
expect(dom.find('.error-message').length).toBe(1); | ||
}); | ||
}); | ||
}); |
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
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
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
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