UNPKG

api-console-assets

Version:

This repo only exists to publish api console components to npm

525 lines (486 loc) 19.6 kB
<!-- @license Copyright 2016 The Advanced REST client authors <arc@mulesoft.com> Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. --> <link rel="import" href="../polymer/polymer.html"> <link rel="import" href="../paper-masked-input/paper-masked-input.html"> <link rel="import" href="../paper-icon-button/paper-icon-button.html"> <link rel="import" href="../paper-input/paper-input.html"> <link rel="import" href="../arc-icons/arc-icons.html"> <link rel="import" href="../iron-form/iron-form.html"> <link rel="import" href="../markdown-styles/markdown-styles.html"> <link rel="import" href="../iron-collapse/iron-collapse.html"> <link rel="import" href="../marked-element/marked-element.html"> <link rel="import" href="../paper-dropdown-menu/paper-dropdown-menu.html"> <link rel="import" href="../paper-listbox/paper-listbox.html"> <link rel="import" href="../paper-item/paper-item.html"> <link rel="import" href="auth-methods-behavior.html"> <link rel="import" href="auth-methods-styles.html"> <!-- The `<auth-method-custom>` element displays a form to provide the authorization details for RAML's custom security scheme. This element works differently than other authorization panels because it sends `request-header-changed` and `query-parameter-changed` custom events directly and it doesn't care if it's wrapped with the `authorization-panel` element that will handle this. This element will also listen for this events and if the application uses other ARC elements that use this events to communicate (like `raml-request-panel`) then the value for headers or query parameters will be updated when event occur. Besides events listed above it will also fire the `auth-settings-changed` custom event as other authorization methods. This element will rended empty if `ramlSettings` property is not set or is empty. Parent element should check if `securedBy` property of RAML method contains values. ### Example ``` <auth-method-custom raml-settings="{...}"></auth-method-custom> ``` ### Styling `<auth-methods>` provides the following custom properties and mixins for styling: Custom property | Description | Default ----------------|-------------|---------- `--auth-method-custom` | Mixin applied to the element. | `{}` `--auth-method-panel` | Mixin applied to all auth elements. | `{}` `--inline-help-icon-color` | Color of the icon button to display help | `rgba(0, 0, 0, 0.24)` `--inline-help-icon-color-hover` | Color of the icon button to display help when hovered | `--accent-color` or `rgba(0, 0, 0, 0.74)` `--raml-headers-form-input-label-color` | Color of the lable of the `paper-input` element. | `rgba(0, 0, 0, 0.48)` `raml-headers-form-input-label-color-required` | Color of the lable of the `paper-input` element when it's required. | `rgba(0, 0, 0, 0.72)` Input styles are consistent with `raml-headers-form` element. @group UI Elements @element auth-method-custom @demo demo/custom.html --> <dom-module id="auth-method-custom"> <template strip-whitespace> <style include="markdown-styles"></style> <style include="auth-methods-styles"> :host { display: block; @apply --auth-method-panel; @apply --auth-method-custom; --paper-input-container-label: { color: var(--raml-headers-form-input-label-color, rgba(0, 0, 0, 0.48)); } } paper-input[required] { --paper-input-container-label: { color: var(--raml-request-parameters-editor-required-input-label-color, rgba(0, 0, 0, 0.72)); } } .input-row { display: block; } .docs { @apply --arc-font-common-base; font-size: 13px !important; font-weight: 200; line-height: 24px; color: var(--inline-documentation-color, rgba(0, 0, 0, 0.87)); } .markdown-html * { font-size: 13px !important; } .markdown-html p:first-child { margin-top: 0; padding-top: 0; } .markdown-html p:last-child { margin-bottom: 0; padding-bottom: 0; } .help-icon { color: var(--inline-help-icon-color, rgba(0, 0, 0, 0.24)); transition: color 0.2s linear; } .help-icon:hover { color: var(--inline-help-icon-color-hover, var(--accent-color, rgba(0, 0, 0, 0.74))); } .value-input { @apply --layout-horizontal; @apply --layout-center; @apply --layout-flex; } .value-input paper-input, .value-input paper-dropdown-menu { @apply --layout-flex; } </style> <form is="iron-form" id="form"> <div class="row"> <div class="stepper"> <span class="step">[[_computeStep(stepStartIndex, 1)]]</span> <span class="step-header"> <span class="step-title">Set authorization data</span> </span> </div> <div class="step-content"> <div class="line"></div> <div class="content"> <template is="dom-if" if="[[hasHeaders]]"> <section id="headersList"> <template is="dom-repeat" items="[[headers]]" headers-repeater> <div class="input-row"> <div class="value-input"> <paper-input label="[[item.inputLabel]]" value="{{item.value}}" required="[[item.required]]" pattern="[[item.pattern]]" name="[[item.name]]" auto-validate type="[[item.inputType]]" min="[[item.minimum]]" max="[[item.maximum]]" maxlength="[[item.maxLength]]" always-float-label="[[item.inputFloatLabel]]" placeholder="[[item.inputPlaceholder]]" on-input="_headerValueChanged"> </paper-input> <paper-icon-button title="Display documentation" class="help-icon" suffix icon="arc:help" hidden$="[[!item.hasDescription]]" on-tap="_openHeaderDoc"></paper-icon-button> </div> <template is="dom-if" if="[[item.hasDescription]]"> <div class="docs"> <iron-collapse> <marked-element markdown="[[item.description]]"> <div class="markdown-html markdown-body"></div> </marked-element> </iron-collapse> </div> </template> </div> </template> </section> </template> <template is="dom-if" if="[[hasQueryParameters]]"> <section id="paramsList"> <template is="dom-repeat" items="[[queryParameters]]" params-repeater> <div class="input-row"> <div class="value-input"> <template is="dom-if" if="[[item.isEnum]]"> <paper-dropdown-menu label="[[item.inputLabel]]" name="[[item.name]]" required="[[item.required]]" on-iron-select="_parameterValueChanged"> <paper-listbox class="dropdown-content" attr-for-selected="data-value" selected="{{item.value}}"> <template is="dom-repeat" items="[[item.enum]]"> <paper-item data-value$="[[item]]">[[item]]</paper-item> </template> </paper-listbox> </paper-dropdown-menu> </template> <template is="dom-if" if="[[!item.isEnum]]"> <paper-input label="[[item.inputLabel]]" value="{{item.value}}" required="[[item.required]]" pattern="[[item.pattern]]" name="[[item.name]]" auto-validate type="[[item.inputType]]" min="[[item.minimum]]" max="[[item.maximum]]" maxlength="[[item.maxLength]]" always-float-label="[[item.inputFloatLabel]]" placeholder="[[item.inputPlaceholder]]" on-input="_parameterValueChanged"> </paper-input> </template> <paper-icon-button title="Display documentation" class="help-icon" suffix icon="arc:help" hidden$="[[!item.hasDescription]]" on-tap="_openParamDoc"></paper-icon-button> </div> <template is="dom-if" if="[[item.hasDescription]]"> <div class="docs"> <iron-collapse> <marked-element markdown="[[item.description]]"> <div class="markdown-html markdown-body"></div> </marked-element> </iron-collapse> </div> </template> </div> </template> </section> </template> </div> </div> </div> </form> </template> <script> Polymer({ is: 'auth-method-custom', behaviors: [ArcBehaviors.AuthMethodsBehavior], /** * Fired when the any of the auth method settings has changed. * This event will be fired quite frequently - each time anything in the text field changed. * With one exception. This event will not be fired if the validation of the form didn't passed. * * @event auth-settings-changed * @param {Object} settings Current settings containing hash, password * and username. * @param {String} type The authorization type - basic * @param {Boolean} valid True if the form has been validated. * @param {String} name Name of the custom method to differeciante them if many. */ /** * Fired when the header value has changed. * * @event request-header-changed * @param {String} name Name of the header * @param {String} value Value of the header */ /** * Fired when the header value has changed. * * @event query-parameter-changed * @param {String} name Name of the parameter * @param {String} value Value of the parameter */ properties: { /** * RAML `securedBy` obejct definition. */ ramlSettings: Object, /** * List of headers to render in the form. * Note, this value will be computed from the `ramlSettings` object after any change to it. */ headers: { type: Array, computed: '_computeHeaders(ramlSettings.describedBy.*)' }, /** * List of query parameters to render. * Note, this value will be computed from the `ramlSettings` object after any change to it. */ queryParameters: { type: Array, computed: '_computeParameters(ramlSettings.describedBy.queryParameters.*)' }, // Computed value, true if headers are defined in RAML settings. hasHeaders: { type: Boolean, computed: '_computeHasHeaders(ramlSettings.describedBy.*)' }, // Computed value, true if query parameters are defined in RAML settings. hasQueryParameters: { type: Boolean, computed: '_computeHasQueryParameters(ramlSettings.describedBy.*)' } }, _attachListeners: function(node) { this.listen(node, 'request-header-changed', '_headerChangedHandler'); this.listen(node, 'query-parameter-changed', '_parameterChangedHandler'); }, _detachListeners: function(node) { this.unlisten(node, 'request-header-changed', '_headerChangedHandler'); this.unlisten(node, 'query-parameter-changed', '_parameterChangedHandler'); }, /** * Gets a map of current settings (user provided data). */ get settings() { return this._getSettings(); }, /** * Validate the form. * * @param {Boolean} passive If true then it will check validity of the form but will not * highlight the fields when invalid. Default to false. * @return {Boolean} `true` if valid, `false` otherwise. */ validate: function(passive) { return passive ? this.$.form.checkValidity() : this.$.form.validate(); }, // Computes a list of query parameters to display _computeParameters: function(record) { var array = record && record.base; if (!array || !array.length) { return; } return this._computeFormData(array); }, // Computes a list of headers to display _computeHeaders: function(record) { var describedBy = record && record.base; if (!(describedBy && describedBy.headers && describedBy.headers.length)) { return; } return this._computeFormData(describedBy.headers); }, // Computes form items data based on headers / query parameters input. _computeFormData: function(array) { var numTypes = ['number', 'integer', 'float']; return array.map(function(item) { item = Object.assign({}, item); item.isEnum = !!(item.enum && item.enum.length); item.hasDescription = !!item.description; item.inputLabel = item.displayName || item.name || 'Value'; if (item.required) { item.inputLabel += '*'; } if (numTypes.indexOf(item.type) !== -1) { item.inputType = 'number'; } else { item.inputType = 'text'; } if (item.examples && item.examples.length && item.examples[0]) { item.inputPlaceholder = 'Example: ' + item.examples[0]; } else if (item.example && typeof item.example === 'string') { item.inputPlaceholder = 'Example: ' + item.example; } if (item.inputPlaceholder) { item.inputFloatLabel = true; } if (item.required && typeof item.default !== 'undefined' && !item.value) { item.value = item.default; } if (typeof item.value === 'undefined' && item.required) { if (item.examples) { item.value = item.examples[0]; } else if (item.example) { item.value = item.example; } if (item.value && item.value.indexOf && item.value.indexOf(item.name + '=') === 0) { item.value = item.value.substr(item.name.length + 1); } if (typeof item.value === 'undefined' && item.isEnum) { item.value = item.enum[0]; } } if (item.value && typeof item.value === 'string') { item.value = decodeURIComponent(item.value.replace(/\+/g, ' ')); } return item; }); }, _computeHasHeaders: function(record) { var describedBy = record && record.base; return !!(describedBy && describedBy.headers && describedBy.headers.length); }, _computeHasQueryParameters: function(record) { var describedBy = record && record.base; return !!(describedBy && describedBy.queryParameters && describedBy.queryParameters.length); }, _getSettings: function() { return this.$.form.serialize(); }, /** * Restores settings from stored value. * For custom methods this is dummy function. */ restore: function() {}, notifySettingsChanged: function() { var validationResult = this.$.form.validate(); var settings = this._getSettings(); var detail = { settings: settings, type: 'x-custom', name: this.ramlSettings.name, valid: validationResult }; this.fire('auth-settings-changed', detail); }, /** * When header value changes then it will file the `request-header-changed` to inform other * observers about this event. */ _headerValueChanged: function(e) { var item = e.model.get('item'); var name = item.name || item.key; var value = item.value; this.fire('request-header-changed', { name: name, value: value }); this.notifySettingsChanged(); }, // Opens the documentation for headers item. _openHeaderDoc: function(e) { var model = this._getItemModel('headers', e); this._toggleDoc('headers', model); }, // Opens the documentation for query parameters item. _openParamDoc: function(e) { var model = this._getItemModel('params', e); this._toggleDoc('params', model); }, /** * Returns model for event fired by a repeater defined as the `source` */ _getItemModel: function(source, e) { var template = this.$.form.querySelector('[' + source + '-repeater]'); return template.modelForElement(e.target); }, _toggleDoc: function(type, model) { var i = model.index + 1; var collapse = this.$$('#' + type + 'List') .querySelector('.input-row:nth-child(' + i + ') iron-collapse'); if (!collapse) { return; } collapse.opened = !collapse.opened; }, /** * When query parameter value changes then it will file the `query-parameter-changed` custom * event to inform other observers about this event. */ _parameterValueChanged: function(e) { var item = e.model.get('item'); var name = item.name || item.key; var value = item.value; this.fire('query-parameter-changed', { name: name, value: value }); this.notifySettingsChanged(); }, /** * Handler for the `request-header-changed` event. * It updates value for a single header if this header is already on the list. */ _headerChangedHandler: function(e) { this._updateEventValue('headers', e); }, /** * Handler for the `query-parameter-changed` event. * It updates value for a single parameter if this parameter is already on the list. */ _parameterChangedHandler: function(e) { this._updateEventValue('queryParameters', e); }, /** * Update array value for given type (`headers` or `queryParameters`) for given event. */ _updateEventValue: function(target, e) { if (e.target === this || !this._isOpened || e.defaultPrevented) { return; } var name = e.detail.name; if (!name) { return; } // Headers are case insensitive. name = target === 'headers' ? name.toLowerCase() : name; var parameters = this[target]; if (!parameters || !parameters.length) { return; } for (var i = 0, len = parameters.length; i < len; i++) { var paramName = parameters[i].name || parameters[i].key; if (!paramName) { continue; } paramName = target === 'headers' ? paramName.toLowerCase() : paramName; if (paramName === name) { this.set([target, i, 'value'], e.detail.value); return; } } } }); </script> </dom-module>