Merge branch 'main' into credential-library-create

This commit is contained in:
Susmitha Girumala authored and GitHub committed 2021-07-21 18:19:53 -04:00
commit b0dd9bf803
21 files changed
+390 -33

No files matched your search

+1 -1
View File
@@ -42,7 +42,7 @@
"ember-auto-import": "^1.10.1",
"ember-cli": "~3.27.0",
"ember-cli-dependency-checker": "^3.2.0",
"ember-cli-inject-live-reload": "^2.0.2",
"ember-cli-inject-live-reload": "^2.1.0",
"ember-cli-sri": "^2.1.1",
"ember-cli-terser": "^4.0.1",
"ember-cli-update": "^0.54.6",
+1 -1
View File
@@ -37,7 +37,7 @@
"ember-auto-import": "^1.10.1",
"ember-cli": "~3.27.0",
"ember-cli-dependency-checker": "^3.2.0",
"ember-cli-inject-live-reload": "^2.0.2",
"ember-cli-inject-live-reload": "^2.1.0",
"ember-cli-sri": "^2.1.1",
"ember-cli-terser": "^4.0.1",
"ember-cli-update": "^0.54.6",
+1 -1
View File
@@ -52,7 +52,7 @@
"ember-auto-import": "^1.10.1",
"ember-cli": "~3.27.0",
"ember-cli-dependency-checker": "^3.2.0",
"ember-cli-inject-live-reload": "^2.0.2",
"ember-cli-inject-live-reload": "^2.1.0",
"ember-cli-sri": "^2.1.1",
"ember-cli-terser": "^4.0.1",
"ember-disable-prototype-extensions": "^1.1.3",
@@ -33,3 +33,4 @@ edit-form: Edit Form
change-origin: Change Origin URL
change-state: Change State
close: Close
toggle-secret: Toggle secret visibility
@@ -1,4 +1,4 @@
<div class="rose-form-checkbox{{if @error " error"}}">
<div class="rose-form-checkbox{{if @error " error"}} {{if @inline " inline"}}">
<Input
...attributes
@@ -68,3 +68,61 @@ Renders a form input checkbox.
}
}}</Story>
</Preview>
```htmlbars
<Rose::Form::Checkbox
@name="boolean-value"
@label="Yes?"
@checked={{false}}
@error={{false}}
@disabled={{false}}
@inline={{false}}
/>
```
<Preview>
<Story name="Checkbox with inline style" height="10rem">{{
template: hbs`
<Rose::Form::Checkbox
@name='checkbox-1'
@label='Checkbox1 🛎'
@description={{description}}
@helperText={{helperText}}
@checked={{checked}}
@error={{error}}
@disabled={{disabled}}
@inline={{inline}}
/>
<Rose::Form::Checkbox
@name='checkbox-2'
@label='Checkbox2 🛎'
@description={{description}}
@helperText={{helperText}}
@checked={{checked}}
@error={{error}}
@disabled={{disabled}}
@inline={{inline}}
/>
<Rose::Form::Checkbox
@name='checkbox-3'
@label='Checkbox3 🛎'
@description={{description}}
@helperText={{helperText}}
@checked={{checked}}
@error={{error}}
@disabled={{disabled}}
@inline={{inline}}
/>
`,
context: {
description: text('description', 'Short description in label'),
helperText: text('helperText', 'This is an optional help text.'),
checked: boolean('checked', false),
error: boolean('error', false),
disabled: boolean('disabled', false),
inline: boolean('inline', false)
}
}}</Story>
</Preview>
@@ -1,3 +1,3 @@
{{yield (hash
radio=(component 'rose/form/radio/radio' name=@name selectedValue=@selectedValue disabled=@disabled changed=@changed)
)}}
radio=(component 'rose/form/radio/radio' name=@name selectedValue=@selectedValue disabled=@disabled changed=@changed inline=@inline)
)}}
@@ -15,6 +15,7 @@ Renders a form radiofield group.
@name={{name}}
@selectedValue="disagree"
@changed={{fn}}
@inline={{false}}
as |radioGroup|
>
<radioGroup.radio
@@ -39,6 +40,7 @@ Renders a form radiofield group.
@name={{name}}
@selectedValue={{selectedValue}}
@changed={{changed}}
@inline={{inline}}
as |radioGroup|
>
<radioGroup.radio
@@ -59,6 +61,7 @@ Renders a form radiofield group.
context: {
name: text('name', 'mustagree'),
disabled: boolean('disabled', false),
inline: boolean('inline', true),
selectedValue: text('selectedValue', 'disagree'),
firstFieldLabel: text('first label', 'Agree'),
firstFieldValue: text('first value', 'agree'),
@@ -1,4 +1,4 @@
<div class="rose-form-radio{{if @error " error"}}">
<div class="rose-form-radio{{if @error " error"}} {{if @inline "inline"}}">
<RadioButton
...attributes
@@ -54,3 +54,71 @@ Renders a form radiofield.
}
}}</Story>
</Preview>
```htmlbars
<Rose::Form::Radio::Radio
@label="Yes"
@error={{false}}
@value="yes-agree"
@selectedValue="yes-agree"
@disabled={{false}}
@changed={{fn}}
@inline={{true}}
/>
```
<Preview>
<Story name="Radio with inline style" height="10rem">{{
template: hbs`
<Rose::Form::Radio::Radio
@label='Radiofield1'
@error={{error}}
@value={{value}}
@selectedValue={{selectedValue}}
@disabled={{disabled}}
@icon={{icon}}
@inline={{inline}}
/>
<Rose::Form::Radio::Radio
@label='Radiofield2'
@error={{error}}
@value={{value}}
@selectedValue={{selectedValue}}
@disabled={{disabled}}
@icon={{icon}}
@inline={{inline}}
/>
<Rose::Form::Radio::Radio
@label='Radiofield3'
@error={{error}}
@value={{value}}
@selectedValue={{selectedValue}}
@disabled={{disabled}}
@icon={{icon}}
@inline={{inline}}
/>
`,
context: {
error: boolean('error', false),
value: text('value', 'value and selectedValue must match to select radiofield'),
selectedValue: text(
'selectedValue',
'value and selectedValue must match to select radiofield'
),
disabled: boolean('disabled', false),
icon: select(
'icon',
{
'none': null,
'user-organization': 'user-organization',
'copy-action': 'copy-action',
'unfold-more': 'unfold-more',
'unfold-less': 'unfold-less',
},
null
),
inline: boolean('inline', false)
}
}}</Story>
</Preview>
@@ -2,6 +2,10 @@
@use '../../utilities/type';
.rose-form-checkbox {
&.inline {
display: inline-block;
margin-right: sizing.rems(s);
}
@include type.type(m);
position: relative;
margin-bottom: sizing.rems(m);
@@ -5,7 +5,7 @@
margin-bottom: sizing.rems(m);
.rose-form-fieldset-title {
@include type.type(l, bold);
@include type.type(s, bold);
}
.rose-form-fieldset-description {
@@ -21,8 +21,13 @@ $radio-svg: (
);
$data-svg-prefix: 'data:image/svg+xml;utf-8,';
.rose-form-radio {
&.inline {
display: inline-block;
.rose-form-radio-label {
margin-right: sizing.rems(s);
}
}
@include type.type(m);
position: relative;
margin-bottom: sizing.rems(xs);
+1 -1
View File
@@ -55,7 +55,7 @@
"ember-auto-import": "^1.10.1",
"ember-cli": "~3.27.0",
"ember-cli-dependency-checker": "^3.2.0",
"ember-cli-inject-live-reload": "^2.0.2",
"ember-cli-inject-live-reload": "^2.1.0",
"ember-cli-sass": "^10.0.1",
"ember-cli-sri": "^2.1.1",
"ember-cli-terser": "^4.0.1",
@@ -2,6 +2,7 @@
@onSubmit={{@submit}}
@cancel={{@cancel}}
@disabled={{@model.isSaving}}
@error={{@model.errors.base}}
@showEditToggle={{if @model.isNew false true}}
as |form|
>
@@ -18,16 +19,38 @@
@value={{@model.name}}
@label={{t 'form.name.label'}}
@helperText={{t 'form.name.help'}}
@error={{@model.errors.name}}
readonly={{false}}
/>
as |field|
>
{{#if @model.errors.name}}
<field.errors as |errors|>
{{#each @model.errors.name as |error|}}
<errors.message>{{error.message}}</errors.message>
{{/each}}
</field.errors>
{{/if}}
</form.input>
<form.textarea
@name='description'
@type='description'
@value={{@model.description}}
@label={{t 'form.description.label'}}
@error={{@model.errors.description}}
@helperText={{t 'form.description.help'}}
/>
disabled={{@model.isSaving}}
readonly={{false}}
as |field|
>
{{#if @model.errors.description}}
<field.errors as |errors|>
{{#each @model.errors.description as |error|}}
<errors.message>{{error.message}}</errors.message>
{{/each}}
</field.errors>
{{/if}}
</form.textarea>
<form.actions
@disabled={{if @model.cannotSave @model.cannotSave}}
+1 -1
View File
@@ -57,7 +57,7 @@
"ember-cli-content-security-policy": "^1.1.1",
"ember-cli-dependency-checker": "^3.2.0",
"ember-cli-htmlbars": "^5.3.1",
"ember-cli-inject-live-reload": "^2.0.2",
"ember-cli-inject-live-reload": "^2.1.0",
"ember-cli-sass": "^10.0.1",
"ember-cli-sri": "^2.1.1",
"ember-cli-template-lint": "^2.0.2",
@@ -1,13 +1,17 @@
import { module, test } from 'qunit';
import { visit, currentURL } from '@ember/test-helpers';
import { visit, currentURL, find, click, fillIn } from '@ember/test-helpers';
import { setupApplicationTest } from 'ember-qunit';
import setupMirage from 'ember-cli-mirage/test-support/setup-mirage';
import a11yAudit from 'ember-a11y-testing/test-support/audit';
import { authenticateSession } from 'ember-simple-auth/test-support';
import { Response } from 'miragejs';
module('Acceptance | credential-stores', function (hooks) {
setupApplicationTest(hooks);
setupMirage(hooks);
let getCredentialStoresCount;
const instances = {
scopes: {
global: null,
@@ -18,7 +22,12 @@ module('Acceptance | credential-stores', function (hooks) {
const urls = {
globalScope: null,
orgScope: null,
projectScope: null,
credentialStores: null,
credentialStore: null,
unknownCredentialStore: null,
newCredentialStore: null,
};
hooks.beforeEach(function () {
@@ -32,17 +41,199 @@ module('Acceptance | credential-stores', function (hooks) {
type: 'project',
scope: { id: instances.scopes.org.id, type: 'org' },
});
instances.credentialStore = this.server.create('credential-store', {
scope: instances.scopes.project,
});
// Generate route URLs for resources
urls.globalScope = `/scopes/global/scopes`;
urls.orgScope = `/scopes/${instances.scopes.org.id}/scopes`;
urls.projectScope = `/scopes/${instances.scopes.project.id}`;
urls.credentialStores = `${urls.projectScope}/credential-stores`;
urls.credentialStore = `${urls.credentialStores}/${instances.credentialStore.id}`;
urls.unknownCredentialStore = `${urls.credentialStores}/foo`;
urls.newCredentialStore = `${urls.credentialStores}/new`;
// Generate resource counter
getCredentialStoresCount = () => {
return this.server.schema.credentialStores.all().models.length;
};
authenticateSession({});
});
test('visiting /credential-stores', async function (assert) {
assert.expect(1);
assert.expect(2);
await visit(urls.credentialStores);
await a11yAudit();
assert.equal(currentURL(), urls.credentialStores);
await visit(urls.credentialStore);
await a11yAudit();
assert.equal(currentURL(), urls.credentialStore);
});
test('visiting an unknown credential store display 404 message', async function (assert) {
assert.expect(1);
await visit(urls.unknownCredentialStore);
await a11yAudit();
assert.equal(
find('.rose-message-subtitle').textContent.trim(),
'Error 404'
);
});
test('can create a new credential stores', async function (assert) {
assert.expect(1);
const count = getCredentialStoresCount();
await visit(urls.newCredentialStore);
await fillIn('[name="name"]', 'random string');
await click('[type="submit"]');
assert.equal(getCredentialStoresCount(), count + 1);
});
test('can cancel create new credential stores', async function (assert) {
assert.expect(2);
const count = getCredentialStoresCount();
await visit(urls.newCredentialStore);
await fillIn('[name="name"]', 'random string');
await click('.rose-form-actions [type="button"]');
assert.equal(currentURL(), urls.credentialStores);
assert.equal(getCredentialStoresCount(), count);
});
test('saving a new credential store with invalid fields displays error messages', async function (assert) {
assert.expect(2);
this.server.post('/credential-stores', () => {
return new Response(
400,
{},
{
status: 400,
code: 'invalid_argument',
message: 'The request was invalid.',
details: {
request_fields: [
{
name: 'name',
description: 'Name is required.',
},
],
},
}
);
});
await visit(urls.newCredentialStore);
await click('[type="submit"]');
assert.ok(
find('[role="alert"]').textContent.trim(),
'The request was invalid.'
);
assert.ok(
find('.rose-form-error-message').textContent.trim(),
'Name is required.'
);
});
test('can save changes to existing credential store', async function (assert) {
assert.expect(3);
assert.notEqual(instances.credentialStore.name, 'random string');
await visit(urls.credentialStore);
await click('form [type="button"]', 'Activate edit mode');
await fillIn('[name="name"]', 'random string');
await click('.rose-form-actions [type="submit"]');
assert.equal(currentURL(), urls.credentialStore);
assert.equal(
this.server.schema.credentialStores.all().models[0].name,
'random string'
);
});
test('can cancel changes to existing credential store', async function (assert) {
assert.expect(2);
await visit(urls.credentialStore);
await click('form [type="button"]', 'Activate edit mode');
await fillIn('[name="name"]', 'random string');
await click('.rose-form-actions [type="button"]');
assert.notEqual(instances.credentialStore.name, 'random string');
assert.equal(find('[name="name"]').value, instances.credentialStore.name);
});
test('saving an existing credential store with invalid fields displays error messages', async function (assert) {
assert.expect(2);
this.server.patch('/credential-stores/:id', () => {
return new Response(
400,
{},
{
status: 400,
code: 'invalid_argument',
message: 'The request was invalid.',
details: {
request_fields: [
{
name: 'name',
description: 'Name is required.',
},
],
},
}
);
});
await visit(urls.credentialStore);
await click('form [type="button"]', 'Activate edit mode');
await fillIn('[name="name"]', 'random string');
await click('[type="submit"]');
assert.ok(
find('[role="alert"]').textContent.trim(),
'The request was invalid.'
);
assert.ok(
find('.rose-form-error-message').textContent.trim(),
'Name is required.'
);
});
test('can discard unsaved credential store changes via dialog', async function (assert) {
assert.expect(5);
const confirmService = this.owner.lookup('service:confirm');
confirmService.enabled = true;
assert.notEqual(instances.credentialStore.name, 'random string');
await visit(urls.credentialStore);
await click('form [type="button"]', 'Activate edit mode');
await fillIn('[name="name"]', 'random string');
assert.equal(currentURL(), urls.credentialStore);
try {
await visit(urls.credentialStores);
} catch (e) {
assert.ok(find('.rose-dialog'));
await click('.rose-dialog-footer button:first-child', 'Click Discard');
assert.equal(currentURL(), urls.credentialStores);
assert.notEqual(
this.server.schema.credentialStores.all().models[0].name,
'random string'
);
}
});
test('can cancel discard unsaved credential store via dialog', async function (assert) {
assert.expect(5);
const confirmService = this.owner.lookup('service:confirm');
confirmService.enabled = true;
assert.notEqual(instances.credentialStore.name, 'random string');
await visit(urls.credentialStore);
await click('form [type="button"]', 'Activate edit mode');
await fillIn('[name="name"]', 'random string');
assert.equal(currentURL(), urls.credentialStore);
try {
await visit(urls.credentialStores);
} catch (e) {
assert.ok(find('.rose-dialog'));
await click('.rose-dialog-footer button:last-child');
assert.equal(currentURL(), urls.credentialStore);
assert.notEqual(
this.server.schema.credentialStores.all().models[0].name,
'random string'
);
}
});
});
@@ -1,9 +1,11 @@
<div class='hidden-secret'>
<Rose::Icon
@name={{if this.isHidden 'visibility-show' 'visibility-hide'}}
@size='medium'
<Rose::Button
@style='ghost'
@iconOnly={{if this.isHidden 'visibility-show' 'visibility-hide'}}
{{on 'click' this.toggleSecretVisibility}}
/>
>
{{t 'actions.toggle-secret'}}
</Rose::Button>
<Copyable
@text={{@secret}}
@buttonText={{t 'actions.copy-to-clipboard'}}
@@ -12,7 +14,7 @@
{{#if this.isHidden}}
{{@secret}}
{{else}}
{{this.textMask}}
<span class='secret-mask'>{{this.textMask}}</span>
{{/if}}
</Copyable>
</div>
+10 -8
View File
@@ -478,20 +478,22 @@ $chevron-down: url("data:image/svg+xml;utf-8,<svg viewBox='0 0 24 24' fill='%231
.hidden-secret {
display: flex;
align-items: center;
color: var(--ui-gray-subtler-2);
.rose-icon {
margin-top: sizing.rems(xxxs);
margin-right: sizing.rems(s);
}
.copyable {
display: flex;
width: 100%;
justify-content: space-between;
.secret-mask {
color: var(--ui-gray-subtler-2);
}
.rose-button-ghost { // Copyable button icon.
color: var(--ui-gray-subtler-1);
}
}
.rose-button-ghost {
color: var(--ui-gray-subtler-1);
.rose-button-ghost { // Hide/Show secret button icon.
color: var(--ui-gray);
}
}
+1 -1
View File
@@ -72,7 +72,7 @@
"ember-cli-code-coverage": "^1.0.2",
"ember-cli-dependency-checker": "^3.2.0",
"ember-cli-htmlbars": "^5.3.1",
"ember-cli-inject-live-reload": "^2.0.2",
"ember-cli-inject-live-reload": "^2.1.0",
"ember-cli-sass": "^10.0.1",
"ember-cli-sri": "^2.1.1",
"ember-cli-template-lint": "^2.0.2",
+4 -4
View File
@@ -8712,10 +8712,10 @@ ember-cli-htmlbars@^5.3.1, ember-cli-htmlbars@^5.3.2, ember-cli-htmlbars@^5.7.1:
strip-bom "^4.0.0"
walk-sync "^2.2.0"
ember-cli-inject-live-reload@^2.0.2:
version "2.0.2"
resolved "https://registry.yarnpkg.com/ember-cli-inject-live-reload/-/ember-cli-inject-live-reload-2.0.2.tgz#95edb543b386239d35959e5ea9579f5382976ac7"
integrity sha512-HDD6o/kBHT/kUtazklU0OW23q2jigIN42QmcpFdXUSvJ2/2SYA6yIqSUxWfJgISmtn5gTNZ2KPq1p3dLkhJxSQ==
ember-cli-inject-live-reload@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/ember-cli-inject-live-reload/-/ember-cli-inject-live-reload-2.1.0.tgz#ef63c733c133024d5726405a3c247fa12e88a385"
integrity sha512-YV5wYRD5PJHmxaxaJt18u6LE6Y+wo455BnmcpN+hGNlChy2piM9/GMvYgTAz/8Vin8RJ5KekqP/w/NEaRndc/A==
dependencies:
clean-base-url "^1.0.0"
ember-cli-version-checker "^3.1.3"