diff --git a/addons/core/addon/components/dropdown/index.hbs b/addons/core/addon/components/dropdown/index.hbs index f52926e90..36fbe29d2 100644 --- a/addons/core/addon/components/dropdown/index.hbs +++ b/addons/core/addon/components/dropdown/index.hbs @@ -13,6 +13,7 @@ @color='secondary' @text={{@name}} @count={{@checkedItems.length}} + {{!-- {{on 'click' this.open}} --}} /> {{#if @isSearchable}} diff --git a/addons/core/addon/components/dropdown/index.js b/addons/core/addon/components/dropdown/index.js index ba84cd198..779d90e27 100644 --- a/addons/core/addon/components/dropdown/index.js +++ b/addons/core/addon/components/dropdown/index.js @@ -70,6 +70,16 @@ export default class DropdownComponent extends Component { onClose(); } + /** + * Sets selectedItems to checkedItems when the user opens + * the dropdown and potentially has made changes + * outside of the scope of dropdown + */ + @action + open() { + this.selectedItems = [...this.args.checkedItems]; + } + /** * Custom close method for dropdown that resets selectedItems * to checkedItems when the user closes the dropdown diff --git a/addons/core/addon/components/filter-tags/index.hbs b/addons/core/addon/components/filter-tags/index.hbs new file mode 100644 index 000000000..89839fe7e --- /dev/null +++ b/addons/core/addon/components/filter-tags/index.hbs @@ -0,0 +1,21 @@ +{{#if this.filters}} +
+ {{t + 'titles.filters-applied' + }} + {{#each this.filters as |filter|}} + + {{/each}} + +
+{{/if}} \ No newline at end of file diff --git a/addons/core/addon/components/filter-tags/index.js b/addons/core/addon/components/filter-tags/index.js new file mode 100644 index 000000000..0bacd1c54 --- /dev/null +++ b/addons/core/addon/components/filter-tags/index.js @@ -0,0 +1,67 @@ +import Component from '@glimmer/component'; +import { inject as service } from '@ember/service'; +import { action } from '@ember/object'; +import { assert } from '@ember/debug'; + +export default class FilterTagsIndexComponent extends Component { + // =services + + @service router; + + // =attributes + + get filters() { + return Object.entries(this.args.filters).flatMap(([key, value]) => { + assert(`Tags must be an array for key ${key}`, Array.isArray(value)); + const paramsSet = new Set(this.getCurrentQueryParams(key)); + const filters = value.filter((item) => paramsSet.has(item.id)); + + return filters.map((item) => ({ + id: item.id, + name: item.name, + type: key, + })); + }); + } + + // =methods + + getCurrentQueryParams(key) { + let queryParamValue; + try { + queryParamValue = JSON.parse(this.router.currentRoute.queryParams[key]); + } catch { + // We should never get an error as we are always asserting the queryParam to an array + queryParamValue = []; + } + return queryParamValue; + } + + /** + * Clears a single filter from queryParams for current route + * @param {object} tag + */ + @action + removeFilter(tag) { + const queryParamValue = this.getCurrentQueryParams(tag.type); + + const queryParams = { + [tag.type]: queryParamValue.filter((item) => item !== tag.id), + }; + + this.router.replaceWith({ queryParams }); + } + + /** + * Clears all filters based on provided queryParams + */ + @action + clearAllFilters() { + const queryParams = Object.keys(this.args.filters).reduce((params, key) => { + params[key] = []; + return params; + }, {}); + + this.router.replaceWith({ queryParams }); + } +} diff --git a/addons/core/addon/styles/addon.scss b/addons/core/addon/styles/addon.scss new file mode 100644 index 000000000..445b3444f --- /dev/null +++ b/addons/core/addon/styles/addon.scss @@ -0,0 +1,22 @@ +/** + * Copyright (c) HashiCorp, Inc. + * SPDX-License-Identifier: MPL-2.0 + */ + +.filters-applied { + display: flex; + flex-wrap: wrap; + align-items: center; + margin-bottom: 1rem; + gap: 0.5rem; + + .filter-tag { + max-width: 200px; + + .hds-tag__text { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } + } +} diff --git a/addons/core/app/components/filter-tags/index.js b/addons/core/app/components/filter-tags/index.js new file mode 100644 index 000000000..4e722476f --- /dev/null +++ b/addons/core/app/components/filter-tags/index.js @@ -0,0 +1 @@ +export { default } from 'core/components/filter-tags/index'; diff --git a/addons/core/app/styles/app.scss b/addons/core/app/styles/app.scss new file mode 100644 index 000000000..3af54990c --- /dev/null +++ b/addons/core/app/styles/app.scss @@ -0,0 +1,4 @@ +/** + * Copyright (c) HashiCorp, Inc. + * SPDX-License-Identifier: MPL-2.0 + */ diff --git a/addons/core/package.json b/addons/core/package.json index aebdde457..981aae0bb 100644 --- a/addons/core/package.json +++ b/addons/core/package.json @@ -37,12 +37,14 @@ "ember-browser-services": "^4.0.3", "ember-cli-babel": "^7.26.11", "ember-cli-htmlbars": "^6.2.0", + "ember-cli-sass": "^11.0.1", "ember-feature-flags": "^6.0.0", "ember-intl": "^6.0.0-beta.6", "ember-loading": "^2.0.0", "ember-truth-helpers": "^3.0.0", "filesize": "^10.0.7", - "intl-unofficial-duration-unit-format": "^3.1.0" + "intl-unofficial-duration-unit-format": "^3.1.0", + "sass": "^1.69.5" }, "devDependencies": { "@babel/eslint-parser": "^7.21.3", @@ -82,6 +84,7 @@ "qunit": "^2.19.4", "qunit-dom": "^2.0.0", "rose": "*", + "sinon": "^17.0.1", "webpack": "^5.78.0" }, "peerDependencies": { diff --git a/addons/core/tests/dummy/app/styles/app.css b/addons/core/tests/dummy/app/styles/app.css index 25bf4df27..f350ec90f 100644 --- a/addons/core/tests/dummy/app/styles/app.css +++ b/addons/core/tests/dummy/app/styles/app.css @@ -3,3 +3,4 @@ * SPDX-License-Identifier: MPL-2.0 */ +@import 'addon/styles/addons.scss'; diff --git a/addons/core/tests/integration/components/filter-tags/index-test.js b/addons/core/tests/integration/components/filter-tags/index-test.js new file mode 100644 index 000000000..45e9cf820 --- /dev/null +++ b/addons/core/tests/integration/components/filter-tags/index-test.js @@ -0,0 +1,29 @@ +import { module, test } from 'qunit'; +import { setupRenderingTest } from 'ember-qunit'; +import { setupIntl } from 'ember-intl/test-support'; +import { render } from '@ember/test-helpers'; +import { hbs } from 'ember-cli-htmlbars'; +import sinon from 'sinon'; + +module('Integration | Component | filter-tags/index', function (hooks) { + setupRenderingTest(hooks); + setupIntl(hooks); + + hooks.afterEach(function () { + sinon.restore(); + }); + + test('it renders', async function (assert) { + const router = this.owner.lookup('service:router'); + sinon.stub(router, 'currentRoute').value({ + queryParams: { + scopes: JSON.stringify(['1']), + }, + }); + + this.set('filter', { scopes: [{ id: '1', name: 'Project 1' }] }); + await render(hbs``); + + assert.dom('.hds-tag__text').hasText('Project 1'); + }); +}); diff --git a/addons/core/translations/actions/en-us.yaml b/addons/core/translations/actions/en-us.yaml index c9fff6205..3bb2884c3 100644 --- a/addons/core/translations/actions/en-us.yaml +++ b/addons/core/translations/actions/en-us.yaml @@ -44,6 +44,7 @@ toggle-secret: Toggle secret visibility minimize: Minimize fullscreen: Fullscreen clear-all-filters: Clear all filters +clear-filters: Clear filters learn-more: Learn more refresh: Refresh remove-resources: Remove resources and save diff --git a/addons/core/translations/en-us.yaml b/addons/core/translations/en-us.yaml index f451416e2..63bcf9866 100644 --- a/addons/core/translations/en-us.yaml +++ b/addons/core/translations/en-us.yaml @@ -39,6 +39,7 @@ titles: choose-an-option: Choose an option feature-edition: 'Edition: {edition}' no-results-found: No results found + filters-applied: 'Filters Applied:' unit: Unit year: '{numberOfYears, plural, =1 {# year} other {# years}}' days: '{days, plural, =1 {# day} other {# days}}' diff --git a/addons/rose/app/components/rose/filter-tags/index.js b/addons/rose/app/components/rose/filter-tags/index.js new file mode 100644 index 000000000..090ac627f --- /dev/null +++ b/addons/rose/app/components/rose/filter-tags/index.js @@ -0,0 +1 @@ +export { default } from 'rose/components/rose/filter-tags/index'; diff --git a/addons/rose/package.json b/addons/rose/package.json index 59b0a4318..73419cd70 100644 --- a/addons/rose/package.json +++ b/addons/rose/package.json @@ -44,7 +44,7 @@ "ember-radio-button": "github:yapplabs/ember-radio-button.git#b2ab65f", "ember-uuid": "^2.1.0", "jsonlint": "^1.6.3", - "sass": "^1.55.0" + "sass": "^1.69.5" }, "devDependencies": { "@babel/core": "^7.18.5", diff --git a/ui/desktop/app/controllers/scopes/scope/projects/targets/index.js b/ui/desktop/app/controllers/scopes/scope/projects/targets/index.js index c79995103..63a7ba9aa 100644 --- a/ui/desktop/app/controllers/scopes/scope/projects/targets/index.js +++ b/ui/desktop/app/controllers/scopes/scope/projects/targets/index.js @@ -103,6 +103,20 @@ export default class ScopesScopeProjectsTargetsIndexController extends Controlle return orderBy(sessions, 'created_time', 'desc'); } + /** + * Returns object of filters to be used for displaying selected filters + * @returns {object} + */ + get filters() { + return { + scopes: this.model.projects, + availableSessions: this.availableSessionOptions, + type: [], + }; + } + + // =methods + /** * Quick connect method used to call main connect method and handle * connection errors unique to this route @@ -202,6 +216,11 @@ export default class ScopesScopeProjectsTargetsIndexController extends Controlle this.page = 1; } + @action + toggleSessionsFlyout() { + this.sessionsFlyoutActive = !this.sessionsFlyoutActive; + } + /** * Sets the scopes query param to value of selectedScopes * to trigger a query and closes the dropdown diff --git a/ui/desktop/app/templates/scopes/scope/projects/targets/index.hbs b/ui/desktop/app/templates/scopes/scope/projects/targets/index.hbs index 9f36cce04..a5a8ed35a 100644 --- a/ui/desktop/app/templates/scopes/scope/projects/targets/index.hbs +++ b/ui/desktop/app/templates/scopes/scope/projects/targets/index.hbs @@ -62,6 +62,7 @@ {{! TODO: Target type filter}} + {{#if @model.targets}} =5", "@sinonjs/fake-timers@^9.1.2": version "9.1.2" resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-9.1.2.tgz#4eaab737fab77332ab132d396a3c0d364bd0ea8c" @@ -5348,6 +5362,20 @@ dependencies: "@sinonjs/commons" "^1.7.0" +"@sinonjs/fake-timers@^10.0.2": + version "10.3.0" + resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz#55fdff1ecab9f354019129daf4df0dd4d923ea66" + integrity sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA== + dependencies: + "@sinonjs/commons" "^3.0.0" + +"@sinonjs/fake-timers@^11.2.2": + version "11.2.2" + resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-11.2.2.tgz#50063cc3574f4a27bd8453180a04171c85cc9699" + integrity sha512-G2piCSxQ7oWOxwGSAyFHfPIsyeJGXYtc6mFbnFA+kRXkiEnTl8c/8jul2S329iFBnDI9HGoeWWAZvuvOkZccgw== + dependencies: + "@sinonjs/commons" "^3.0.0" + "@sinonjs/fake-timers@^8.0.1": version "8.1.0" resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-8.1.0.tgz#3fdc2b6cb58935b21bfb8d1625eb1300484316e7" @@ -5364,6 +5392,15 @@ lodash.get "^4.4.2" type-detect "^4.0.8" +"@sinonjs/samsam@^8.0.0": + version "8.0.0" + resolved "https://registry.yarnpkg.com/@sinonjs/samsam/-/samsam-8.0.0.tgz#0d488c91efb3fa1442e26abea81759dfc8b5ac60" + integrity sha512-Bp8KUVlLp8ibJZrnvq2foVhP0IVX2CIprMJPK0vqGqgrDa0OHVKeZyBykqskkrdxV6yKBPmGasO8LVjAKR3Gew== + dependencies: + "@sinonjs/commons" "^2.0.0" + lodash.get "^4.4.2" + type-detect "^4.0.8" + "@sinonjs/text-encoding@^0.7.1": version "0.7.1" resolved "https://registry.yarnpkg.com/@sinonjs/text-encoding/-/text-encoding-0.7.1.tgz#8da5c6530915653f3a1f38fd5f101d8c3f8079c5" @@ -8286,9 +8323,9 @@ caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001502, caniuse-lite@^1.0.30001517, can integrity sha512-xrE//a3O7TP0vaJ8ikzkD2c2NgcVUvsEe2IvFTntV4Yd1Z9FVzh+gW+enX96L0psrbaFMcVcH2l90xNuGDWc8w== caniuse-lite@^1.0.30001565: - version "1.0.30001570" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001570.tgz#b4e5c1fa786f733ab78fc70f592df6b3f23244ca" - integrity sha512-+3e0ASu4sw1SWaoCtvPeyXp+5PsjigkSt8OXZbF9StH5pQWbxEjLAZE3n8Aup5udop1uRiKA7a4utUk/uoSpUw== + version "1.0.30001571" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001571.tgz#4182e93d696ff42930f4af7eba515ddeb57917ac" + integrity sha512-tYq/6MoXhdezDLFZuCO/TKboTzuQ/xR5cFdgXPfDtM7/kchBO3b4VWghE/OAi/DV7tTdhmLjZiZBZi1fA/GheQ== capture-exit@^2.0.0: version "2.0.0" @@ -9832,9 +9869,9 @@ electron-to-chromium@^1.4.535: integrity sha512-t2ZP4AcGE0iKCCQCBx/K2426crYdxD3YU6l0uK2EO3FZH0pbC4pFz/sZm2ruZsND6hQBTcDWWlo/MLpiOdif5g== electron-to-chromium@^1.4.601: - version "1.4.613" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.613.tgz#529e4fc65576ecfd055d7d4619fade4fac446af2" - integrity sha512-r4x5+FowKG6q+/Wj0W9nidx7QO31BJwmR2uEo+Qh3YLGQ8SbBAFuDFpTxzly/I2gsbrFwBuIjrMp423L3O5U3w== + version "1.4.615" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.615.tgz#b1c41839962d2e4e63dca05519da9040e34848c2" + integrity sha512-/bKPPcgZVUziECqDc+0HkT87+0zhaWSZHNXqF8FLd2lQcptpmUFwoCSWjCdOng9Gdq+afKArPdEg/0ZW461Eng== elliptic@^6.5.3, elliptic@^6.5.4: version "6.5.4" @@ -16849,6 +16886,17 @@ nise@^5.1.1: just-extend "^4.0.2" path-to-regexp "^1.7.0" +nise@^5.1.5: + version "5.1.5" + resolved "https://registry.yarnpkg.com/nise/-/nise-5.1.5.tgz#f2aef9536280b6c18940e32ba1fbdc770b8964ee" + integrity sha512-VJuPIfUFaXNRzETTQEEItTOP8Y171ijr+JLq42wHes3DiryR8vT+1TXQW/Rx8JNUhyYYWyIvjXTU6dOhJcs9Nw== + dependencies: + "@sinonjs/commons" "^2.0.0" + "@sinonjs/fake-timers" "^10.0.2" + "@sinonjs/text-encoding" "^0.7.1" + just-extend "^4.0.2" + path-to-regexp "^1.7.0" + no-case@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/no-case/-/no-case-3.0.4.tgz#d361fd5c9800f558551a8369fc0dcd4662b6124d" @@ -19163,15 +19211,6 @@ sane@^5.0.1: minimist "^1.1.1" walker "~1.0.5" -sass@^1.55.0: - version "1.55.0" - resolved "https://registry.yarnpkg.com/sass/-/sass-1.55.0.tgz#0c4d3c293cfe8f8a2e8d3b666e1cf1bff8065d1c" - integrity sha512-Pk+PMy7OGLs9WaxZGJMn7S96dvlyVBwwtToX895WmCpAOr5YiJYEUJfiJidMuKb613z2xNWcXCHEuOvjZbqC6A== - dependencies: - chokidar ">=3.0.0 <4.0.0" - immutable "^4.0.0" - source-map-js ">=0.6.2 <2.0.0" - sass@^1.69.5: version "1.69.5" resolved "https://registry.yarnpkg.com/sass/-/sass-1.69.5.tgz#23e18d1c757a35f2e52cc81871060b9ad653dfde" @@ -19469,6 +19508,18 @@ sinon@^14.0.0: nise "^5.1.1" supports-color "^7.2.0" +sinon@^17.0.1: + version "17.0.1" + resolved "https://registry.yarnpkg.com/sinon/-/sinon-17.0.1.tgz#26b8ef719261bf8df43f925924cccc96748e407a" + integrity sha512-wmwE19Lie0MLT+ZYNpDymasPHUKTaZHUH/pKEubRXIzySv9Atnlw+BUMGCzWgV7b7wO+Hw6f1TEOr0IUnmU8/g== + dependencies: + "@sinonjs/commons" "^3.0.0" + "@sinonjs/fake-timers" "^11.2.2" + "@sinonjs/samsam" "^8.0.0" + diff "^5.1.0" + nise "^5.1.5" + supports-color "^7.2.0" + sisteransi@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/sisteransi/-/sisteransi-1.0.5.tgz#134d681297756437cc05ca01370d3a7a571075ed"