-
+
{{@node.title}}
@@ -16,7 +16,7 @@
-
diff --git a/lib/osf-components/addon/components/group-list/component.ts b/lib/osf-components/addon/components/group-list/component.ts
new file mode 100644
index 000000000..0c8b0d410
--- /dev/null
+++ b/lib/osf-components/addon/components/group-list/component.ts
@@ -0,0 +1,75 @@
+import { tagName } from '@ember-decorators/component';
+import Component from '@ember/component';
+import { computed } from '@ember/object';
+import { alias } from '@ember/object/computed';
+import { inject as service } from '@ember/service';
+import { task } from 'ember-concurrency-decorators';
+import DS from 'ember-data';
+
+import { layout } from 'ember-osf-web/decorators/component';
+import Node from 'ember-osf-web/models/node';
+import NodeMapcoreGroup from 'ember-osf-web/models/node-mapcore-group';
+import Ready from 'ember-osf-web/services/ready';
+import captureException from 'ember-osf-web/utils/capture-exception';
+import defaultTo from 'ember-osf-web/utils/default-to';
+
+import styles from './styles';
+import template from './template';
+
+@layout(template, styles)
+@tagName('span')
+export default class GroupList extends Component {
+ // Required arguments
+ node?: Node;
+
+ // Optional arguments
+ shouldTruncate: boolean = defaultTo(this.shouldTruncate, true);
+
+ // Private properties
+ @service store!: DS.Store;
+ @service ready!: Ready;
+
+ displayedGroups: NodeMapcoreGroup[] = [];
+ totalGroups?: number;
+
+ @alias('loadGroups.isRunning')
+ isLoading!: boolean;
+
+ @task({ restartable: true, on: 'didReceiveAttrs' })
+ loadGroups = task(function *(this: GroupList) {
+ try {
+ if (!this.node || this.node.isAnonymous) {
+ return;
+ }
+ const blocker = this.ready.getBlocker();
+ const itemsFromResult = (res: any) => {
+ const arr = (res && typeof res.toArray === 'function') ? res.toArray() : res || [];
+ return arr.map((item: any) => {
+ if (item && typeof item === 'object' && item.__data) {
+ return item.__data;
+ }
+ return item;
+ });
+ };
+
+ const query = { nodeId: this.node.id, page: 1, visible: true };
+ const result = yield this.store.query('node-mapcore-group', query);
+ const groups = itemsFromResult(result) as NodeMapcoreGroup[];
+ const meta = (result as any).meta || {};
+ this.setProperties({
+ displayedGroups: groups,
+ totalGroups: meta.total || groups.length,
+ });
+
+ blocker.done();
+ } catch (e) {
+ captureException(e, { errorMessage: 'loadGroups task failed synchronously' });
+ throw e;
+ }
+ });
+
+ @computed('truncated')
+ get truncateCount() {
+ return this.shouldTruncate ? 3 : undefined;
+ }
+}
diff --git a/lib/osf-components/addon/components/group-list/group/component.ts b/lib/osf-components/addon/components/group-list/group/component.ts
new file mode 100644
index 000000000..ef4cdbeba
--- /dev/null
+++ b/lib/osf-components/addon/components/group-list/group/component.ts
@@ -0,0 +1,28 @@
+import { tagName } from '@ember-decorators/component';
+import Component from '@ember/component';
+import { task } from 'ember-concurrency-decorators';
+
+import { layout } from 'ember-osf-web/decorators/component';
+import NodeMapcoreGroup from 'ember-osf-web/models/node-mapcore-group';
+import defaultTo from 'ember-osf-web/utils/default-to';
+import template from './template';
+
+@layout(template)
+@tagName('')
+export default class NodeMapcoreGroupListGroup extends Component {
+ group!: NodeMapcoreGroup;
+ shouldShortenName: boolean = defaultTo(this.shouldShortenName, false);
+
+ groupName?: string;
+
+ @task({ restartable: true, on: 'didReceiveAttrs' })
+ loadGroup = task(function *(this: NodeMapcoreGroupListGroup) {
+ yield Promise.resolve();
+ this.set(
+ 'groupName',
+ this.shouldShortenName
+ ? this.group && this.group.name
+ : this.group.name,
+ );
+ });
+}
diff --git a/lib/osf-components/addon/components/group-list/group/template.hbs b/lib/osf-components/addon/components/group-list/group/template.hbs
new file mode 100644
index 000000000..26b5bb1bd
--- /dev/null
+++ b/lib/osf-components/addon/components/group-list/group/template.hbs
@@ -0,0 +1,3 @@
+
+ {{~this.groupName~}}
+
\ No newline at end of file
diff --git a/lib/osf-components/addon/components/group-list/styles.scss b/lib/osf-components/addon/components/group-list/styles.scss
new file mode 100644
index 000000000..4cf5bb451
--- /dev/null
+++ b/lib/osf-components/addon/components/group-list/styles.scss
@@ -0,0 +1,3 @@
+:global(.btn).load-groups {
+ padding: 0 0 3px;
+}
diff --git a/lib/osf-components/addon/components/group-list/template.hbs b/lib/osf-components/addon/components/group-list/template.hbs
new file mode 100644
index 000000000..19d033076
--- /dev/null
+++ b/lib/osf-components/addon/components/group-list/template.hbs
@@ -0,0 +1,23 @@
+{{#if this.node.isAnonymous}}
+ {{t 'group_list.anonymous'}}
+{{else}}
+
+ {{~#if list.item}}
+ {{~group-list/group
+ group=list.item
+ shouldShortenName=this.shouldTruncate
+ ~}}
+ {{else if list.remainingCount}}
+ {{#if this.isLoading }}
+
+ {{else}}
+ {{t 'group_list.x_more' x=list.remainingCount}}
+ {{/if}}
+ {{/if~}}
+
+{{/if}}
diff --git a/lib/osf-components/app/components/group-list/component.js b/lib/osf-components/app/components/group-list/component.js
new file mode 100644
index 000000000..f255aea3a
--- /dev/null
+++ b/lib/osf-components/app/components/group-list/component.js
@@ -0,0 +1 @@
+export { default } from 'osf-components/components/group-list/component';
diff --git a/lib/osf-components/app/components/group-list/group/component.js b/lib/osf-components/app/components/group-list/group/component.js
new file mode 100644
index 000000000..d9cac0b79
--- /dev/null
+++ b/lib/osf-components/app/components/group-list/group/component.js
@@ -0,0 +1 @@
+export { default } from 'osf-components/components/group-list/group/component';
diff --git a/mirage/config.ts b/mirage/config.ts
index 1607ac66b..019effa5b 100644
--- a/mirage/config.ts
+++ b/mirage/config.ts
@@ -86,6 +86,15 @@ export default function(this: Server) {
defaultSortKey: 'index',
onCreate: createBibliographicContributor,
});
+ // Added handler for node map_core groups so adapters querying
+ // Returns an empty paginated response by default.
+ this.get('/nodes/:parentID/map_core/groups', () => ({
+ data: [],
+ meta: {
+ total: 0,
+ per_page: 10,
+ },
+ }));
this.get('/nodes/:parentID/files', nodeFileProviderList); // Node file providers list
this.get('/nodes/:parentID/files/:fileProviderId', nodeFilesListForProvider); // Node files list for file provider
diff --git a/translations/en-us.yml b/translations/en-us.yml
index 6120993d9..e57e67416 100644
--- a/translations/en-us.yml
+++ b/translations/en-us.yml
@@ -95,6 +95,7 @@ general:
preprints: Preprints
registries: Registries
other: Other
+ groups: Groups
node_categories:
analysis: Analysis
communication: Communication
@@ -709,6 +710,8 @@ list:
contributor_list:
x_more: '{x} more'
anonymous: 'Anonymous contributors'
+group_list:
+ x_more: '{x} more'
app_components:
branded_navbar:
my_osf_projects: 'My GakuNin RDM Projects'
diff --git a/translations/ja.yml b/translations/ja.yml
index cd7debe6a..acf9bda09 100644
--- a/translations/ja.yml
+++ b/translations/ja.yml
@@ -95,6 +95,7 @@ general:
preprints: プレプリント
registries: 登録
other: Other
+ groups: グループ
node_categories:
analysis: 分析
communication: コミュニケーション
@@ -709,6 +710,8 @@ list:
contributor_list:
x_more: '他{x}名'
anonymous: 匿名のメンバー
+group_list:
+ x_more: '他{x}名'
app_components:
branded_navbar:
my_osf_projects: 'My GakuNin RDM Projects'