5c64846225
* Ui/transform enable (#9647) * Show Transform on engines list if enterprise * Add box-radio component * Add is-disabled styling for box-radio and fix tooltip styling when position: above * Add KMIP and Transform to possible features on has feature helper * Sidebranch: Transform Secret Engine Initial setup (#9625) * WIP // list transforms, console.logs and all * setup LIST transformations ajax request and draft out options-for-backend options * change from plural to singluar and add transform to secret-edit * create two transform edit components * modify transform model with new attrs * add adapterFor to connect transform adapter to transform-edit-form component * setup Allowed roles searchSelect component to search over new transform/role adapter and model. * clean up for PR * clean up linting errors * restructure adapter call, now it works. * remove console * setup template model for SearchSelect component * add props to form field and search select for styling Co-authored-by: Chelsea Shaw <chelshaw.dev@gmail.com> * Ui/transform language fixes (#9666) * Update casing and wording on Transform list route. Use generic list item for transformations * Add back js file for transformation-edit * Set up transform for tabs * Ui/create edit transformation fixes (#9668) * add conditional for masking vs tweak source based on type, and update text for create transformation * change order * fix error with stringArray * setup the edit/delete transformation view * clean up toolbar links * setup serializer to change response of mask character from keycode to character * change styling of label and sub-text size, confirmed with design * temp fix on templates vs template * add clickable list item * add space between template list * setup styling and structure for the rest of the show transformation. TODO: turn into components. * create transform-show-transformation component * add attachCapabilities to transform model and update transform-transformation-itme list accordingly * clean up liniting errors * address pr comments * remove leftover * clean up * Sidebranch: UI transform create and edit clean up (#9778) * clean up some of the TODOs * setup edit view with read only attributes for name and template * setup initial selected for search select component * fixes * hide templates form field for now * set selectLimit for search select component * hide power select if the select limit is greater than or equal to the selectedOptions length * clean up failing linting * address pr comments * Ui/fix list roles transformation (#9788) * Update search-select to pass backend to query if exists * Update role and template adapters * cleanup * Fix replace with static string * Ui/transform cleanup 2 (#9789) * amend encode/decode commands for now until design gets back with more details * restrict character count on masking input field * clean up selectLimit * show backend instead of transform in cli copy command * Show KMIP un-selectable if enterprise but no ADP module (#9780) * New component transform-edit-base * Duplicate RoleEdit as TransformEditBase and swap in all transform components * Roll back role-edit changes * Update to transform edit base * Remove extraeneous set backend type on transform components * formatting * Revert search-select changes * Update template/templates data on transformation (#9838) Co-authored-by: Angel Garbarino <Monkeychip@users.noreply.github.com>
156 lines
5.8 KiB
JavaScript
156 lines
5.8 KiB
JavaScript
import Component from '@ember/component';
|
|
import { inject as service } from '@ember/service';
|
|
import { task } from 'ember-concurrency';
|
|
import { computed } from '@ember/object';
|
|
import { singularize } from 'ember-inflector';
|
|
import layout from '../templates/components/search-select';
|
|
|
|
/**
|
|
* @module SearchSelect
|
|
* The `SearchSelect` is an implementation of the [ember-power-select-with-create](https://github.com/poteto/ember-cli-flash) used for form elements where options come dynamically from the API.
|
|
* @example
|
|
* <SearchSelect @id="group-policies" @models={{["policies/acl"]}} @onChange={{onChange}} @selectLimit={{2}} @inputValue={{get model valuePath}} @helpText="Policies associated with this group" @label="Policies" @fallbackComponent="string-list" />
|
|
*
|
|
* @param id {String} - The name of the form field
|
|
* @param models {String} - An array of model types to fetch from the API.
|
|
* @param onChange {Func} - The onchange action for this form field.
|
|
* @param inputValue {String | Array} - A comma-separated string or an array of strings.
|
|
* @param [helpText] {String} - Text to be displayed in the info tooltip for this form field
|
|
* @param [subText] {String} - Text to be displayed below the label
|
|
* @param [selectLimit] {Number} - A number that sets the limit to how many select options they can choose
|
|
* @param label {String} - Label for this form field
|
|
* @param [subLabel] {String} - a smaller label below the main Label
|
|
* @param fallbackComponent {String} - name of component to be rendered if the API call 403s
|
|
* @param [backend] {String} - name of the backend if the query for options needs additional information (eg. secret backend)
|
|
*
|
|
* @param options {Array} - *Advanced usage* - `options` can be passed directly from the outside to the
|
|
* power-select component. If doing this, `models` should not also be passed as that will overwrite the
|
|
* passed value.
|
|
* @param search {Func} - *Advanced usage* - Customizes how the power-select component searches for matches -
|
|
* see the power-select docs for more information.
|
|
*
|
|
*/
|
|
export default Component.extend({
|
|
layout,
|
|
'data-test-component': 'search-select',
|
|
classNames: ['field', 'search-select'],
|
|
store: service(),
|
|
|
|
onChange: () => {},
|
|
inputValue: computed(function() {
|
|
return [];
|
|
}),
|
|
selectedOptions: null, //list of selected options
|
|
options: null, //all possible options
|
|
shouldUseFallback: false,
|
|
shouldRenderName: false,
|
|
init() {
|
|
this._super(...arguments);
|
|
this.set('selectedOptions', this.inputValue || []);
|
|
},
|
|
didRender() {
|
|
this._super(...arguments);
|
|
let { oldOptions, options, selectedOptions } = this;
|
|
let hasFormattedInput = typeof selectedOptions.firstObject !== 'string';
|
|
if (options && !oldOptions && !hasFormattedInput) {
|
|
// this is the first time they've been set, so we need to format them
|
|
this.formatOptions(options);
|
|
}
|
|
this.set('oldOptions', options);
|
|
},
|
|
formatOptions: function(options) {
|
|
options = options.toArray().map(option => {
|
|
option.searchText = `${option.name} ${option.id}`;
|
|
return option;
|
|
});
|
|
let formattedOptions = this.selectedOptions.map(option => {
|
|
let matchingOption = options.findBy('id', option);
|
|
options.removeObject(matchingOption);
|
|
return {
|
|
id: option,
|
|
name: matchingOption ? matchingOption.name : option,
|
|
searchText: matchingOption ? matchingOption.searchText : option,
|
|
};
|
|
});
|
|
this.set('selectedOptions', formattedOptions);
|
|
if (this.options) {
|
|
options = this.options.concat(options).uniq();
|
|
}
|
|
this.set('options', options);
|
|
},
|
|
fetchOptions: task(function*() {
|
|
if (!this.models) {
|
|
if (this.options) {
|
|
this.formatOptions(this.options);
|
|
}
|
|
return;
|
|
}
|
|
for (let modelType of this.models) {
|
|
if (modelType.includes('identity')) {
|
|
this.set('shouldRenderName', true);
|
|
}
|
|
try {
|
|
let queryOptions = {};
|
|
if (this.backend) {
|
|
queryOptions = { backend: this.backend };
|
|
}
|
|
let options = yield this.store.query(modelType, queryOptions);
|
|
this.formatOptions(options);
|
|
} catch (err) {
|
|
if (err.httpStatus === 404) {
|
|
//leave options alone, it's okay
|
|
return;
|
|
}
|
|
if (err.httpStatus === 403) {
|
|
this.set('shouldUseFallback', true);
|
|
return;
|
|
}
|
|
//special case for storybook
|
|
if (this.staticOptions) {
|
|
let options = this.staticOptions;
|
|
this.formatOptions(options);
|
|
return;
|
|
}
|
|
throw err;
|
|
}
|
|
}
|
|
}).on('didInsertElement'),
|
|
handleChange() {
|
|
if (this.selectedOptions.length && typeof this.selectedOptions.firstObject === 'object') {
|
|
this.onChange(Array.from(this.selectedOptions, option => option.id));
|
|
} else {
|
|
this.onChange(this.selectedOptions);
|
|
}
|
|
},
|
|
actions: {
|
|
onChange(val) {
|
|
this.onChange(val);
|
|
},
|
|
createOption(optionId) {
|
|
let newOption = { name: optionId, id: optionId };
|
|
this.selectedOptions.pushObject(newOption);
|
|
this.handleChange();
|
|
},
|
|
selectOption(option) {
|
|
this.selectedOptions.pushObject(option);
|
|
this.options.removeObject(option);
|
|
this.handleChange();
|
|
},
|
|
discardSelection(selected) {
|
|
this.selectedOptions.removeObject(selected);
|
|
this.options.pushObject(selected);
|
|
this.handleChange();
|
|
},
|
|
constructSuggestion(id) {
|
|
return `Add new ${singularize(this.label)}: ${id}`;
|
|
},
|
|
hideCreateOptionOnSameID(id, options) {
|
|
if (options && options.length && options.firstObject.groupName) {
|
|
return !options.some(group => group.options.findBy('id', id));
|
|
}
|
|
let existingOption = this.options && (this.options.findBy('id', id) || this.options.findBy('name', id));
|
|
return !existingOption;
|
|
},
|
|
},
|
|
});
|