diff --git a/addons/api/addon/services/resource-filter-store.js b/addons/api/addon/services/resource-filter-store.js
index 8bd4130ae..9dbca0f5e 100644
--- a/addons/api/addon/services/resource-filter-store.js
+++ b/addons/api/addon/services/resource-filter-store.js
@@ -52,7 +52,7 @@ export class ResourceFilter {
* @return {string}
*/
equals(key, value) {
- return `"/item/${key}" == "${value}"`;
+ return value !== null ? `"/item/${key}" == "${value}"` : null;
}
/**
@@ -76,7 +76,7 @@ export class ResourceFilter {
* @return {string}
*/
parenthetical(expression) {
- return `(${expression})`;
+ return expression ? `(${expression})` : null;
}
}
diff --git a/addons/api/mirage/config.js b/addons/api/mirage/config.js
index 0fe8fb6b1..da0632da8 100644
--- a/addons/api/mirage/config.js
+++ b/addons/api/mirage/config.js
@@ -55,8 +55,9 @@ export default function () {
this.get(
'/auth-methods',
- ({ authMethods }, { queryParams: { scope_id: scopeId } }) => {
- return authMethods.where({ scopeId });
+ ({ authMethods }, { queryParams: { scope_id: scopeId, filter } }) => {
+ const resultSet = authMethods.where({ scopeId });
+ return resultSet.filter(makeBooleanFilter(filter));
}
);
this.post('/auth-methods');
diff --git a/addons/api/mirage/scenarios/default.js b/addons/api/mirage/scenarios/default.js
index 965234b5d..9291f8a3e 100644
--- a/addons/api/mirage/scenarios/default.js
+++ b/addons/api/mirage/scenarios/default.js
@@ -22,7 +22,7 @@ export default function (server) {
// Auth
const globalAuthMethods = server.createList(
'auth-method',
- 4,
+ 1,
{ scope: globalScope },
'withAccountsAndUsersAndManagedGroups'
);
diff --git a/addons/core/addon/decorators/resource-filter-param.js b/addons/core/addon/decorators/resource-filter-param.js
index 61978ee06..b8395a20f 100644
--- a/addons/core/addon/decorators/resource-filter-param.js
+++ b/addons/core/addon/decorators/resource-filter-param.js
@@ -46,6 +46,9 @@ export function resourceFilterParam(allowedValues, defaultValue) {
// If the route has no query params specified yet, declare them.
if (!Object.keys(target.queryParams).length) target.queryParams = {};
+ // If the route has no resource filters list specified yet, create one.
+ if (!target.resourceFilterParams) target.resourceFilterParams = [];
+
// Add the resource filter query parameter to the route. Resource filter
// param changes refresh the model but do not contribute to browser history.
// See https://guides.emberjs.com/release/routing/query-params/
@@ -54,6 +57,9 @@ export function resourceFilterParam(allowedValues, defaultValue) {
replace: true,
};
+ // Note the name of this filter param.
+ target.resourceFilterParams.push(name);
+
// Store the allowed values for future lookup
// (via `ResourceFilterParamHelper`).
target[filterAllowedValuesKey] = allowedValues;
diff --git a/addons/core/addon/helpers/has-resource-filter-param-selections.js b/addons/core/addon/helpers/has-resource-filter-param-selections.js
new file mode 100644
index 000000000..6f6015078
--- /dev/null
+++ b/addons/core/addon/helpers/has-resource-filter-param-selections.js
@@ -0,0 +1,73 @@
+import Helper from '@ember/component/helper';
+import { inject as service } from '@ember/service';
+import { getOwner } from '@ember/application';
+import { action } from '@ember/object';
+
+/**
+ * Use this helper to determine if a resource filter for a route has any
+ * currently selected value.
+ *
+ * @see resourceFilterParam (decorator)
+ *
+ * @example
+ *
+ * {{#if (has-resource-filter-param-selections 'route.name' 'field')}}
+ * Field has filter selections.
+ * {{/if}}
+ */
+export default class HasResourceFilterParamSelectionsHelper extends Helper {
+ // =services
+
+ @service router;
+
+ // =methods
+
+ // =lifecycle management methods
+
+ /**
+ * Recompute this helper when the route changes.
+ */
+ constructor() {
+ super(...arguments);
+ this.router.on('routeDidChange', this.routeDidChange);
+ }
+
+ /**
+ * Stop listening to route events when this helper is destroyed.
+ */
+ willDestroy() {
+ this.router.off('routeDidChange', this.routeDidChange);
+ super.willDestroy();
+ }
+
+ // =compute method
+
+ /**
+ * Returns true if the specified route has a value set for
+ * any resource filter.
+ * @param {string} routeName
+ * @return {boolean}
+ */
+ compute([routeName]) {
+ const owner = getOwner(this);
+ const route = owner.lookup(`route:${routeName}`);
+ const names = route.resourceFilterParams;
+ const selectedValues = names.map((name) => route[name] || null).flat();
+ const anyTruthy = selectedValues.reduce(
+ (previousValue, currentValue) => previousValue || currentValue,
+ false
+ );
+ return Boolean(anyTruthy);
+ }
+
+ // =actions
+
+ /**
+ * Triggers a recompute (see above) when the route changes, especially when
+ * route query params change.
+ */
+ @action
+ routeDidChange() {
+ this.recompute();
+ }
+}
diff --git a/addons/core/app/helpers/has-resource-filter-param-selections.js b/addons/core/app/helpers/has-resource-filter-param-selections.js
new file mode 100644
index 000000000..b6ab1bc86
--- /dev/null
+++ b/addons/core/app/helpers/has-resource-filter-param-selections.js
@@ -0,0 +1 @@
+export { default } from 'core/helpers/has-resource-filter-param-selections';
diff --git a/addons/core/package.json b/addons/core/package.json
index b51acc24b..fd10d6d9f 100644
--- a/addons/core/package.json
+++ b/addons/core/package.json
@@ -37,10 +37,10 @@
"ember-intl": "^5.7.0",
"ember-loading": "^0.4.0",
"ember-route-action-helper": "^2.0.8",
+ "ember-truth-helpers": "^3.0.0",
"rose": "*"
},
"devDependencies": {
- "js-bexpr": "hashicorp/js-bexpr#9b4a4b54d85eba67fdfc0990133d1518d890b1e1",
"@ember/optional-features": "^2.0.0",
"@ember/test-helpers": "^2.4.2",
"@glimmer/component": "^1.0.4",
@@ -73,6 +73,7 @@
"eslint-plugin-prettier": "^4.0.0",
"eslint-plugin-qunit": "^6.2.0",
"faker": "^5.5.3",
+ "js-bexpr": "hashicorp/js-bexpr#9b4a4b54d85eba67fdfc0990133d1518d890b1e1",
"loader.js": "^4.7.0",
"npm-run-all": "^4.1.5",
"prettier": "^2.4.0",
diff --git a/addons/core/translations/actions/en-us.yaml b/addons/core/translations/actions/en-us.yaml
index 144ced636..118c5f601 100644
--- a/addons/core/translations/actions/en-us.yaml
+++ b/addons/core/translations/actions/en-us.yaml
@@ -35,3 +35,4 @@ close: Close
toggle-secret: Toggle secret visibility
minimize: Minimize
fullscreen: Fullscreen
+clear-all-filters: Clear all filters
diff --git a/addons/core/translations/en-us.yaml b/addons/core/translations/en-us.yaml
index 8f98f633c..73a720d62 100644
--- a/addons/core/translations/en-us.yaml
+++ b/addons/core/translations/en-us.yaml
@@ -29,11 +29,13 @@ titles:
welcome-to-boundary: Welcome to Boundary
label: Label
value: Value
+ no-results: No Results
descriptions:
empty-set: There are no items to display yet. You may be able to add items or try back later.
origin-initialization: To get started, please enter your origin URL
create-but-not-list: You do not have permission to list {resource}, but you can create new instances.
neither-list-nor-create: You do not have permission to list or create {resource}.
+ no-results: We found no matching records.
questions:
delete-confirm: Are you sure you want to delete this resource?
remove-confirm: Are you sure you want to remove this association?
diff --git a/addons/rose/addon/components/rose/dropdown/index.hbs b/addons/rose/addon/components/rose/dropdown/index.hbs
index 5d02e87e8..d3679b3a6 100644
--- a/addons/rose/addon/components/rose/dropdown/index.hbs
+++ b/addons/rose/addon/components/rose/dropdown/index.hbs
@@ -18,6 +18,9 @@
{{@text}}
+ {{#if @count}}
+ {{@count}}
+ {{/if}}
diff --git a/addons/rose/addon/components/rose/dropdown/index.stories.mdx b/addons/rose/addon/components/rose/dropdown/index.stories.mdx
index 1ee5d754a..625447e19 100644
--- a/addons/rose/addon/components/rose/dropdown/index.stories.mdx
+++ b/addons/rose/addon/components/rose/dropdown/index.stories.mdx
@@ -1,6 +1,6 @@
import { Meta, Story, Preview } from '@storybook/addon-docs/blocks';
import { hbs } from 'ember-cli-htmlbars';
-import { boolean, object } from '@storybook/addon-knobs';
+import { number, boolean, object } from '@storybook/addon-knobs';
import { action } from '@storybook/addon-actions';
@@ -12,7 +12,7 @@ A simple dropdown component with support for links and buttons.
{{
template: hbs`
-
+
Menu item
Menu item
@@ -24,6 +24,7 @@ A simple dropdown component with support for links and buttons.
`,
context: {
+ count: number('count', 0),
showCaret: boolean('showCaret', true),
},
}}
diff --git a/addons/rose/addon/styles/rose/components/dropdown/_dropdown.scss b/addons/rose/addon/styles/rose/components/dropdown/_dropdown.scss
index b8f3450f4..62a8f3d36 100644
--- a/addons/rose/addon/styles/rose/components/dropdown/_dropdown.scss
+++ b/addons/rose/addon/styles/rose/components/dropdown/_dropdown.scss
@@ -39,6 +39,11 @@ $chevronGrey: url("data:image/svg+xml;utf-8,`);
+ assert.equal(find('.rose-badge').textContent.trim(), '2');
+ });
+
test('it supports an icon', async function (assert) {
await render(
hbs``
diff --git a/ui/admin/app/routes/scopes/scope/auth-methods.js b/ui/admin/app/routes/scopes/scope/auth-methods.js
index 687c5ef81..e83a95911 100644
--- a/ui/admin/app/routes/scopes/scope/auth-methods.js
+++ b/ui/admin/app/routes/scopes/scope/auth-methods.js
@@ -4,6 +4,7 @@ import { action } from '@ember/object';
import loading from 'ember-loading/decorator';
import { confirm } from 'core/decorators/confirm';
import { notifySuccess, notifyError } from 'core/decorators/notify';
+import { resourceFilterParam } from 'core/decorators/resource-filter-param';
export default class ScopesScopeAuthMethodsRoute extends Route {
// =services
@@ -12,6 +13,11 @@ export default class ScopesScopeAuthMethodsRoute extends Route {
@service notify;
@service session;
@service can;
+ @service resourceFilterStore;
+
+ // =attributes
+
+ @resourceFilterParam(['password', 'oidc']) type;
// =methods
@@ -32,7 +38,12 @@ export default class ScopesScopeAuthMethodsRoute extends Route {
if (
this.can.can('list collection', scope, { collection: 'auth-methods' })
) {
- return this.store.query('auth-method', { scope_id });
+ const { type } = this;
+ return this.resourceFilterStore.queryBy(
+ 'auth-method',
+ { type },
+ { scope_id }
+ );
}
}
@@ -185,4 +196,22 @@ export default class ScopesScopeAuthMethodsRoute extends Route {
const value = { from, to };
array.addObject(value);
}
+
+ /**
+ * Sets the specified resource filter field to the specified value.
+ * @param {string} field
+ * @param value
+ */
+ @action
+ filterBy(field, value) {
+ this[field] = value;
+ }
+
+ /**
+ * Clears and filter selections.
+ */
+ @action
+ clearAllFilters() {
+ this.type = [];
+ }
}
diff --git a/ui/admin/app/templates/scopes/scope/auth-methods/index.hbs b/ui/admin/app/templates/scopes/scope/auth-methods/index.hbs
index f4e285c77..d6075980e 100644
--- a/ui/admin/app/templates/scopes/scope/auth-methods/index.hbs
+++ b/ui/admin/app/templates/scopes/scope/auth-methods/index.hbs
@@ -35,8 +35,58 @@
- {{#if @model}}
+ {{#if
+ (or
+ @model
+ (has-resource-filter-param-selections 'scopes.scope.auth-methods')
+ )
+ }}
+
+
+ {{#with
+ (resource-filter-param 'scopes.scope.auth-methods' 'type')
+ as |filter|
+ }}
+
+
+
+
+
+
+
+ {{/with}}
+
+ {{#if
+ (has-resource-filter-param-selections 'scopes.scope.auth-methods')
+ }}
+ {{t 'actions.clear-all-filters'}}
+ {{/if}}
+
+
+ {{/if}}
+
+ {{#if @model}}
{{#if (feature-flag 'primary-auth-method')}}
{{#unless this.hasPrimaryAuthMethod}}
+ {{else if (has-resource-filter-param-selections
+ 'scopes.scope.auth-methods'
+ )}}
+
+
+
+
+ {{t 'descriptions.no-results'}}
+
+
+
+
{{else}}
diff --git a/yarn.lock b/yarn.lock
index 3d3cb02ab..8bcb1a8fc 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -6993,14 +6993,14 @@ browserify-zlib@^0.2.0:
pako "~1.0.5"
browserslist@4.14.2, browserslist@^4.0.0, browserslist@^4.12.0, browserslist@^4.14.5, browserslist@^4.16.5, browserslist@^4.16.6, browserslist@^4.16.7, browserslist@^4.17.3:
- version "4.17.4"
- resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.17.4.tgz#72e2508af2a403aec0a49847ef31bd823c57ead4"
- integrity sha512-Zg7RpbZpIJRW3am9Lyckue7PLytvVxxhJj1CaJVlCWENsGEAOlnlt8X0ZxGRPp7Bt9o8tIRM5SEXy4BCPMJjLQ==
+ version "4.17.6"
+ resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.17.6.tgz#c76be33e7786b497f66cad25a73756c8b938985d"
+ integrity sha512-uPgz3vyRTlEiCv4ee9KlsKgo2V6qPk7Jsn0KAn2OBqbqKo3iNcPEC1Ti6J4dwnz+aIRfEEEuOzC9IBk8tXUomw==
dependencies:
- caniuse-lite "^1.0.30001265"
- electron-to-chromium "^1.3.867"
+ caniuse-lite "^1.0.30001274"
+ electron-to-chromium "^1.3.886"
escalade "^3.1.1"
- node-releases "^2.0.0"
+ node-releases "^2.0.1"
picocolors "^1.0.0"
bser@2.1.1:
@@ -7311,10 +7311,10 @@ caniuse-api@^3.0.0:
lodash.memoize "^4.1.2"
lodash.uniq "^4.5.0"
-caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001109, caniuse-lite@^1.0.30001157, caniuse-lite@^1.0.30001265:
- version "1.0.30001271"
- resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001271.tgz#0dda0c9bcae2cf5407cd34cac304186616cc83e8"
- integrity sha512-BBruZFWmt3HFdVPS8kceTBIguKxu4f99n5JNp06OlPD/luoAMIaIK5ieV5YjnBLH3Nysai9sxj9rpJj4ZisXOA==
+caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001109, caniuse-lite@^1.0.30001157, caniuse-lite@^1.0.30001274:
+ version "1.0.30001279"
+ resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001279.tgz#eb06818da481ef5096a3b3760f43e5382ed6b0ce"
+ integrity sha512-VfEHpzHEXj6/CxggTwSFoZBBYGQfQv9Cf42KPlO79sWXCD1QNKWKsKzFeWL7QpZHJQYAvocqV6Rty1yJMkqWLQ==
capture-exit@^2.0.0:
version "2.0.0"
@@ -9151,10 +9151,10 @@ electron-rebuild@^2.0.3:
tar "^6.0.5"
yargs "^16.0.0"
-electron-to-chromium@^1.3.867:
- version "1.3.884"
- resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.884.tgz#0cd8c3a80271fd84a81f284c60fb3c9ecb33c166"
- integrity sha512-kOaCAa+biA98PwH5BpCkeUeTL6mCeg8p3Q3OhqzPyqhu/5QUnWAN2wr/3IK8xMQxIV76kfoQpP+Bn/wij/jXrg==
+electron-to-chromium@^1.3.886:
+ version "1.3.895"
+ resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.895.tgz#9b0f8f2e32d8283bbb200156fd5d8dfd775f31ed"
+ integrity sha512-9Ww3fB8CWctjqHwkOt7DQbMZMpal2x2reod+/lU4b9axO1XJEDUpPMBxs7YnjLhhqpKXIIB5SRYN/B4K0QpvyQ==
element-resize-detector@^1.2.2:
version "1.2.3"
@@ -10335,6 +10335,13 @@ ember-template-recast@^5.0.1, ember-template-recast@^5.0.3:
tmp "^0.2.1"
workerpool "^6.1.4"
+ember-truth-helpers@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/ember-truth-helpers/-/ember-truth-helpers-3.0.0.tgz#86766bdca4ac9b86bce3d262dff2aabc4a0ea384"
+ integrity sha512-hPKG9QqruAELh0li5xaiLZtr88ioWYxWCXisAWHWE0qCP4a2hgtuMzKUPpiTCkltvKjuqpzTZCU4VhQ+IlRmew==
+ dependencies:
+ ember-cli-babel "^7.22.1"
+
ember-try-config@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/ember-try-config/-/ember-try-config-3.0.0.tgz#012d8c90cae9eb624e2b62040bf7e76a1aa58edc"
@@ -16669,7 +16676,7 @@ node-pre-gyp@^0.11.0:
semver "^5.3.0"
tar "^4"
-node-releases@^2.0.0:
+node-releases@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.1.tgz#3d1d395f204f1f2f29a54358b9fb678765ad2fc5"
integrity sha512-CqyzN6z7Q6aMeF/ktcMVTzhAHCEpf8SOarwpzpf8pNBY2k5/oM34UHldUwp8VKI7uxct2HxSRdJjBaZeESzcxA==