feat: 🎸 add export options flyout (#3198)
* feat: 🎸 add export options flyout * fix: 🐛 update id to be ids * test: 💍 add a11y ignores * test: 💍 fix tests for grant string format ✅ Closes: https://hashicorp.atlassian.net/browse/ICU-18524
This commit is contained in:
1 parent
759c1f5555
commit
add2a9d6c4
8 files changed
+211
-10
No files matched your search
@@ -70,3 +70,4 @@ hide-errors: Hide Errors
|
||||
edit-worker-filter: Edit Worker Filter
|
||||
add-worker-filter: Add Worker Filter
|
||||
open: Open
|
||||
download: Download
|
||||
@@ -680,6 +680,11 @@ role:
|
||||
insert-resource-types: insert resource types
|
||||
insert-id: insert id
|
||||
insert-resource-ids: insert resource ids
|
||||
export:
|
||||
format: Format
|
||||
options:
|
||||
terraform: Terraform
|
||||
native-hcl: Native HCL
|
||||
scope:
|
||||
title: Scope
|
||||
title_plural: Scopes
|
||||
|
||||
@@ -25,16 +25,67 @@
|
||||
</Hds::Accordion>
|
||||
</Hds::Layout::Grid>
|
||||
|
||||
{{#if this.showExportOptionsFlyout}}
|
||||
<Hds::Flyout
|
||||
id='export-options-flyout'
|
||||
@size='large'
|
||||
@onClose={{this.toggleExportOptionsFlyout}}
|
||||
data-test-export-options-flyout
|
||||
as |M|
|
||||
>
|
||||
<M.Header>{{t 'actions.export'}}</M.Header>
|
||||
<M.Body>
|
||||
<Hds::Form::Select::Field
|
||||
name='export-format'
|
||||
@width='100%'
|
||||
{{on 'change' (set-from-event this 'selectedExportOption')}}
|
||||
as |F|
|
||||
>
|
||||
<F.Label>{{t 'resources.role.edit-grants.export.format'}}</F.Label>
|
||||
<F.Options>
|
||||
{{#each this.exportOptions as |type|}}
|
||||
<option
|
||||
value={{type}}
|
||||
selected={{eq type this.selectedExportOption}}
|
||||
>
|
||||
{{t (concat 'resources.role.edit-grants.export.options.' type)}}
|
||||
</option>
|
||||
{{/each}}
|
||||
</F.Options>
|
||||
</Hds::Form::Select::Field>
|
||||
|
||||
<Hds::CodeBlock
|
||||
@ariaLabel='formatted-export'
|
||||
@hasCopyButton={{true}}
|
||||
@language='hcl'
|
||||
@value={{this.formattedExport}}
|
||||
/>
|
||||
|
||||
<Hds::Button
|
||||
class='margin-top-l'
|
||||
@text={{t 'actions.download'}}
|
||||
@icon='download'
|
||||
@color='secondary'
|
||||
{{on 'click' this.downloadFormattedExport}}
|
||||
/>
|
||||
</M.Body>
|
||||
</Hds::Flyout>
|
||||
{{/if}}
|
||||
|
||||
<form.actions class='margin-top-l'>
|
||||
<:actions>
|
||||
<Hds::ButtonSet>
|
||||
<Hds::Button type='submit' @text={{t 'actions.save'}} />
|
||||
<Hds::Button @text={{t 'actions.cancel'}} @color='secondary' />
|
||||
<Hds::Button
|
||||
@text={{t 'actions.export'}}
|
||||
@color='tertiary'
|
||||
@icon='upload'
|
||||
/>
|
||||
{{#if this.grantStringLines}}
|
||||
<Hds::Button
|
||||
@text={{t 'actions.export'}}
|
||||
@color='tertiary'
|
||||
@icon='upload'
|
||||
data-test-export-grants-btn
|
||||
{{on 'click' this.toggleExportOptionsFlyout}}
|
||||
/>
|
||||
{{/if}}
|
||||
</Hds::ButtonSet>
|
||||
</:actions>
|
||||
</form.actions>
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
/**
|
||||
* Copyright IBM Corp. 2021, 2026
|
||||
* SPDX-License-Identifier: BUSL-1.1
|
||||
*/
|
||||
|
||||
import Component from '@glimmer/component';
|
||||
import { tracked } from '@glimmer/tracking';
|
||||
import { action } from '@ember/object';
|
||||
|
||||
export default class FormRoleEditGrants extends Component {
|
||||
// =attributes
|
||||
|
||||
exportOptionsMap = { terraform: 'terraform', nativeHCL: 'native-hcl' };
|
||||
exportOptions = Object.values(this.exportOptionsMap);
|
||||
|
||||
// TODO: Replace with actual grant lines from code editor once implemented.
|
||||
grantStringLines =
|
||||
'ids=hc_123;type=host-catalog;actions=read,create,list\nids=ttcp_123;type=target;actions=list\ntype=credential;actions=create';
|
||||
|
||||
@tracked showExportOptionsFlyout = false;
|
||||
@tracked selectedExportOption = this.exportOptions[0];
|
||||
|
||||
/**
|
||||
* Returns the formatted export based on the selected export option.
|
||||
* @type {string}
|
||||
*/
|
||||
get formattedExport() {
|
||||
if (this.selectedExportOption === this.exportOptionsMap.terraform) {
|
||||
return this.terraformFormattedExport;
|
||||
}
|
||||
return this.nativeHclFormattedExport;
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats grant strings into a Terraform format.
|
||||
* @type {string}
|
||||
*/
|
||||
get terraformFormattedExport() {
|
||||
let formatted = `grant_strings = [ \n`;
|
||||
this.grantStringLines.split('\n').forEach((line) => {
|
||||
formatted += ` "${line}",\n`;
|
||||
});
|
||||
formatted += `]\n`;
|
||||
return formatted;
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats grant strings into a Native HCL format.
|
||||
* @type {string}
|
||||
*/
|
||||
get nativeHclFormattedExport() {
|
||||
let formatted = `[ \n`;
|
||||
this.grantStringLines.split('\n').forEach((line) => {
|
||||
formatted += ` "${line}",\n`;
|
||||
});
|
||||
formatted += `]\n`;
|
||||
return formatted;
|
||||
}
|
||||
|
||||
// =actions
|
||||
|
||||
/**
|
||||
* Toggles the export options flyout open and closed.
|
||||
*/
|
||||
@action
|
||||
toggleExportOptionsFlyout() {
|
||||
this.showExportOptionsFlyout = !this.showExportOptionsFlyout;
|
||||
}
|
||||
|
||||
/**
|
||||
* Triggers the download of the formatted export as a file.
|
||||
*/
|
||||
@action
|
||||
downloadFormattedExport() {
|
||||
const fileName =
|
||||
this.selectedExportOption === this.exportOptionsMap.terraform
|
||||
? 'grant-strings.tf'
|
||||
: 'grant-strings.hcl';
|
||||
const blob = new Blob([this.formattedExport], { type: 'text/plain' });
|
||||
|
||||
const url = URL.createObjectURL(blob);
|
||||
const a = document.createElement('a');
|
||||
|
||||
a.href = url;
|
||||
a.download = fileName;
|
||||
const flyoutElement = document.getElementById('export-options-flyout');
|
||||
flyoutElement.appendChild(a);
|
||||
a.click();
|
||||
flyoutElement.removeChild(a);
|
||||
URL.revokeObjectURL(url);
|
||||
}
|
||||
}
|
||||
@@ -24,9 +24,9 @@ export default class GrantStringFormatsIndex extends Component {
|
||||
[this.stringFormatTypes.resourceType]:
|
||||
`type=<${this.intl.t('resources.role.edit-grants.string-formats.insert-resource-types')}>;actions=<${this.intl.t('resources.role.edit-grants.string-formats.insert-actions')}>`,
|
||||
[this.stringFormatTypes.resource]:
|
||||
`id=<${this.intl.t('resources.role.edit-grants.string-formats.insert-resource-ids')}>;actions=<${this.intl.t('resources.role.edit-grants.string-formats.insert-actions')}>`,
|
||||
`ids=<${this.intl.t('resources.role.edit-grants.string-formats.insert-resource-ids')}>;actions=<${this.intl.t('resources.role.edit-grants.string-formats.insert-actions')}>`,
|
||||
[this.stringFormatTypes.pinnedId]:
|
||||
`id=<${this.intl.t('resources.role.edit-grants.string-formats.insert-id')}>;type=<${this.intl.t('resources.role.edit-grants.string-formats.insert-resource-types')}>;actions=<${this.intl.t('resources.role.edit-grants.string-formats.insert-actions')}>`,
|
||||
`ids=<${this.intl.t('resources.role.edit-grants.string-formats.insert-id')}>;type=<${this.intl.t('resources.role.edit-grants.string-formats.insert-resource-types')}>;actions=<${this.intl.t('resources.role.edit-grants.string-formats.insert-actions')}>`,
|
||||
};
|
||||
|
||||
@tracked selectedFormatType = this.stringFormatTypes.resourceType;
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
*/
|
||||
|
||||
import { module, test } from 'qunit';
|
||||
import { visit, click, currentURL } from '@ember/test-helpers';
|
||||
import { visit, click, currentURL, select } from '@ember/test-helpers';
|
||||
import { setupApplicationTest } from 'admin/tests/helpers';
|
||||
import * as selectors from './selectors';
|
||||
import { setRunOptions } from 'ember-a11y-testing/test-support';
|
||||
@@ -76,4 +76,50 @@ module('Acceptance | roles/edit grants', function (hooks) {
|
||||
assert.dom(selectors.MANAGE_DROPDOWN_EDIT_GRANTS).doesNotExist();
|
||||
assert.strictEqual(currentURL(), urls.role);
|
||||
});
|
||||
|
||||
test('user can export a terraform formatted grant string', async function (assert) {
|
||||
setRunOptions({
|
||||
rules: {
|
||||
'color-contrast': {
|
||||
// [ember-a11y-ignore]: axe rule "color-contrast" automatically ignored on 2026-03-17
|
||||
enabled: false,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
await visit(urls.role);
|
||||
|
||||
await click(selectors.MANAGE_DROPDOWN_ROLES);
|
||||
await click(selectors.MANAGE_DROPDOWN_EDIT_GRANTS);
|
||||
await click(selectors.EXPORT_GRANTS_BTN);
|
||||
|
||||
assert.dom(selectors.EXPORT_OPTIONS_FLYOUT).isVisible();
|
||||
assert.dom(selectors.EXPORT_FORMAT_SELECT).hasValue('terraform');
|
||||
// TODO: Update to check for the actual formatted grant string.
|
||||
assert
|
||||
.dom(selectors.FORMATTED_EXPORT_CODE_BLOCK)
|
||||
.includesText('grant_strings');
|
||||
});
|
||||
|
||||
test('user can export a hcl formatted grant string', async function (assert) {
|
||||
setRunOptions({
|
||||
rules: {
|
||||
'color-contrast': {
|
||||
// [ember-a11y-ignore]: axe rule "color-contrast" automatically ignored on 2026-03-17
|
||||
enabled: false,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
await visit(urls.role);
|
||||
|
||||
await click(selectors.MANAGE_DROPDOWN_ROLES);
|
||||
await click(selectors.MANAGE_DROPDOWN_EDIT_GRANTS);
|
||||
await click(selectors.EXPORT_GRANTS_BTN);
|
||||
await select(selectors.EXPORT_FORMAT_SELECT, 'native-hcl');
|
||||
|
||||
assert.dom(selectors.EXPORT_OPTIONS_FLYOUT).isVisible();
|
||||
assert.dom(selectors.EXPORT_FORMAT_SELECT).hasValue('native-hcl');
|
||||
// TODO: Update to check for the actual formatted grant string.
|
||||
});
|
||||
});
|
||||
@@ -60,6 +60,12 @@ export const FIELD_GRANT = 'form:nth-child(2) [name="grant"]';
|
||||
export const FIELD_GRANT_VALUE = 'ids=123,action=delete';
|
||||
export const FIELD_GRANT_REMOVE_BTN = 'form:nth-child(2) button[type="button"]';
|
||||
export const GRANTS_FORM = 'form:nth-child(2)';
|
||||
export const EXPORT_OPTIONS_FLYOUT = '[data-test-export-options-flyout]';
|
||||
export const EXPORT_FORMAT_SELECT =
|
||||
'[data-test-export-options-flyout] [name="export-format"]';
|
||||
export const FORMATTED_EXPORT_CODE_BLOCK =
|
||||
'[data-test-export-options-flyout] .hds-code-block code';
|
||||
// We need to use a different selector for the grants form because the
|
||||
// grants form has multiple submit buttons
|
||||
export const SAVE_BTN = '.rose-form-actions [type=submit]';
|
||||
export const EXPORT_GRANTS_BTN = '[data-test-export-grants-btn]';
|
||||
@@ -41,7 +41,7 @@ module(
|
||||
assert.dom(PINNED_ID_OPTION).isNotChecked();
|
||||
assert
|
||||
.dom(STRING_FORMAT_FIELD)
|
||||
.hasValue('id=<insert resource ids>;actions=<insert actions>');
|
||||
.hasValue('ids=<insert resource ids>;actions=<insert actions>');
|
||||
});
|
||||
|
||||
test('it renders string format when type is changed to pinned id', async function (assert) {
|
||||
@@ -55,7 +55,7 @@ module(
|
||||
assert
|
||||
.dom(STRING_FORMAT_FIELD)
|
||||
.hasValue(
|
||||
'id=<insert id>;type=<insert resource types>;actions=<insert actions>',
|
||||
'ids=<insert id>;type=<insert resource types>;actions=<insert actions>',
|
||||
);
|
||||
});
|
||||
},
|
||||
|
||||
Reference in new issue
Block a user