diff --git a/wherehows-web/app/components/datasets/owners/suggested-owner-card.ts b/wherehows-web/app/components/datasets/owners/suggested-owner-card.ts new file mode 100644 index 0000000000..83f08f5f6b --- /dev/null +++ b/wherehows-web/app/components/datasets/owners/suggested-owner-card.ts @@ -0,0 +1,18 @@ +import DatasetAuthorComponent from 'wherehows-web/components/dataset-author'; + +export default class DatasetsOwnersSuggestedOwnerCard extends DatasetAuthorComponent { + /** + * Sets the html tag binded to the element generated by this component + */ + tagName = 'div'; + + /** + * Sets the class names binded to the html element generated by this component + */ + classNames = ['dataset-authors-suggested__card']; + + /** + * Sets the class names that are triggered by certain properties on the component evaluating truthy + */ + classNameBindings = []; +} diff --git a/wherehows-web/app/components/datasets/owners/suggested-owners.ts b/wherehows-web/app/components/datasets/owners/suggested-owners.ts new file mode 100644 index 0000000000..74d1750289 --- /dev/null +++ b/wherehows-web/app/components/datasets/owners/suggested-owners.ts @@ -0,0 +1,43 @@ +import Component from '@ember/component'; +import { IOwner } from 'wherehows-web/typings/api/datasets/owners'; +import { computed, get } from '@ember/object'; +import ComputedProperty, { empty } from '@ember/object/computed'; + +export default class DatasetsOwnersSuggestedOwners extends Component { + /** + * Sets the class names binded to the html element generated by this component + */ + classNames = ['dataset-authors-suggested']; + + /** + * Whether or not the component is expanded. If not, users will only see the initial header information + * whereas if expanded then users will see the list of all suggested owners + * @type {boolean} + * @default false + */ + isExpanded = false; + + /** + * Computed based on the owners array, detects whether this array is empty or not + * @type {ComputedProperty} + */ + isEmpty = empty('owners'); + + /** + * Passed in value from parent component, `dataset-authors`, a.k.a. systemGeneratedOwners, this list + * represents a possible list of owners provided by scanning various systems. + * @type {Array} + * @default [] + */ + owners: Array = this.owners || []; + + /** + * For the facepile in the suggestions window header, we do not need tos how all the faces of all the + * possible owners as this could be a large amount. Take only up to the first four to pass into the + * template for rendering + * @type {ComputedProperty>} + */ + facepileOwners: ComputedProperty> = computed('owners', function(this: DatasetsOwnersSuggestedOwners) { + return get(this, 'owners').slice(0, 4); + }); +} diff --git a/wherehows-web/app/controllers/datasets/dataset.ts b/wherehows-web/app/controllers/datasets/dataset.ts index 65e07dc512..66e09d2644 100644 --- a/wherehows-web/app/controllers/datasets/dataset.ts +++ b/wherehows-web/app/controllers/datasets/dataset.ts @@ -66,6 +66,13 @@ export default class DatasetController extends Controller { */ jitAclAccessWhitelist: Array; + /** + * Flag indicating whether or not to show the ownership revamp tab information + * @type {string} + * @memberof DatasetController + */ + showOwnership: string; + /** * Flag indicating the dataset policy is derived from an upstream source * @type {boolean} diff --git a/wherehows-web/app/routes/datasets/dataset.ts b/wherehows-web/app/routes/datasets/dataset.ts index 4eefaf2144..588107b5de 100644 --- a/wherehows-web/app/routes/datasets/dataset.ts +++ b/wherehows-web/app/routes/datasets/dataset.ts @@ -92,18 +92,20 @@ export default class DatasetRoute extends Route { async setupController(this: DatasetRoute, controller: DatasetController, model: IDatasetView) { set(controller, 'model', model); + const configuratorService = get(this, 'configurator'); // TODO: refactor getConfig with conditional types after TS2.8 upgrade to reduce verbosity of annotations below - const isInternal = get(this, 'configurator').getConfig( - 'isInternal' + const isInternal = configuratorService.getConfig('isInternal'); + const jitAclAccessWhitelist: IAppConfig['jitAclAccessWhitelist'] = configuratorService.getConfig< + IAppConfig['jitAclAccessWhitelist'] + >('JitAclAccessWhitelist'); + const showOwnership = configuratorService.getConfig( + 'showOwnership' ); - const jitAclAccessWhitelist: IAppConfig['jitAclAccessWhitelist'] = get( - this, - 'configurator' - ).getConfig('JitAclAccessWhitelist'); setProperties(controller, { isInternal: !!isInternal, - jitAclAccessWhitelist: jitAclAccessWhitelist || [] + jitAclAccessWhitelist: jitAclAccessWhitelist || [], + showOwnership }); } diff --git a/wherehows-web/app/styles/components/dataset-author/_all.scss b/wherehows-web/app/styles/components/dataset-author/_all.scss index 9bbf2321d8..1a36770309 100644 --- a/wherehows-web/app/styles/components/dataset-author/_all.scss +++ b/wherehows-web/app/styles/components/dataset-author/_all.scss @@ -1,2 +1,3 @@ @import 'owner-table'; @import 'dataset-author'; +@import 'suggested-owners'; diff --git a/wherehows-web/app/styles/components/dataset-author/_suggested-owners.scss b/wherehows-web/app/styles/components/dataset-author/_suggested-owners.scss new file mode 100644 index 0000000000..4bdfb7641b --- /dev/null +++ b/wherehows-web/app/styles/components/dataset-author/_suggested-owners.scss @@ -0,0 +1,145 @@ +.dataset-authors-suggested { + background-color: rgba(225, 233, 238, 0.75); + width: 100%; + margin-bottom: 28px; + padding: 16px 24px 0; + overflow: hidden; + cursor: default; + + .user-avatar { + height: 36px; + width: 36px; + } + + &__header { + margin: 0; + font-weight: 400; + } + + &__info { + height: 54px; + margin-bottom: 16px; + + &__facepile, + &__description, + &__trigger { + float: left; + height: 100%; + padding-top: 6px; + } + + &__facepile { + width: 128px; + display: flex; + flex-wrap: wrap; + + &__avatar { + box-sizing: border-box; + background-clip: content-box; + border: 2px solid white; + width: 36px; + height: 36px; + clear: right; + border-radius: 50%; + margin-right: -12px; + + &:last-child { + margin-right: 0; + } + } + } + + &__description { + width: 560px; + word-wrap: wrap; + padding-left: 16px; + } + + &__trigger { + float: right; + cursor: pointer; + + &__action { + color: $link-color; + font-weight: 500; + } + } + } + + &__card { + width: 31%; + box-sizing: border-box; + height: 108px; + background-color: white; + margin: 0 1% 16px; + float: left; + box-shadow: 0 1px 1px 0 rgba(0, 0, 0, 0.16), 0 0 0 1px rgba(0, 0, 0, 0.08); + + &__owner-info, + &__source-info { + width: 100%; + box-sizing: border-box; + padding: 6px 16px; + } + + &__owner-info { + border-bottom: 1px solid rgba(0, 0, 0, 0.16); + height: 70px; + + &__profile, + &__add { + width: 50%; + box-sizing: border-box; + float: left; + } + + &__add { + padding-top: 12px; + padding-left: 12px; + text-align: center; + + &--disabled, + .fa-check-circle-o { + font-size: 18px; + color: rgba(0, 0, 0, 0.5); + } + + &--disabled { + font-weight: 600; + margin-left: 8px; + } + } + + &__profile { + &__pic { + float: left; + margin-right: 6px; + margin-top: 10px; + } + + &__name { + float: left; + max-width: 108px; + + &__full { + max-width: 108px; + font-size: 16px; + font-weight: 400; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + margin-bottom: 0; + } + + &__username { + font-size: 14px; + } + } + } + } + + &__source-info { + font-size: 14px; + } + } +} diff --git a/wherehows-web/app/templates/components/dataset-authors.hbs b/wherehows-web/app/templates/components/dataset-authors.hbs index 2919ea8108..4aefdcaf5b 100644 --- a/wherehows-web/app/templates/components/dataset-authors.hbs +++ b/wherehows-web/app/templates/components/dataset-authors.hbs @@ -84,59 +84,71 @@ {{#if systemGeneratedOwners}} -
-
-

- System Suggested Owners -

- - -
- - - - - - - - - - - - - - - - {{#each systemGeneratedOwners as |systemGeneratedOwner|}} - {{dataset-author - owner=systemGeneratedOwner + {{#if (eq showOwnership "show")}} +
+ {{datasets/owners/suggested-owners + owners=systemGeneratedOwners ownerTypes=ownerTypes commonOwners=commonOwners removeOwner=(action "removeOwner") - confirmSuggestedOwner=(action "confirmSuggestedOwner") - updateOwnerType=(action "updateOwnerType") - }} - {{/each}} -
+ confirmSuggestedOwner=(action "confirmSuggestedOwner") + updateOwnerType=(action "updateOwnerType")}} + + {{else}} -
LDAP UsernameFull NameID TypeSourceLast Modified - Ownership Type - - - - - - - - - Add Suggested Owner
-
+
+
+

+ System Suggested Owners +

+ +
+ + + + + + + + + + + + + + + + {{#each systemGeneratedOwners as |systemGeneratedOwner|}} + {{dataset-author + owner=systemGeneratedOwner + ownerTypes=ownerTypes + commonOwners=commonOwners + removeOwner=(action "removeOwner") + confirmSuggestedOwner=(action "confirmSuggestedOwner") + updateOwnerType=(action "updateOwnerType") + }} + {{/each}} + + +
LDAP UsernameFull NameID TypeSourceLast Modified + Ownership Type + + + + + + + + + Add Suggested Owner
+
+ {{/if}} {{/if}}
diff --git a/wherehows-web/app/templates/components/datasets/containers/dataset-ownership.hbs b/wherehows-web/app/templates/components/datasets/containers/dataset-ownership.hbs index 1e568f2d94..facd85d417 100644 --- a/wherehows-web/app/templates/components/datasets/containers/dataset-ownership.hbs +++ b/wherehows-web/app/templates/components/datasets/containers/dataset-ownership.hbs @@ -33,6 +33,7 @@ {{dataset-authors owners=owners ownerTypes=ownerTypes + showOwnership=showOwnership save=(action "saveOwnerChanges") }} diff --git a/wherehows-web/app/templates/components/datasets/owners/suggested-owner-card.hbs b/wherehows-web/app/templates/components/datasets/owners/suggested-owner-card.hbs new file mode 100644 index 0000000000..834210ccc7 --- /dev/null +++ b/wherehows-web/app/templates/components/datasets/owners/suggested-owner-card.hbs @@ -0,0 +1,29 @@ +
+
+ {{user-avatar + class="dataset-authors-suggested__card__owner-info__profile__pic" + userName=owner.userName}} +
+
+ {{owner.name}} +
+

+ {{owner.userName}} +

+
+
+
+ {{#if isConfirmedSuggestedOwner}} + {{fa-icon "check-circle-o" title="Added Owner" size="2"}} + Added + {{else}} + + {{/if}} +
+
+
+ Source: {{owner.source}} +
\ No newline at end of file diff --git a/wherehows-web/app/templates/components/datasets/owners/suggested-owners.hbs b/wherehows-web/app/templates/components/datasets/owners/suggested-owners.hbs new file mode 100644 index 0000000000..b87bcd5d6a --- /dev/null +++ b/wherehows-web/app/templates/components/datasets/owners/suggested-owners.hbs @@ -0,0 +1,35 @@ +

Suggestions

+ +{{#if isEmpty}} +
+ We found no suggested owner(s) for this dataset, based on scanning different systems +
+{{else}} +
+
+ {{#each facepileOwners as |owner|}} + {{user-avatar class="dataset-authors-suggested__info__facepile__avatar" userName=owner.userName}} + {{/each}} +
+
+ We found {{owners.length}} {{if (gt owners.length 1) "people" "person"}} who would be great owner(s) + for this dataset, based on scanning different systems +
+
+ View Suggestions + {{fa-icon (if isExpanded "chevron-up" "chevron-down")}} +
+
+{{/if}} + +{{#if isExpanded}} + {{#each owners as |owner|}} + {{datasets/owners/suggested-owner-card + owner=owner + ownerTypes=ownerTypes + commonOwners=commonOwners + removeOwner=removeOwner + confirmSuggestedOwner=confirmSuggestedOwner + updateOwnerType=updateOwnerType}} + {{/each}} +{{/if}} \ No newline at end of file diff --git a/wherehows-web/app/templates/datasets/dataset.hbs b/wherehows-web/app/templates/datasets/dataset.hbs index c448e52f86..5c12dc163c 100644 --- a/wherehows-web/app/templates/datasets/dataset.hbs +++ b/wherehows-web/app/templates/datasets/dataset.hbs @@ -101,7 +101,7 @@ {{/tabs.tabpanel}} {{#tabs.tabpanel tabIds.Ownership}} - {{datasets/containers/dataset-ownership urn=encodedUrn}} + {{datasets/containers/dataset-ownership urn=encodedUrn showOwnership=showOwnership}} {{/tabs.tabpanel}} {{#tabs.tabpanel tabIds.Compliance}} diff --git a/wherehows-web/app/typings/api/configurator/configurator.d.ts b/wherehows-web/app/typings/api/configurator/configurator.d.ts index 9560f44585..937253f86e 100644 --- a/wherehows-web/app/typings/api/configurator/configurator.d.ts +++ b/wherehows-web/app/typings/api/configurator/configurator.d.ts @@ -8,6 +8,7 @@ import { DatasetPlatform } from 'wherehows-web/constants'; interface IAppConfig { isInternal: boolean | void; jitAclAccessWhitelist: Array | void; + showOwnership: string; [key: string]: any; } diff --git a/wherehows-web/tests/integration/components/datasets/owners/suggested-owner-card-test.js b/wherehows-web/tests/integration/components/datasets/owners/suggested-owner-card-test.js new file mode 100644 index 0000000000..028da58e47 --- /dev/null +++ b/wherehows-web/tests/integration/components/datasets/owners/suggested-owner-card-test.js @@ -0,0 +1,120 @@ +import { moduleForComponent, test } from 'ember-qunit'; +import hbs from 'htmlbars-inline-precompile'; +import { triggerEvent } from 'ember-native-dom-helpers'; + +import owners from 'wherehows-web/mirage/fixtures/owners'; +import { OwnerType } from 'wherehows-web/utils/api/datasets/owners'; +import noop from 'wherehows-web/utils/noop'; + +moduleForComponent( + 'datasets/owners/suggested-owner-card', + 'Integration | Component | datasets/owners/suggested owner card', + { + integration: true + } +); + +const [confirmedOwner, suggestedOwner] = owners; +const ownerTypes = Object.values(OwnerType); +const commonOwners = [confirmedOwner]; + +const fullNameClass = '.dataset-authors-suggested__card__owner-info__profile__name__full'; +const usernameClass = '.dataset-authors-suggested__card__owner-info__profile__name__username'; +const addedClass = '.dataset-authors-suggested__card__owner-info__add--disabled'; +const addButtonClass = '.nacho-button--secondary.nacho-button--medium'; +const sourceClass = '.dataset-authors-suggested__card__source-info'; + +test('it renders for base and empty cases', function(assert) { + this.setProperties({ + commonOwners, + owner: {}, + ownerTypes: [], + removeOwner: noop, + confirmSuggestedOwner: noop + }); + + this.render(hbs`{{datasets/owners/suggested-owner-card + owner=owner + ownerTypes=ownerTypes + commonOwners=commonOwners + removeOwner=removeOwner + confirmSuggestedOwner=confirmSuggestedOwner}}`); + + assert.ok(this.$(), 'Renders independently without errors'); + assert.ok(this.$(), 'Empty owner does not create breaking error'); +}); + +test('it renders the correct information for suggested owner', function(assert) { + const model = suggestedOwner; + const fullNameText = model.name; + const usernameText = model.userName; + const sourceText = `Source: ${model.source}`; + + this.setProperties({ + ownerTypes, + commonOwners, + owner: model, + removeOwner: noop, + confirmSuggestedOwner: noop + }); + + this.render(hbs`{{datasets/owners/suggested-owner-card + owner=owner + ownerTypes=ownerTypes + commonOwners=commonOwners + removeOwner=removeOwner + confirmSuggestedOwner=confirmSuggestedOwner}}`); + + assert.ok(this.$(), 'Still renders without errors'); + + assert.equal( + this.$(fullNameClass) + .text() + .trim(), + fullNameText, + 'Renders the name correctly' + ); + + assert.equal( + this.$(usernameClass) + .text() + .trim(), + usernameText, + 'Renders the username correctly' + ); + + assert.equal( + this.$(sourceClass) + .text() + .trim(), + sourceText, + 'Renders the source correctly' + ); + + assert.equal(this.$(addedClass).length, 0, 'Does not consider suggested owner already added'); + assert.equal(this.$(addButtonClass).length, 1, 'Renders add button for suggested class'); +}); + +test('it functions correctly to add a suggested owner', function(assert) { + const model = suggestedOwner; + + this.setProperties({ + ownerTypes, + commonOwners, + owner: model, + removeOwner: noop, + confirmSuggestedOwner: owner => { + assert.equal(owner.name, model.name, 'Passes the correct information to the confirmOwner function'); + } + }); + + this.render(hbs`{{datasets/owners/suggested-owner-card + owner=owner + ownerTypes=ownerTypes + commonOwners=commonOwners + removeOwner=removeOwner + confirmSuggestedOwner=confirmSuggestedOwner}}`); + + assert.ok(this.$(), 'Still renders without errors for real function passed in'); + triggerEvent(addButtonClass, 'click'); +}); diff --git a/wherehows-web/tests/integration/components/datasets/owners/suggested-owners-test.js b/wherehows-web/tests/integration/components/datasets/owners/suggested-owners-test.js new file mode 100644 index 0000000000..7c1dd29e8c --- /dev/null +++ b/wherehows-web/tests/integration/components/datasets/owners/suggested-owners-test.js @@ -0,0 +1,26 @@ +import { moduleForComponent, test } from 'ember-qunit'; +import hbs from 'htmlbars-inline-precompile'; + +moduleForComponent('datasets/owners/suggested-owners', 'Integration | Component | datasets/owners/suggested owners', { + integration: true +}); + +const descriptionClass = '.dataset-authors-suggested__info__description'; +const suggestedCardClass = '.dataset-authors-suggested__card'; + +test('it renders properly for null case and empty states', function(assert) { + const descriptionText = 'We found no suggested owner(s) for this dataset, based on scanning different systems'; + + this.render(hbs`{{datasets/owners/suggested-owners}}`); + + assert.ok(this.$(), 'Renders without errors when passed no values'); + assert.equal(this.$(suggestedCardClass).length, 0, 'Renders no cards'); + + assert.equal( + this.$(descriptionClass) + .text() + .trim(), + descriptionText, + 'Renders the correct message when there are no owners' + ); +});