diff --git a/changelog/22502.txt b/changelog/22502.txt
new file mode 100644
index 000000000..b9d21c2ce
--- /dev/null
+++ b/changelog/22502.txt
@@ -0,0 +1,3 @@
+```release-note:improvement
+ui: KV View Secret card will link to list view if input ends in "/"
+```
\ No newline at end of file
diff --git a/ui/app/components/get-credentials-card.js b/ui/app/components/get-credentials-card.js
index 60ff42e33..ddb432eb7 100644
--- a/ui/app/components/get-credentials-card.js
+++ b/ui/app/components/get-credentials-card.js
@@ -32,18 +32,38 @@ export default class GetCredentialsCard extends Component {
@tracked role = '';
@tracked secret = '';
+ constructor() {
+ super(...arguments);
+ this.secret = this.args?.initialValue || '';
+ }
+
+ get buttonText() {
+ if (this.args.type === 'secret') {
+ if (this.secret.endsWith('/')) {
+ return 'View list';
+ }
+ return 'View secret';
+ }
+ return 'Get credentials';
+ }
+
get buttonDisabled() {
return !this.role && !this.secret;
}
@action
- transitionToCredential() {
+ transitionToCredential(evt) {
+ evt.preventDefault();
const role = this.role;
const secret = this.secret;
if (role) {
this.router.transitionTo('vault.cluster.secrets.backend.credentials', role);
}
if (secret) {
+ if (secret.endsWith('/')) {
+ this.router.transitionTo('vault.cluster.secrets.backend.list', secret);
+ return;
+ }
this.router.transitionTo('vault.cluster.secrets.backend.show', secret);
}
}
diff --git a/ui/app/templates/components/get-credentials-card.hbs b/ui/app/templates/components/get-credentials-card.hbs
index 563a5d862..916345403 100644
--- a/ui/app/templates/components/get-credentials-card.hbs
+++ b/ui/app/templates/components/get-credentials-card.hbs
@@ -1,4 +1,4 @@
-
\ No newline at end of file
diff --git a/ui/app/templates/vault/cluster/secrets/backend/list.hbs b/ui/app/templates/vault/cluster/secrets/backend/list.hbs
index e4e314f94..06e4bd11b 100644
--- a/ui/app/templates/vault/cluster/secrets/backend/list.hbs
+++ b/ui/app/templates/vault/cluster/secrets/backend/list.hbs
@@ -26,10 +26,10 @@
@renderInputSearch={{true}}
@title="View secret"
@searchLabel="Secret path"
- @subText="Type the path of the secret you want to read"
+ @subText="Type the path of the secret you want to view. Include a trailing slash to navigate to the list view."
@placeholder="secret/"
- @backend="kv"
@type="secret"
+ @initialValue={{this.baseKey.id}}
/>
@@ -143,7 +143,7 @@
`);
assert.dom('[data-test-get-credentials]').isDisabled();
+ assert.dom('[data-test-get-credentials]').hasText('Get credentials', 'Button has default text');
});
test('it shows button that can be clicked to credentials route when an item is selected', async function (assert) {
@@ -89,6 +90,7 @@ module('Integration | Component | get-credentials-card', function (hooks) {
);
await typeIn('[data-test-search-roles] input', 'test');
assert.dom('[data-test-get-credentials]').isEnabled('submit button enables after typing input text');
+ assert.dom('[data-test-get-credentials]').hasText('View secret', 'Button has view secret CTA');
await click('[data-test-get-credentials]');
assert.propEqual(
this.router.transitionTo.lastCall.args,
@@ -96,4 +98,44 @@ module('Integration | Component | get-credentials-card', function (hooks) {
'transitionTo is called with correct route and secret name'
);
});
+
+ test('it prefills input if initialValue has value', async function (assert) {
+ await render(
+ hbs``
+ );
+ assert
+ .dom('[data-test-component="search-select"]')
+ .doesNotExist('does not render search select component');
+ assert.dom('[data-test-search-roles] input').hasValue('hello/', 'pre-fills search input');
+ assert.dom('[data-test-get-credentials]').isEnabled('submit button is enabled at render');
+ assert.dom('[data-test-get-credentials]').hasText('View list', 'Button has list CTA');
+ await typeIn('[data-test-search-roles] input', 'test');
+ assert
+ .dom('[data-test-get-credentials]')
+ .hasText('View secret', 'Button has view secret CTA after input');
+ await click('[data-test-get-credentials]');
+ assert.propEqual(
+ this.router.transitionTo.lastCall.args,
+ ['vault.cluster.secrets.backend.show', 'hello/test'],
+ 'transitionTo is called with correct route and secret name'
+ );
+ });
+
+ test('it goes to list route if input ends in / and type=secret', async function (assert) {
+ await render(
+ hbs``
+ );
+ assert
+ .dom('[data-test-component="search-select"]')
+ .doesNotExist('does not render search select component');
+ await typeIn('[data-test-search-roles] input', 'test/');
+ assert.dom('[data-test-get-credentials]').hasText('View list', 'submit button has list CTA');
+ assert.dom('[data-test-get-credentials]').isEnabled('submit button is enabled at render');
+ await click('[data-test-get-credentials]');
+ assert.propEqual(
+ this.router.transitionTo.lastCall.args,
+ ['vault.cluster.secrets.backend.list', 'test/'],
+ 'transitionTo is called with correct route and secret name'
+ );
+ });
});