Remove Ivy Codemirror (#14659)
* setup * fix mode issue * actions * readonly styling * remove ivycodemirror from package json * wip * test coverage for json editor * text fixes * fix tests * fix cursor issue * changelog * clean up * fix * address pr comments * unused css and it overides other styling. * fix * fix comment
This commit is contained in:
parent
33888c3340
commit
0455d31b84
|
@ -0,0 +1,3 @@
|
||||||
|
```release-note:improvement
|
||||||
|
ui: Replaces the IvyCodemirror wrapper with a custom ember modifier.
|
||||||
|
```
|
|
@ -14,50 +14,24 @@ import { action } from '@ember/object';
|
||||||
* @param {Function} [valueUpdated] - action to preform when you edit the codemirror value.
|
* @param {Function} [valueUpdated] - action to preform when you edit the codemirror value.
|
||||||
* @param {Function} [onFocusOut] - action to preform when you focus out of codemirror.
|
* @param {Function} [onFocusOut] - action to preform when you focus out of codemirror.
|
||||||
* @param {string} [helpText] - helper text.
|
* @param {string} [helpText] - helper text.
|
||||||
* @param {object} [options] - option object that overrides codemirror default options such as the styling.
|
* @param {Object} [extraKeys] - Provides keyboard shortcut methods for things like saving on shift + enter.
|
||||||
|
* @param {Array} [gutters] - An array of CSS class names or class name / CSS string pairs, each of which defines a width (and optionally a background), and which will be used to draw the background of the gutters.
|
||||||
|
* @param {string} [mode] - The mode defined for styling. Right now we only import ruby so mode must but be ruby or defaults to javascript. If you wanted another language you need to import it into the modifier.
|
||||||
|
* @param {Boolean} [readOnly] - Sets the view to readOnly, allowing for copying but no editing. It also hides the cursor. Defaults to false.
|
||||||
|
* @param {String} [theme] - Specify or customize the look via a named "theme" class in scss.
|
||||||
|
* @param {String} [value] - Value within the display. Generally, a json string.
|
||||||
|
* @param {String} [viewportMargin] - Size of viewport. Often set to "Infinity" to load/show all text regardless of length.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const JSON_EDITOR_DEFAULTS = {
|
|
||||||
// IMPORTANT: `gutters` must come before `lint` since the presence of
|
|
||||||
// `gutters` is cached internally when `lint` is toggled
|
|
||||||
gutters: ['CodeMirror-lint-markers'],
|
|
||||||
tabSize: 2,
|
|
||||||
mode: 'application/json',
|
|
||||||
lineNumbers: true,
|
|
||||||
lint: { lintOnChange: false },
|
|
||||||
theme: 'hashi',
|
|
||||||
readOnly: false,
|
|
||||||
showCursorWhenSelecting: true,
|
|
||||||
};
|
|
||||||
|
|
||||||
export default class JsonEditorComponent extends Component {
|
export default class JsonEditorComponent extends Component {
|
||||||
value = null;
|
|
||||||
valueUpdated = null;
|
|
||||||
onFocusOut = null;
|
|
||||||
readOnly = false;
|
|
||||||
options = null;
|
|
||||||
|
|
||||||
constructor() {
|
|
||||||
super(...arguments);
|
|
||||||
this.options = { ...JSON_EDITOR_DEFAULTS, ...this.args.options };
|
|
||||||
if (this.options.autoHeight) {
|
|
||||||
this.options.viewportMargin = Infinity;
|
|
||||||
delete this.options.autoHeight;
|
|
||||||
}
|
|
||||||
if (this.options.readOnly) {
|
|
||||||
this.options.readOnly = 'nocursor';
|
|
||||||
this.options.lineNumbers = false;
|
|
||||||
delete this.options.gutters;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
get getShowToolbar() {
|
get getShowToolbar() {
|
||||||
return this.args.showToolbar === false ? false : true;
|
return this.args.showToolbar === false ? false : true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@action
|
@action
|
||||||
updateValue(...args) {
|
onUpdate(...args) {
|
||||||
if (this.args.valueUpdated) {
|
if (!this.args.readOnly) {
|
||||||
|
// catching a situation in which the user is not readOnly and has not provided a valueUpdated function to the instance
|
||||||
this.args.valueUpdated(...args);
|
this.args.valueUpdated(...args);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,68 @@
|
||||||
|
import { action } from '@ember/object';
|
||||||
|
import { bind } from '@ember/runloop';
|
||||||
|
import codemirror from 'codemirror';
|
||||||
|
import Modifier from 'ember-modifier';
|
||||||
|
|
||||||
|
import 'codemirror/addon/edit/matchbrackets';
|
||||||
|
import 'codemirror/addon/selection/active-line';
|
||||||
|
import 'codemirror/addon/lint/lint.js';
|
||||||
|
import 'codemirror/addon/lint/json-lint.js';
|
||||||
|
// right now we only use the ruby and javascript, if you use another mode you'll need to import it.
|
||||||
|
// https://codemirror.net/mode/
|
||||||
|
import 'codemirror/mode/ruby/ruby';
|
||||||
|
import 'codemirror/mode/javascript/javascript';
|
||||||
|
|
||||||
|
export default class CodeMirrorModifier extends Modifier {
|
||||||
|
didInstall() {
|
||||||
|
this._setup();
|
||||||
|
}
|
||||||
|
|
||||||
|
didUpdateArguments() {
|
||||||
|
this._editor.setOption('readOnly', this.args.named.readOnly);
|
||||||
|
if (!this.args.named.content) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (this._editor.getValue() !== this.args.named.content) {
|
||||||
|
this._editor.setValue(this.args.named.content);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@action
|
||||||
|
_onChange(editor) {
|
||||||
|
this.args.named.onUpdate(editor.getValue(), this._editor);
|
||||||
|
}
|
||||||
|
|
||||||
|
@action
|
||||||
|
_onFocus(editor) {
|
||||||
|
this.args.named.onFocus(editor.getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
_setup() {
|
||||||
|
if (!this.element) {
|
||||||
|
throw new Error('CodeMirror modifier has no element');
|
||||||
|
}
|
||||||
|
const editor = codemirror(this.element, {
|
||||||
|
// IMPORTANT: `gutters` must come before `lint` since the presence of
|
||||||
|
// `gutters` is cached internally when `lint` is toggled
|
||||||
|
gutters: this.args.named.gutters || ['CodeMirror-lint-markers'],
|
||||||
|
matchBrackets: true,
|
||||||
|
lint: { lintOnChange: true },
|
||||||
|
showCursorWhenSelecting: true,
|
||||||
|
styleActiveLine: true,
|
||||||
|
tabSize: 2,
|
||||||
|
// all values we can pass into the JsonEditor
|
||||||
|
extraKeys: this.args.named.extraKeys || '',
|
||||||
|
lineNumbers: this.args.named.lineNumbers,
|
||||||
|
mode: this.args.named.mode || 'application/json',
|
||||||
|
readOnly: this.args.named.readOnly || false,
|
||||||
|
theme: this.args.named.theme || 'hashi',
|
||||||
|
value: this.args.named.content || '',
|
||||||
|
viewportMargin: this.args.named.viewportMargin || '',
|
||||||
|
});
|
||||||
|
|
||||||
|
editor.on('change', bind(this, this._onChange));
|
||||||
|
editor.on('focus', bind(this, this._onFocus));
|
||||||
|
|
||||||
|
this._editor = editor;
|
||||||
|
}
|
||||||
|
}
|
|
@ -168,34 +168,14 @@ $gutter-grey: #2a2f36;
|
||||||
}
|
}
|
||||||
|
|
||||||
.readonly-codemirror {
|
.readonly-codemirror {
|
||||||
.CodeMirror-cursors {
|
.CodeMirror-code {
|
||||||
|
cursor: default;
|
||||||
|
}
|
||||||
|
.CodeMirror-cursor {
|
||||||
|
// https://github.com/codemirror/CodeMirror/issues/1099
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.cm-s-hashi {
|
|
||||||
span {
|
|
||||||
color: $light-grey;
|
|
||||||
}
|
|
||||||
|
|
||||||
span.cm-string,
|
|
||||||
span.cm-string-2 {
|
|
||||||
color: $faded-gray;
|
|
||||||
}
|
|
||||||
|
|
||||||
span.cm-number {
|
|
||||||
color: lighten($dark-gray, 30%);
|
|
||||||
}
|
|
||||||
|
|
||||||
span.cm-property {
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
span.cm-variable-2 {
|
|
||||||
color: $light-grey-blue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.cm-s-auto-height.CodeMirror {
|
.cm-s-auto-height.CodeMirror {
|
||||||
height: auto;
|
height: auto;
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,10 @@
|
||||||
<JsonEditor
|
<JsonEditor
|
||||||
@showToolbar={{false}}
|
@showToolbar={{false}}
|
||||||
@value={{stringify this.content}}
|
@value={{stringify this.content}}
|
||||||
@options={{hash readOnly=true lineNumbers=false autoHeight=true gutters=false theme="hashi auto-height"}}
|
@readOnly={{true}}
|
||||||
|
@viewportMargin="Infinity"
|
||||||
|
@gutters={{false}}
|
||||||
|
@theme="hashi auto-height"
|
||||||
/>
|
/>
|
||||||
<HoverCopyButton @copyValue={{stringify this.content}} />
|
<HoverCopyButton @copyValue={{stringify this.content}} />
|
||||||
</div>
|
</div>
|
|
@ -17,7 +17,10 @@
|
||||||
data-test-json-viewer
|
data-test-json-viewer
|
||||||
@showToolbar={{false}}
|
@showToolbar={{false}}
|
||||||
@value={{stringify this.unwrapData}}
|
@value={{stringify this.unwrapData}}
|
||||||
@options={{hash readOnly=true lineNumbers=false autoHeight=true gutters=false theme="hashi-read-only auto-height"}}
|
@readOnly={{true}}
|
||||||
|
@viewportMargin="Infinity"
|
||||||
|
@gutters={{false}}
|
||||||
|
@theme="hashi-read-only auto-height"
|
||||||
/>
|
/>
|
||||||
<HoverCopyButton @copyValue={{stringify this.unwrapData}} />
|
<HoverCopyButton @copyValue={{stringify this.unwrapData}} />
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -23,14 +23,22 @@
|
||||||
</Toolbar>
|
</Toolbar>
|
||||||
</div>
|
</div>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
<div
|
||||||
<IvyCodemirror
|
{{code-mirror
|
||||||
@data-test-component="json-editor"
|
content=@value
|
||||||
@value={{@value}}
|
extraKeys=@extraKeys
|
||||||
@options={{this.options}}
|
gutters=@gutters
|
||||||
@valueUpdated={{action "updateValue"}}
|
lineNumbers=(if @readOnly false true)
|
||||||
@onFocusOut={{action "onFocus"}}
|
mode=@mode
|
||||||
/>
|
readOnly=@readOnly
|
||||||
|
theme=@theme
|
||||||
|
viewportMarg=@viewportMargin
|
||||||
|
onUpdate=this.onUpdate
|
||||||
|
onFocus=this.onFocus
|
||||||
|
}}
|
||||||
|
class={{if @readOnly "readonly-codemirror"}}
|
||||||
|
data-test-component="code-mirror-modifier"
|
||||||
|
></div>
|
||||||
|
|
||||||
{{#if @helpText}}
|
{{#if @helpText}}
|
||||||
<div class="box is-shadowless is-fullwidth has-short-padding">
|
<div class="box is-shadowless is-fullwidth has-short-padding">
|
||||||
|
|
|
@ -33,7 +33,7 @@
|
||||||
<JsonEditor
|
<JsonEditor
|
||||||
@title={{if @isV2 "Version Data" "Secret Data"}}
|
@title={{if @isV2 "Version Data" "Secret Data"}}
|
||||||
@value={{@modelForData.dataAsJSONString}}
|
@value={{@modelForData.dataAsJSONString}}
|
||||||
@options={{hash readOnly=true}}
|
@readOnly={{true}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
{{else}}
|
{{else}}
|
||||||
|
|
|
@ -45,7 +45,7 @@
|
||||||
{{#if (eq @unwrapActiveTab "data")}}
|
{{#if (eq @unwrapActiveTab "data")}}
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<div class="control">
|
<div class="control">
|
||||||
<JsonEditor @title="Unwrapped Data" @value={{stringify @unwrap_data}} @options={{hash readOnly=true}} />
|
<JsonEditor @title="Unwrapped Data" @value={{stringify @unwrap_data}} @readOnly={{true}} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{{else}}
|
{{else}}
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
<JsonEditor
|
<JsonEditor
|
||||||
@title="Ciphertext"
|
@title="Ciphertext"
|
||||||
@valueUpdated={{action (mut @ciphertext)}}
|
@valueUpdated={{action (mut @ciphertext)}}
|
||||||
@options={{hash mode="ruby"}}
|
@mode="ruby"
|
||||||
@data-test-transit-input="ciphertext"
|
@data-test-transit-input="ciphertext"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
@title="Plaintext"
|
@title="Plaintext"
|
||||||
@value={{@plaintext}}
|
@value={{@plaintext}}
|
||||||
@valueUpdated={{action (mut @plaintext)}}
|
@valueUpdated={{action (mut @plaintext)}}
|
||||||
@options={{hash mode="ruby"}}
|
@mode="ruby"
|
||||||
@data-test-transit-input="plaintext"
|
@data-test-transit-input="plaintext"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -11,12 +11,7 @@
|
||||||
<KeyVersionSelect @key={{@key}} @onVersionChange={{action (mut @key_version)}} @key_version={{@key_version}} />
|
<KeyVersionSelect @key={{@key}} @onVersionChange={{action (mut @key_version)}} @key_version={{@key_version}} />
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<div id="input-control" class="control is-relative">
|
<div id="input-control" class="control is-relative">
|
||||||
<JsonEditor
|
<JsonEditor @title="Input" @valueUpdated={{action (mut @input)}} @mode="ruby" @data-test-transit-input="input" />
|
||||||
@title="Input"
|
|
||||||
@valueUpdated={{action (mut @input)}}
|
|
||||||
@options={{hash mode="ruby"}}
|
|
||||||
@data-test-transit-input="input"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="field">
|
<div class="field">
|
||||||
|
|
|
@ -11,7 +11,7 @@
|
||||||
<KeyVersionSelect @key={{@key}} @onVersionChange={{action (mut @key_version)}} @key_version={{@key_version}} />
|
<KeyVersionSelect @key={{@key}} @onVersionChange={{action (mut @key_version)}} @key_version={{@key_version}} />
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<div class="control is-expanded">
|
<div class="control is-expanded">
|
||||||
<JsonEditor @title="Ciphertext" @valueUpdated={{action (mut @ciphertext)}} @options={{hash mode="ruby"}} />
|
<JsonEditor @title="Ciphertext" @valueUpdated={{action (mut @ciphertext)}} @mode="ruby" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{{#if @key.derived}}
|
{{#if @key.derived}}
|
||||||
|
|
|
@ -28,7 +28,7 @@
|
||||||
@title="Input"
|
@title="Input"
|
||||||
@value={{@input}}
|
@value={{@input}}
|
||||||
@valueUpdated={{action (mut @input)}}
|
@valueUpdated={{action (mut @input)}}
|
||||||
@options={{hash mode="ruby"}}
|
@mode="ruby"
|
||||||
@data-test-transit-input="input"
|
@data-test-transit-input="input"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -23,7 +23,7 @@
|
||||||
@title="Input"
|
@title="Input"
|
||||||
@value={{@input}}
|
@value={{@input}}
|
||||||
@valueUpdated={{action (mut @input)}}
|
@valueUpdated={{action (mut @input)}}
|
||||||
@options={{hash mode="ruby"}}
|
@mode="ruby"
|
||||||
@data-test-transit-input="input"
|
@data-test-transit-input="input"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
@ -140,12 +140,7 @@
|
||||||
{{#if (or (and @verification (eq @verification "HMAC")) @hmac)}}
|
{{#if (or (and @verification (eq @verification "HMAC")) @hmac)}}
|
||||||
<div class="field is-flex-column is-flex-1">
|
<div class="field is-flex-column is-flex-1">
|
||||||
<div class="control is-flex-column is-flex-1">
|
<div class="control is-flex-column is-flex-1">
|
||||||
<JsonEditor
|
<JsonEditor @title="HMAC" @value={{@hmac}} @valueUpdated={{action (mut @hmac)}} @mode="ruby" />
|
||||||
@title="HMAC"
|
|
||||||
@value={{@hmac}}
|
|
||||||
@valueUpdated={{action (mut @hmac)}}
|
|
||||||
@options={{hash mode="ruby"}}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{{else}}
|
{{else}}
|
||||||
|
@ -155,7 +150,7 @@
|
||||||
@title="Signature"
|
@title="Signature"
|
||||||
@value={{@signature}}
|
@value={{@signature}}
|
||||||
@valueUpdated={{action (mut @signature)}}
|
@valueUpdated={{action (mut @signature)}}
|
||||||
@options={{hash mode="ruby"}}
|
@mode="ruby"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -165,7 +160,7 @@
|
||||||
{{else}}
|
{{else}}
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<div class="control">
|
<div class="control">
|
||||||
<JsonEditor @title="HMAC" @value={{@hmac}} @valueUpdated={{action (mut @hmac)}} @options={{hash mode="ruby"}} />
|
<JsonEditor @title="HMAC" @value={{@hmac}} @valueUpdated={{action (mut @hmac)}} @mode="ruby" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="field">
|
<div class="field">
|
||||||
|
|
|
@ -62,7 +62,8 @@
|
||||||
@showToolbar={{false}}
|
@showToolbar={{false}}
|
||||||
@value={{this.model.policy}}
|
@value={{this.model.policy}}
|
||||||
@valueUpdated={{action (mut this.model.policy)}}
|
@valueUpdated={{action (mut this.model.policy)}}
|
||||||
@options={{hash mode="ruby" extraKeys=(hash Shift-Enter=(action "savePolicy" this.model))}}
|
@mode="ruby"
|
||||||
|
@extraKeys={{hash Shift-Enter=(action "savePolicy" this.model)}}
|
||||||
/>
|
/>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -50,7 +50,8 @@
|
||||||
@title="Policy"
|
@title="Policy"
|
||||||
@value={{this.model.policy}}
|
@value={{this.model.policy}}
|
||||||
@valueUpdated={{action (mut this.model.policy)}}
|
@valueUpdated={{action (mut this.model.policy)}}
|
||||||
@options={{hash mode="ruby" extraKeys=(hash Shift-Enter=(action "savePolicy" this.model))}}
|
@mode="ruby"
|
||||||
|
@extraKeys={{hash Shift-Enter=(action "savePolicy" this.model)}}
|
||||||
/>
|
/>
|
||||||
<div class="box is-shadowless is-fullwidth has-short-padding">
|
<div class="box is-shadowless is-fullwidth has-short-padding">
|
||||||
<p class="help-text has-text-grey-dark is-size-7">
|
<p class="help-text has-text-grey-dark is-size-7">
|
||||||
|
|
|
@ -42,7 +42,8 @@
|
||||||
@title="Policy"
|
@title="Policy"
|
||||||
@subTitle={{if (eq this.policyType "acl") (concat this.uppercase this.model.format " format")}}
|
@subTitle={{if (eq this.policyType "acl") (concat this.uppercase this.model.format " format")}}
|
||||||
@value={{this.model.policy}}
|
@value={{this.model.policy}}
|
||||||
@options={{hash readOnly=true mode="ruby"}}
|
@readOnly={{true}}
|
||||||
|
@mode="ruby"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
{{#if this.model.paths}}
|
{{#if this.model.paths}}
|
||||||
|
|
|
@ -26,10 +26,6 @@ const appConfig = {
|
||||||
return `${config.rootURL.replace(/\/$/, '')}${filePath}`;
|
return `${config.rootURL.replace(/\/$/, '')}${filePath}`;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
codemirror: {
|
|
||||||
modes: ['javascript', 'ruby'],
|
|
||||||
keyMaps: ['sublime'],
|
|
||||||
},
|
|
||||||
babel: {
|
babel: {
|
||||||
plugins: ['@babel/plugin-proposal-object-rest-spread', ['inline-json-import', {}]],
|
plugins: ['@babel/plugin-proposal-object-rest-spread', ['inline-json-import', {}]],
|
||||||
},
|
},
|
||||||
|
@ -74,8 +70,7 @@ module.exports = function (defaults) {
|
||||||
|
|
||||||
app.import('node_modules/jsonlint/lib/jsonlint.js');
|
app.import('node_modules/jsonlint/lib/jsonlint.js');
|
||||||
app.import('node_modules/codemirror/addon/lint/lint.css');
|
app.import('node_modules/codemirror/addon/lint/lint.css');
|
||||||
app.import('node_modules/codemirror/addon/lint/lint.js');
|
app.import('node_modules/codemirror/lib/codemirror.css');
|
||||||
app.import('node_modules/codemirror/addon/lint/json-lint.js');
|
|
||||||
app.import('node_modules/text-encoder-lite/text-encoder-lite.js');
|
app.import('node_modules/text-encoder-lite/text-encoder-lite.js');
|
||||||
app.import('node_modules/jsondiffpatch/dist/jsondiffpatch.umd.js');
|
app.import('node_modules/jsondiffpatch/dist/jsondiffpatch.umd.js');
|
||||||
app.import('node_modules/jsondiffpatch/dist/formatters-styles/html.css');
|
app.import('node_modules/jsondiffpatch/dist/formatters-styles/html.css');
|
||||||
|
|
|
@ -233,7 +233,7 @@
|
||||||
this.attr.options.defaultValue
|
this.attr.options.defaultValue
|
||||||
}}
|
}}
|
||||||
@valueUpdated={{action "codemirrorUpdated" this.attr.name "string"}}
|
@valueUpdated={{action "codemirrorUpdated" this.attr.name "string"}}
|
||||||
@options={{hash theme=(or this.attr.options.theme "hashi")}}
|
@theme={{or this.attr.options.theme "hashi"}}
|
||||||
@helpText={{this.attr.options.helpText}}
|
@helpText={{this.attr.options.helpText}}
|
||||||
>
|
>
|
||||||
{{#if this.attr.options.allowReset}}
|
{{#if this.attr.options.allowReset}}
|
||||||
|
|
|
@ -129,6 +129,7 @@
|
||||||
"ember-maybe-import-regenerator": "^0.1.6",
|
"ember-maybe-import-regenerator": "^0.1.6",
|
||||||
"ember-maybe-in-element": "^2.0.3",
|
"ember-maybe-in-element": "^2.0.3",
|
||||||
"ember-modal-dialog": "^3.0.3",
|
"ember-modal-dialog": "^3.0.3",
|
||||||
|
"ember-modifier": "^3.1.0",
|
||||||
"ember-page-title": "^6.0.3",
|
"ember-page-title": "^6.0.3",
|
||||||
"ember-power-select": "^5.0.3",
|
"ember-power-select": "^5.0.3",
|
||||||
"ember-promise-helpers": "^1.0.9",
|
"ember-promise-helpers": "^1.0.9",
|
||||||
|
|
|
@ -374,13 +374,15 @@ module('Acceptance | secrets/secret/create', function (hooks) {
|
||||||
await listPage.visitRoot({ backend: 'secret' });
|
await listPage.visitRoot({ backend: 'secret' });
|
||||||
await listPage.create();
|
await listPage.create();
|
||||||
await editPage.path(secretPath).toggleJSON();
|
await editPage.path(secretPath).toggleJSON();
|
||||||
await editPage.editor.fillIn(this, content);
|
let instance = document.querySelector('.CodeMirror').CodeMirror;
|
||||||
|
instance.setValue(content);
|
||||||
await editPage.save();
|
await editPage.save();
|
||||||
|
|
||||||
assert.equal(currentRouteName(), 'vault.cluster.secrets.backend.show', 'redirects to the show page');
|
assert.equal(currentRouteName(), 'vault.cluster.secrets.backend.show', 'redirects to the show page');
|
||||||
assert.ok(showPage.editIsPresent, 'shows the edit button');
|
assert.ok(showPage.editIsPresent, 'shows the edit button');
|
||||||
|
let savedInstance = document.querySelector('.CodeMirror').CodeMirror;
|
||||||
assert.equal(
|
assert.equal(
|
||||||
showPage.editor.content(this),
|
savedInstance.options.value,
|
||||||
JSON.stringify({ bar: 'boo', foo: 'fa' }, null, 2),
|
JSON.stringify({ bar: 'boo', foo: 'fa' }, null, 2),
|
||||||
'saves the content'
|
'saves the content'
|
||||||
);
|
);
|
||||||
|
|
|
@ -19,8 +19,7 @@ module('Integration | Component | console/log json', function (hooks) {
|
||||||
this.set('content', objectContent);
|
this.set('content', objectContent);
|
||||||
|
|
||||||
await render(hbs`{{console/log-json content=content}}`);
|
await render(hbs`{{console/log-json content=content}}`);
|
||||||
const instance = this.codeMirror.instanceFor(find('[data-test-component=json-editor]').id);
|
const instance = find('[data-test-component=code-mirror-modifier]').innerText;
|
||||||
|
assert.equal(instance, expectedText);
|
||||||
assert.equal(instance.getValue(), expectedText);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,44 +1,76 @@
|
||||||
import { module, test } from 'qunit';
|
import { module, test } from 'qunit';
|
||||||
import { setupRenderingTest } from 'ember-qunit';
|
import { setupRenderingTest } from 'ember-qunit';
|
||||||
import { create } from 'ember-cli-page-object';
|
import { create } from 'ember-cli-page-object';
|
||||||
import { render } from '@ember/test-helpers';
|
import { render, fillIn, find, waitUntil } from '@ember/test-helpers';
|
||||||
import hbs from 'htmlbars-inline-precompile';
|
import hbs from 'htmlbars-inline-precompile';
|
||||||
import jsonEditor from '../../pages/components/json-editor';
|
import jsonEditor from '../../pages/components/json-editor';
|
||||||
|
import sinon from 'sinon';
|
||||||
|
|
||||||
const component = create(jsonEditor);
|
const component = create(jsonEditor);
|
||||||
|
|
||||||
module('Integration | Component | json-editor', function (hooks) {
|
module('Integration | Component | json-editor', function (hooks) {
|
||||||
setupRenderingTest(hooks);
|
setupRenderingTest(hooks);
|
||||||
|
|
||||||
const setup = async function (context, title, value, options, showToolbar = true) {
|
const JSON_BLOB = `{
|
||||||
context.set('value', JSON.stringify(value));
|
"test": "test"
|
||||||
context.set('options', options);
|
}`;
|
||||||
context.set('title', title);
|
const BAD_JSON_BLOB = `{
|
||||||
context.set('showToolbar', showToolbar);
|
"test": test
|
||||||
await render(hbs`{{json-editor title=title value=value options=options showToolbar=showToolbar}}`);
|
}`;
|
||||||
};
|
|
||||||
|
hooks.beforeEach(function () {
|
||||||
|
this.set('valueUpdated', sinon.spy());
|
||||||
|
this.set('onFocusOut', sinon.spy());
|
||||||
|
this.set('json_blob', JSON_BLOB);
|
||||||
|
this.set('bad_json_blob', BAD_JSON_BLOB);
|
||||||
|
this.set('hashi-read-only-theme', 'hashi-read-only auto-height');
|
||||||
|
});
|
||||||
|
|
||||||
test('it renders', async function (assert) {
|
test('it renders', async function (assert) {
|
||||||
let value = '';
|
await render(hbs`<JsonEditor
|
||||||
await setup(this, 'Test title', value, null);
|
@value={{"{}"}}
|
||||||
|
@title={{"Test title"}}
|
||||||
|
@showToolbar={{true}}
|
||||||
|
@readOnly={{true}}
|
||||||
|
/>`);
|
||||||
|
|
||||||
assert.equal(component.title, 'Test title', 'renders the provided title');
|
assert.equal(component.title, 'Test title', 'renders the provided title');
|
||||||
assert.equal(component.hasToolbar, true, 'renders the toolbar');
|
assert.equal(component.hasToolbar, true, 'renders the toolbar');
|
||||||
assert.equal(component.hasJSONEditor, true, 'renders the ivy code mirror component');
|
assert.equal(component.hasJSONEditor, true, 'renders the code mirror modifier');
|
||||||
assert.equal(component.canEdit, true, 'json editor is in read only mode');
|
assert.ok(component.canEdit, 'json editor can be edited');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('it renders in read only mode', async function (assert) {
|
test('it handles editing and linting and styles to json', async function (assert) {
|
||||||
let value = '';
|
await render(hbs`<JsonEditor
|
||||||
let options = {
|
@value={{this.json_blob}}
|
||||||
readOnly: true,
|
@readOnly={{false}}
|
||||||
};
|
@valueUpdated={{this.valueUpdated}}
|
||||||
await setup(this, 'Test title', value, options);
|
@onFocusOut={{this.onFocusOut}}
|
||||||
assert.equal(component.canEdit, false, 'editor is in read only mode');
|
/>`);
|
||||||
|
// check for json styling
|
||||||
|
assert.dom('.cm-property').hasStyle({
|
||||||
|
color: 'rgb(158, 132, 197)',
|
||||||
|
});
|
||||||
|
assert.dom('.cm-string:nth-child(2)').hasStyle({
|
||||||
|
color: 'rgb(29, 219, 163)',
|
||||||
});
|
});
|
||||||
|
|
||||||
test('it renders the editor without toolbar', async function (assert) {
|
await fillIn('textarea', this.bad_json_blob);
|
||||||
let value = '';
|
await waitUntil(() => find('.CodeMirror-lint-marker-error'));
|
||||||
await setup(this, 'Test title', value, null, false);
|
assert.dom('.CodeMirror-lint-marker-error').exists('throws linting error');
|
||||||
assert.equal(component.hasToolbar, false, 'toolbar is not rendered');
|
assert.dom('.CodeMirror-linenumber').exists('shows line numbers');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('it renders the correct theme and expected styling', async function (assert) {
|
||||||
|
await render(hbs`<JsonEditor
|
||||||
|
@value={{this.json_blob}}
|
||||||
|
@theme={{this.hashi-read-only-theme}}
|
||||||
|
@readOnly={{true}}
|
||||||
|
/>`);
|
||||||
|
|
||||||
|
assert.dom('.cm-s-hashi-read-only').hasStyle({
|
||||||
|
background: 'rgb(247, 248, 250) none repeat scroll 0% 0% / auto padding-box border-box',
|
||||||
|
});
|
||||||
|
assert.dom('.CodeMirror-linenumber').doesNotExist('on readOnly does not show line numbers');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { module, test } from 'qunit';
|
import { module, test } from 'qunit';
|
||||||
import { setupRenderingTest } from 'ember-qunit';
|
import { setupRenderingTest } from 'ember-qunit';
|
||||||
import { render, find, settled } from '@ember/test-helpers';
|
import { render, settled } from '@ember/test-helpers';
|
||||||
import { resolve } from 'rsvp';
|
import { resolve } from 'rsvp';
|
||||||
import { run } from '@ember/runloop';
|
import { run } from '@ember/runloop';
|
||||||
import Service from '@ember/service';
|
import Service from '@ember/service';
|
||||||
|
@ -55,15 +55,12 @@ module('Integration | Component | secret edit', function (hooks) {
|
||||||
test('it shows an error when creating and data is not an object', async function (assert) {
|
test('it shows an error when creating and data is not an object', async function (assert) {
|
||||||
this.set('mode', 'create');
|
this.set('mode', 'create');
|
||||||
this.set('model', {
|
this.set('model', {
|
||||||
secretData: {
|
secretData: null,
|
||||||
int: '2',
|
|
||||||
null: 'null',
|
|
||||||
float: '1.234',
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
||||||
await render(hbs`{{secret-edit mode=mode model=model preferAdvancedEdit=true }}`);
|
await render(hbs`{{secret-edit mode=mode model=model preferAdvancedEdit=true }}`);
|
||||||
let instance = this.codeMirror.instanceFor(find('[data-test-component=json-editor]').id);
|
|
||||||
|
let instance = document.querySelector('.CodeMirror').CodeMirror;
|
||||||
instance.setValue(JSON.stringify([{ foo: 'bar' }]));
|
instance.setValue(JSON.stringify([{ foo: 'bar' }]));
|
||||||
await settled();
|
await settled();
|
||||||
assert.dom('[data-test-error]').includesText('Vault expects data to be formatted as an JSON object');
|
assert.dom('[data-test-error]').includesText('Vault expects data to be formatted as an JSON object');
|
||||||
|
@ -99,7 +96,7 @@ module('Integration | Component | secret edit', function (hooks) {
|
||||||
|
|
||||||
await render(hbs`{{secret-edit mode=mode model=model preferAdvancedEdit=true }}`);
|
await render(hbs`{{secret-edit mode=mode model=model preferAdvancedEdit=true }}`);
|
||||||
|
|
||||||
let instance = this.codeMirror.instanceFor(find('[data-test-component=json-editor]').id);
|
let instance = document.querySelector('.CodeMirror').CodeMirror;
|
||||||
instance.setValue(JSON.stringify([{ foo: 'bar' }]));
|
instance.setValue(JSON.stringify([{ foo: 'bar' }]));
|
||||||
await settled();
|
await settled();
|
||||||
assert.dom('[data-test-error]').includesText('Vault expects data to be formatted as an JSON object');
|
assert.dom('[data-test-error]').includesText('Vault expects data to be formatted as an JSON object');
|
||||||
|
|
|
@ -15,7 +15,7 @@ export default {
|
||||||
hasSearchSelect: isPresent('[data-test-component=search-select]'),
|
hasSearchSelect: isPresent('[data-test-component=search-select]'),
|
||||||
hasTextFile: isPresent('[data-test-component=text-file]'),
|
hasTextFile: isPresent('[data-test-component=text-file]'),
|
||||||
hasTTLPicker: isPresent('[data-test-toggle-input="Foo"]'),
|
hasTTLPicker: isPresent('[data-test-toggle-input="Foo"]'),
|
||||||
hasJSONEditor: isPresent('[data-test-component=json-editor]'),
|
hasJSONEditor: isPresent('[data-test-component="code-mirror-modifier"]'),
|
||||||
hasJSONClearButton: isPresent('[data-test-json-clear-button]'),
|
hasJSONClearButton: isPresent('[data-test-json-clear-button]'),
|
||||||
hasSelect: isPresent('select'),
|
hasSelect: isPresent('select'),
|
||||||
hasInput: isPresent('input'),
|
hasInput: isPresent('input'),
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
import { isPresent, isVisible, text } from 'ember-cli-page-object';
|
import { isPresent, notHasClass, text } from 'ember-cli-page-object';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
title: text('[data-test-component=json-editor-title]'),
|
title: text('[data-test-component=json-editor-title]'),
|
||||||
hasToolbar: isPresent('[data-test-component=json-editor-toolbar]'),
|
hasToolbar: isPresent('[data-test-component=json-editor-toolbar]'),
|
||||||
hasJSONEditor: isPresent('[data-test-component=json-editor]'),
|
hasJSONEditor: isPresent('[data-test-component="code-mirror-modifier"]'),
|
||||||
canEdit: isVisible('div.CodeMirror-gutters'),
|
canEdit: notHasClass('readonly-codemirror'),
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,34 +0,0 @@
|
||||||
import getCodeMirrorInstance from 'vault/tests/helpers/codemirror';
|
|
||||||
// Like fillable, but for the CodeMirror editor
|
|
||||||
//
|
|
||||||
// Usage: fillIn: codeFillable('[data-test-editor]')
|
|
||||||
// Page.fillIn(code);
|
|
||||||
export function codeFillable(selector) {
|
|
||||||
return {
|
|
||||||
isDescriptor: true,
|
|
||||||
|
|
||||||
get() {
|
|
||||||
return function (context, code) {
|
|
||||||
const cm = getCodeMirrorInstance(context, selector);
|
|
||||||
cm.setValue(code);
|
|
||||||
return this;
|
|
||||||
};
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Like text, but for the CodeMirror editor
|
|
||||||
//
|
|
||||||
// Usage: content: code('[data-test-editor]')
|
|
||||||
// Page.code(); // some = [ 'string', 'of', 'code' ]
|
|
||||||
export function code(selector) {
|
|
||||||
return {
|
|
||||||
isDescriptor: true,
|
|
||||||
get() {
|
|
||||||
return function (context) {
|
|
||||||
const cm = getCodeMirrorInstance(context, selector);
|
|
||||||
return cm.getValue();
|
|
||||||
};
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { Base } from '../create';
|
import { Base } from '../create';
|
||||||
import { isPresent, clickable, visitable, create, fillable } from 'ember-cli-page-object';
|
import { isPresent, clickable, visitable, create, fillable } from 'ember-cli-page-object';
|
||||||
import { codeFillable } from 'vault/tests/pages/helpers/codemirror';
|
|
||||||
export default create({
|
export default create({
|
||||||
...Base,
|
...Base,
|
||||||
path: fillable('[data-test-secret-path="true"]'),
|
path: fillable('[data-test-secret-path="true"]'),
|
||||||
|
@ -17,9 +17,6 @@ export default create({
|
||||||
hasMetadataFields: isPresent('[data-test-metadata-fields]'),
|
hasMetadataFields: isPresent('[data-test-metadata-fields]'),
|
||||||
maxVersion: fillable('[data-test-input="maxVersions"]'),
|
maxVersion: fillable('[data-test-input="maxVersions"]'),
|
||||||
startCreateSecret: clickable('[data-test-secret-create]'),
|
startCreateSecret: clickable('[data-test-secret-create]'),
|
||||||
editor: {
|
|
||||||
fillIn: codeFillable('[data-test-component="json-editor"]'),
|
|
||||||
},
|
|
||||||
deleteSecret() {
|
deleteSecret() {
|
||||||
return this.deleteBtn().confirmBtn();
|
return this.deleteBtn().confirmBtn();
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import { Base } from '../show';
|
import { Base } from '../show';
|
||||||
import { create, clickable, collection, isPresent, text } from 'ember-cli-page-object';
|
import { create, clickable, collection, isPresent, text } from 'ember-cli-page-object';
|
||||||
import { code } from 'vault/tests/pages/helpers/codemirror';
|
|
||||||
|
|
||||||
export default create({
|
export default create({
|
||||||
...Base,
|
...Base,
|
||||||
|
@ -18,9 +17,7 @@ export default create({
|
||||||
editIsPresent: isPresent('[data-test-secret-edit]'),
|
editIsPresent: isPresent('[data-test-secret-edit]'),
|
||||||
noReadIsPresent: isPresent('[data-test-write-without-read-empty-message]'),
|
noReadIsPresent: isPresent('[data-test-write-without-read-empty-message]'),
|
||||||
noReadMessage: text('data-test-empty-state-message'),
|
noReadMessage: text('data-test-empty-state-message'),
|
||||||
editor: {
|
|
||||||
content: code('[data-test-component="json-editor"]'),
|
|
||||||
},
|
|
||||||
deleteSecret() {
|
deleteSecret() {
|
||||||
return this.deleteBtn().confirmBtn();
|
return this.deleteBtn().confirmBtn();
|
||||||
},
|
},
|
||||||
|
|
31
ui/yarn.lock
31
ui/yarn.lock
|
@ -7974,7 +7974,12 @@ code-point-at@^1.0.0:
|
||||||
resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77"
|
resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77"
|
||||||
integrity sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=
|
integrity sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=
|
||||||
|
|
||||||
codemirror@^5.47.0, codemirror@^5.58.2:
|
codemirror@^5.47.0:
|
||||||
|
version "5.65.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/codemirror/-/codemirror-5.65.2.tgz#5799a70cb3d706e10f60e267245e3a75205d3dd9"
|
||||||
|
integrity sha512-SZM4Zq7XEC8Fhroqe3LxbEEX1zUPWH1wMr5zxiBuiUF64iYOUH/JI88v4tBag8MiBS8B8gRv8O1pPXGYXQ4ErA==
|
||||||
|
|
||||||
|
codemirror@^5.58.2:
|
||||||
version "5.61.0"
|
version "5.61.0"
|
||||||
resolved "https://registry.yarnpkg.com/codemirror/-/codemirror-5.61.0.tgz#318e5b034a707207948b92ffc2862195e8fdb08e"
|
resolved "https://registry.yarnpkg.com/codemirror/-/codemirror-5.61.0.tgz#318e5b034a707207948b92ffc2862195e8fdb08e"
|
||||||
integrity sha512-D3wYH90tYY1BsKlUe0oNj2JAhQ9TepkD51auk3N7q+4uz7A/cgJ5JsWHreT0PqieW1QhOuqxQ2reCXV1YXzecg==
|
integrity sha512-D3wYH90tYY1BsKlUe0oNj2JAhQ9TepkD51auk3N7q+4uz7A/cgJ5JsWHreT0PqieW1QhOuqxQ2reCXV1YXzecg==
|
||||||
|
@ -10222,7 +10227,7 @@ ember-cli-typescript@^3.0.0, ember-cli-typescript@^3.1.3, ember-cli-typescript@^
|
||||||
stagehand "^1.0.0"
|
stagehand "^1.0.0"
|
||||||
walk-sync "^2.0.0"
|
walk-sync "^2.0.0"
|
||||||
|
|
||||||
ember-cli-typescript@^4.1.0, ember-cli-typescript@^4.2.0:
|
ember-cli-typescript@^4.1.0, ember-cli-typescript@^4.2.0, ember-cli-typescript@^4.2.1:
|
||||||
version "4.2.1"
|
version "4.2.1"
|
||||||
resolved "https://registry.yarnpkg.com/ember-cli-typescript/-/ember-cli-typescript-4.2.1.tgz#54d08fc90318cc986f3ea562f93ce58a6cc4c24d"
|
resolved "https://registry.yarnpkg.com/ember-cli-typescript/-/ember-cli-typescript-4.2.1.tgz#54d08fc90318cc986f3ea562f93ce58a6cc4c24d"
|
||||||
integrity sha512-0iKTZ+/wH6UB/VTWKvGuXlmwiE8HSIGcxHamwNhEC5x1mN3z8RfvsFZdQWYUzIWFN2Tek0gmepGRPTwWdBYl/A==
|
integrity sha512-0iKTZ+/wH6UB/VTWKvGuXlmwiE8HSIGcxHamwNhEC5x1mN3z8RfvsFZdQWYUzIWFN2Tek0gmepGRPTwWdBYl/A==
|
||||||
|
@ -10396,6 +10401,17 @@ ember-compatibility-helpers@^1.2.0, ember-compatibility-helpers@^1.2.1, ember-co
|
||||||
fs-extra "^9.1.0"
|
fs-extra "^9.1.0"
|
||||||
semver "^5.4.1"
|
semver "^5.4.1"
|
||||||
|
|
||||||
|
ember-compatibility-helpers@^1.2.5:
|
||||||
|
version "1.2.6"
|
||||||
|
resolved "https://registry.yarnpkg.com/ember-compatibility-helpers/-/ember-compatibility-helpers-1.2.6.tgz#603579ab2fb14be567ef944da3fc2d355f779cd8"
|
||||||
|
integrity sha512-2UBUa5SAuPg8/kRVaiOfTwlXdeVweal1zdNPibwItrhR0IvPrXpaqwJDlEZnWKEoB+h33V0JIfiWleSG6hGkkA==
|
||||||
|
dependencies:
|
||||||
|
babel-plugin-debug-macros "^0.2.0"
|
||||||
|
ember-cli-version-checker "^5.1.1"
|
||||||
|
find-up "^5.0.0"
|
||||||
|
fs-extra "^9.1.0"
|
||||||
|
semver "^5.4.1"
|
||||||
|
|
||||||
ember-composable-helpers@^4.3.0:
|
ember-composable-helpers@^4.3.0:
|
||||||
version "4.5.0"
|
version "4.5.0"
|
||||||
resolved "https://registry.yarnpkg.com/ember-composable-helpers/-/ember-composable-helpers-4.5.0.tgz#94febbdf4348e64f45f7a6f993f326e32540a61e"
|
resolved "https://registry.yarnpkg.com/ember-composable-helpers/-/ember-composable-helpers-4.5.0.tgz#94febbdf4348e64f45f7a6f993f326e32540a61e"
|
||||||
|
@ -10656,6 +10672,17 @@ ember-modifier@^2.1.0:
|
||||||
ember-destroyable-polyfill "^2.0.2"
|
ember-destroyable-polyfill "^2.0.2"
|
||||||
ember-modifier-manager-polyfill "^1.2.0"
|
ember-modifier-manager-polyfill "^1.2.0"
|
||||||
|
|
||||||
|
ember-modifier@^3.1.0:
|
||||||
|
version "3.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/ember-modifier/-/ember-modifier-3.1.0.tgz#ba5b0941302accd787ed3dcfc8d20400b77ffc41"
|
||||||
|
integrity sha512-G5Lj9jVFsD2sVJcRNQfaGKG1p81wT4LGfClBhCuB4TgwP1NGJKdqI+Q8BW2MptONxQt/71UjjUH0YK7Gm9eahg==
|
||||||
|
dependencies:
|
||||||
|
ember-cli-babel "^7.26.6"
|
||||||
|
ember-cli-normalize-entity-name "^1.0.0"
|
||||||
|
ember-cli-string-utils "^1.1.0"
|
||||||
|
ember-cli-typescript "^4.2.1"
|
||||||
|
ember-compatibility-helpers "^1.2.5"
|
||||||
|
|
||||||
ember-native-dom-helpers@^0.7.0:
|
ember-native-dom-helpers@^0.7.0:
|
||||||
version "0.7.0"
|
version "0.7.0"
|
||||||
resolved "https://registry.yarnpkg.com/ember-native-dom-helpers/-/ember-native-dom-helpers-0.7.0.tgz#98a87c11a391cec5c12382a4857e59ea2fb4b00a"
|
resolved "https://registry.yarnpkg.com/ember-native-dom-helpers/-/ember-native-dom-helpers-0.7.0.tgz#98a87c11a391cec5c12382a4857e59ea2fb4b00a"
|
||||||
|
|
Loading…
Reference in New Issue