diff --git a/ui/app/styles/helper-classes/typography.scss b/ui/app/styles/helper-classes/typography.scss
index 4cde9b2d3c..8466b0c294 100644
--- a/ui/app/styles/helper-classes/typography.scss
+++ b/ui/app/styles/helper-classes/typography.scss
@@ -110,6 +110,9 @@
}
}
+.opacity-050 {
+ opacity: 0.5;
+}
.opacity-060 {
opacity: 0.6;
}
diff --git a/ui/lib/core/addon/components/search-select-placeholder.hbs b/ui/lib/core/addon/components/search-select-placeholder.hbs
index b168b1c9ab..32b9adeea1 100644
--- a/ui/lib/core/addon/components/search-select-placeholder.hbs
+++ b/ui/lib/core/addon/components/search-select-placeholder.hbs
@@ -6,7 +6,7 @@
- {{or @placeholder "Search"}}
+ {{or @placeholder "Search"}}
diff --git a/ui/lib/sync/addon/components/secrets/page/destinations.hbs b/ui/lib/sync/addon/components/secrets/page/destinations.hbs
index 40eba8842c..a1cf7ecbea 100644
--- a/ui/lib/sync/addon/components/secrets/page/destinations.hbs
+++ b/ui/lib/sync/addon/components/secrets/page/destinations.hbs
@@ -28,18 +28,17 @@
class="is-marginless"
data-test-filter="type"
/>
-
+
+
+
diff --git a/ui/lib/sync/addon/components/secrets/page/destinations.ts b/ui/lib/sync/addon/components/secrets/page/destinations.ts
index d4aa6c08b5..77ac790f06 100644
--- a/ui/lib/sync/addon/components/secrets/page/destinations.ts
+++ b/ui/lib/sync/addon/components/secrets/page/destinations.ts
@@ -9,6 +9,7 @@ import { action } from '@ember/object';
import { getOwner } from '@ember/application';
import errorMessage from 'vault/utils/error-message';
import { findDestination, syncDestinations } from 'core/helpers/sync-destinations';
+import { next } from '@ember/runloop';
import type SyncDestinationModel from 'vault/vault/models/sync/destination';
import type RouterService from '@ember/routing/router-service';
@@ -16,6 +17,7 @@ import type StoreService from 'vault/services/store';
import type FlashMessageService from 'vault/services/flash-messages';
import type { EngineOwner } from 'vault/vault/app-types';
import type { SyncDestinationName, SyncDestinationType } from 'vault/vault/helpers/sync-destinations';
+import type Transition from '@ember/routing/transition';
interface Args {
destinations: Array;
@@ -28,15 +30,31 @@ export default class SyncSecretsDestinationsPageComponent extends Component document.getElementById('name-filter')?.focus());
+ }
+ }
+
// typeFilter arg comes in as destination type but we need to pass the destination display name into the SearchSelect
get typeFilterName() {
return findDestination(this.args.typeFilter)?.name;
}
- get destinationNames() {
- return this.args.destinations.map((destination) => ({ id: destination.name, name: destination.name }));
- }
-
get destinationTypes() {
return syncDestinations().map((d) => ({ id: d.name, name: d.type }));
}
@@ -65,9 +83,10 @@ export default class SyncSecretsDestinationsPageComponent extends Component) {
+ onFilterChange(key: string, value: { id: string; name: string }[] | string | undefined) {
+ const queryValue = Array.isArray(value) ? value[0]?.name : value;
this.router.transitionTo('vault.cluster.sync.secrets.destinations', {
- queryParams: { [key]: selectObject[0]?.name },
+ queryParams: { [key]: queryValue },
});
}
diff --git a/ui/lib/sync/addon/components/secrets/page/destinations/create-and-edit.ts b/ui/lib/sync/addon/components/secrets/page/destinations/create-and-edit.ts
index 04e9331bcf..6ba75cfe0e 100644
--- a/ui/lib/sync/addon/components/secrets/page/destinations/create-and-edit.ts
+++ b/ui/lib/sync/addon/components/secrets/page/destinations/create-and-edit.ts
@@ -59,7 +59,7 @@ export default class DestinationsCreateForm extends Component {
@waitFor
*save(event: Event) {
event.preventDefault();
-
+ this.error = '';
// clear out validation warnings
this.modelValidations = null;
const { destination } = this.args;
diff --git a/ui/lib/sync/addon/routes/secrets/destinations/destination/secrets.ts b/ui/lib/sync/addon/routes/secrets/destinations/destination/secrets.ts
index ddf1ca5dd5..9d24413acd 100644
--- a/ui/lib/sync/addon/routes/secrets/destinations/destination/secrets.ts
+++ b/ui/lib/sync/addon/routes/secrets/destinations/destination/secrets.ts
@@ -8,12 +8,24 @@ import { inject as service } from '@ember/service';
import { hash } from 'rsvp';
import type StoreService from 'vault/services/store';
-import SyncDestinationModel from 'vault/vault/models/sync/destination';
+import type SyncDestinationModel from 'vault/vault/models/sync/destination';
+import type SyncAssociationModel from 'vault/vault/models/sync/association';
+import type Controller from '@ember/controller';
interface SyncDestinationSecretsRouteParams {
page: string;
}
+interface SyncDestinationSecretsRouteModel {
+ destination: SyncDestinationModel;
+ associations: SyncAssociationModel[];
+}
+
+interface SyncDestinationSecretsController extends Controller {
+ model: SyncDestinationSecretsRouteModel;
+ page: number | undefined;
+}
+
export default class SyncDestinationSecretsRoute extends Route {
@service declare readonly store: StoreService;
@@ -35,4 +47,10 @@ export default class SyncDestinationSecretsRoute extends Route {
}),
});
}
+
+ resetController(controller: SyncDestinationSecretsController, isExiting: boolean) {
+ if (isExiting) {
+ controller.set('page', undefined);
+ }
+ }
}
diff --git a/ui/lib/sync/addon/routes/secrets/destinations/index.ts b/ui/lib/sync/addon/routes/secrets/destinations/index.ts
index 132417095f..ae8d37cbcc 100644
--- a/ui/lib/sync/addon/routes/secrets/destinations/index.ts
+++ b/ui/lib/sync/addon/routes/secrets/destinations/index.ts
@@ -11,6 +11,7 @@ import type StoreService from 'vault/services/store';
import type RouterService from '@ember/routing/router-service';
import type { ModelFrom } from 'vault/vault/route';
import type SyncDestinationModel from 'vault/vault/models/sync/destination';
+import type Controller from '@ember/controller';
interface SyncSecretsDestinationsIndexRouteParams {
name: string;
@@ -18,6 +19,19 @@ interface SyncSecretsDestinationsIndexRouteParams {
page: string;
}
+interface SyncSecretsDestinationsRouteModel {
+ destinations: SyncDestinationModel[];
+ nameFilter: string | undefined;
+ typeFilter: string | undefined;
+}
+
+interface SyncSecretsDestinationsController extends Controller {
+ model: SyncSecretsDestinationsRouteModel;
+ page: number | undefined;
+ name: number | undefined;
+ type: number | undefined;
+}
+
export default class SyncSecretsDestinationsIndexRoute extends Route {
@service declare readonly store: StoreService;
@service declare readonly router: RouterService;
@@ -35,7 +49,7 @@ export default class SyncSecretsDestinationsIndexRoute extends Route {
};
redirect(model: ModelFrom) {
- if (model.destinations.length === 0) {
+ if (!model.destinations.meta.total) {
this.router.transitionTo('vault.cluster.sync.secrets.overview');
}
}
@@ -43,7 +57,7 @@ export default class SyncSecretsDestinationsIndexRoute extends Route {
filterData(dataset: Array, name: string, type: string): Array {
let filteredDataset = dataset;
const filter = (key: keyof SyncDestinationModel, value: string) => {
- return dataset.filter((model) => {
+ return filteredDataset.filter((model) => {
return model[key].toLowerCase().includes(value.toLowerCase());
});
};
@@ -68,4 +82,14 @@ export default class SyncSecretsDestinationsIndexRoute extends Route {
typeFilter: params.type,
});
}
+
+ resetController(controller: SyncSecretsDestinationsController, isExiting: boolean) {
+ if (isExiting) {
+ controller.setProperties({
+ page: undefined,
+ name: undefined,
+ type: undefined,
+ });
+ }
+ }
}
diff --git a/ui/tests/acceptance/sync/secrets/destinations-test.js b/ui/tests/acceptance/sync/secrets/destinations-test.js
index 017460621f..715d8ab1f5 100644
--- a/ui/tests/acceptance/sync/secrets/destinations-test.js
+++ b/ui/tests/acceptance/sync/secrets/destinations-test.js
@@ -9,7 +9,7 @@ import { setupMirage } from 'ember-cli-mirage/test-support';
import syncScenario from 'vault/mirage/scenarios/sync';
import syncHandlers from 'vault/mirage/handlers/sync';
import authPage from 'vault/tests/pages/auth';
-import { click, visit } from '@ember/test-helpers';
+import { click, visit, fillIn } from '@ember/test-helpers';
import { PAGE } from 'vault/tests/helpers/sync/sync-selectors';
const { searchSelect, filter, listItem } = PAGE;
@@ -29,6 +29,11 @@ module('Acceptance | sync | destinations', function (hooks) {
assert.dom(listItem).exists({ count: 6 }, 'All destinations render');
await click(`${filter('type')} .ember-basic-dropdown-trigger`);
await click(searchSelect.option());
- assert.dom(listItem).exists({ count: 2 }, 'Filtered destinations render');
+ assert.dom(listItem).exists({ count: 2 }, 'Destinations are filtered by type');
+ await fillIn(filter('name'), 'new');
+ assert.dom(listItem).exists({ count: 1 }, 'Destinations are filtered by type and name');
+ await click(searchSelect.removeSelected);
+ await fillIn(filter('name'), 'gcp');
+ assert.dom(listItem).exists({ count: 1 }, 'Destinations are filtered by name');
});
});
diff --git a/ui/tests/helpers/general-selectors.js b/ui/tests/helpers/general-selectors.js
index 670fda1f4c..36ee70c5f9 100644
--- a/ui/tests/helpers/general-selectors.js
+++ b/ui/tests/helpers/general-selectors.js
@@ -14,6 +14,7 @@ export const SELECTORS = {
icon: (name) => `[data-test-icon="${name}"]`,
tab: (name) => `[data-test-tab="${name}"]`,
filter: (name) => `[data-test-filter="${name}"]`,
+ filterInput: '[data-test-filter-input]',
confirmModalInput: '[data-test-confirmation-modal-input]',
confirmButton: '[data-test-confirm-button]',
emptyStateTitle: '[data-test-empty-state-title]',
diff --git a/ui/tests/integration/components/sync/secrets/page/destinations-test.js b/ui/tests/integration/components/sync/secrets/page/destinations-test.js
index 4366bb379b..66bf310a79 100644
--- a/ui/tests/integration/components/sync/secrets/page/destinations-test.js
+++ b/ui/tests/integration/components/sync/secrets/page/destinations-test.js
@@ -7,7 +7,7 @@ import { module, test } from 'qunit';
import { setupRenderingTest } from 'ember-qunit';
import { setupEngine } from 'ember-engines/test-support';
import { setupMirage } from 'ember-cli-mirage/test-support';
-import { render, click } from '@ember/test-helpers';
+import { render, click, fillIn } from '@ember/test-helpers';
import hbs from 'htmlbars-inline-precompile';
import { allowAllCapabilitiesStub } from 'vault/tests/helpers/stubs';
import sinon from 'sinon';
@@ -97,8 +97,7 @@ module('Integration | Component | sync | Page::Destinations', function (hooks) {
);
// NAME FILTER
- await click(`${filter('name')} .ember-basic-dropdown-trigger`);
- await click(searchSelect.option(searchSelect.optionIndex('destination-aws')));
+ await fillIn(filter('name'), 'destination-aws');
assert.deepEqual(
this.transitionStub.lastCall.args,
['vault.cluster.sync.secrets.destinations', { queryParams: { name: 'destination-aws' } }],