From 02c7198db1d90bba3eb1d071dd4f21bda64ae27d Mon Sep 17 00:00:00 2001 From: jonasongg <120372506+jonasongg@users.noreply.github.com> Date: Sat, 27 Apr 2024 15:36:04 +0800 Subject: [PATCH] [#2148] Show tags on the ramp chart (#2163) It can be useful to view all the tags of each repo that appears on the ramp chart. Though this information is visible if the repo is merged and the user click to see the breakdown of each commit, it can still be useful to see this information upfront (for searching Ctrl+F perhaps). This feature would be toggleable with a checkbox to reduce clutter. Let's add this functionality to add convenience when searching through RepoSense reports with tags. --- docs/ug/usingReports.md | 1 + .../tests/chartView/chartView_showTags.cy.js | 67 +++++++++++++++++++ .../chartView/chartView_zoomFeature.cy.js | 26 +++---- .../codeView/codeView_renderFilterHash.cy.js | 14 ++++ frontend/src/components/c-summary-charts.vue | 48 +++++++++++++ frontend/src/styles/summary-chart.scss | 5 ++ frontend/src/styles/tags.scss | 16 +++++ frontend/src/views/c-summary.vue | 27 ++++++-- frontend/src/views/c-zoom.vue | 16 +---- 9 files changed, 188 insertions(+), 32 deletions(-) create mode 100644 frontend/cypress/tests/chartView/chartView_showTags.cy.js create mode 100644 frontend/src/styles/tags.scss diff --git a/docs/ug/usingReports.md b/docs/ug/usingReports.md index ebe587bd2c..6aa5ebae6c 100644 --- a/docs/ug/usingReports.md +++ b/docs/ug/usingReports.md @@ -118,6 +118,7 @@ The `Tool Bar` at the top of the Chart panel provides a set of configuration opt * a breakdown of the number of lines of codes added to each file type (if the checkbox is checked). More info on note [3] below. * `Merge group`: merges all the ramp charts of each group into a single ramp chart; aggregates the contribution of each group. * viewing of authored code of the group as a whole is available when `group by repos`. +* `Show tags`: shows the tags of all the repos under a group Notes:
[1] **`Sort groups by`**: each main group has its own index and percentile according to its ranking position after sorting (e.g., if the groups are sorted by contribution in descending order, a 25% percentile indicates that the group is in the top 25% of the whole cohort in terms of contribution)
. diff --git a/frontend/cypress/tests/chartView/chartView_showTags.cy.js b/frontend/cypress/tests/chartView/chartView_showTags.cy.js new file mode 100644 index 0000000000..0e5ed49e67 --- /dev/null +++ b/frontend/cypress/tests/chartView/chartView_showTags.cy.js @@ -0,0 +1,67 @@ +describe('show tags', () => { + it('unchecked should not display any tags for a group', () => { + cy.get('#summary label.show-tags > input:visible') + .should('be.visible') + .uncheck() + .should('not.be.checked'); + + cy.get('.summary-charts__title--tags') + .should('not.exist'); + }); + + it('checked should display all tags for a group', () => { + cy.get('#summary label.show-tags > input:visible') + .should('be.visible') + .check() + .should('be.checked'); + + cy.get('#summary label.merge-group > input:visible') + .should('be.visible') + .check() + .should('be.checked'); + + cy.get('.icon-button.fa-list-ul') + .should('exist') + .first() + .click(); + + const correctTags = []; + + cy.get('.zoom__title--tags') + .find('.tag') + .each(($tag) => correctTags.push($tag.text().trim())) + .then(() => { + cy.get('.summary-charts') + .first() + .find('.summary-charts__title--tags') + .find('.tag') + .each(($tag) => { + expect(correctTags).to.include($tag.text().trim()); + }); + + cy.get('.summary-charts') + .first() + .find('.summary-charts__title--tags') + .find('.tag') + .should('have.length', correctTags.length); + }); + }); + + it('clicked should redirect to the correct tag page', () => { + cy.get('#summary label.show-tags > input:visible') + .should('be.visible') + .check() + .should('be.checked'); + + cy.get('.summary-charts__title--tags') + .find('.tag') + .first() + .invoke('removeAttr', 'target') // to open in the same window + .click(); + + cy.origin('https://github.com', () => { + cy.url() + .should('equal', 'https://github.com/reposense/RepoSense/releases/tag/v1.0'); + }); + }); +}); diff --git a/frontend/cypress/tests/chartView/chartView_zoomFeature.cy.js b/frontend/cypress/tests/chartView/chartView_zoomFeature.cy.js index e3abafacb2..5fb72ddede 100644 --- a/frontend/cypress/tests/chartView/chartView_zoomFeature.cy.js +++ b/frontend/cypress/tests/chartView/chartView_zoomFeature.cy.js @@ -242,7 +242,7 @@ describe('date changes in chart view should reflect in zoom', () => { }); describe('range changes in chartview should reflect in zoom', () => { - const zoomKey = Cypress.platform === 'darwin' ? '{meta}' : '{ctrl}'; + const zoomKeyOption = Cypress.platform === 'darwin' ? { metaKey: true } : { ctrlKey: true }; // Assumptions: Contributer 'jamessspanggg' is the first result, // he does not add more commits in the future, @@ -254,11 +254,11 @@ describe('range changes in chartview should reflect in zoom', () => { cy.get('input[name="until"]:visible') .type('2023-12-31'); - cy.get('body').type(zoomKey, { release: false }) + cy.get('body') .get('#summary-charts .summary-chart__ramp .ramp') .first() - .click(120, 20) - .click(250, 20); + .click(120, 20, zoomKeyOption) + .click(250, 20, zoomKeyOption); cy.get('#tab-zoom') .should('be.visible'); @@ -283,11 +283,11 @@ describe('range changes in chartview should reflect in zoom', () => { cy.get('input[name="until"]:visible') .type('2023-12-31'); - cy.get('body').type(zoomKey, { release: false }) + cy.get('body') .get('#summary-charts .summary-chart__ramp .ramp') .first() - .click(120, 20) - .click(170, 20); + .click(120, 20, zoomKeyOption) + .click(170, 20, zoomKeyOption); cy.get('#tab-zoom') .should('be.visible'); @@ -312,11 +312,11 @@ describe('range changes in chartview should reflect in zoom', () => { cy.get('input[name="until"]:visible') .type('2023-12-31'); - cy.get('body').type(zoomKey, { release: false }) + cy.get('body') .get('#summary-charts .summary-chart__ramp .ramp') .first() - .click(170, 20) - .click(250, 20); + .click(170, 20, zoomKeyOption) + .click(250, 20, zoomKeyOption); cy.get('#tab-zoom') .should('be.visible'); @@ -341,11 +341,11 @@ describe('range changes in chartview should reflect in zoom', () => { cy.get('input[name="until"]:visible') .type('2023-12-31'); - cy.get('body').type(zoomKey, { release: false }) + cy.get('body') .get('#summary-charts .summary-chart__ramp .ramp') .first() - .click(170, 20) - .click(225, 20); + .click(170, 20, zoomKeyOption) + .click(225, 20, zoomKeyOption); cy.get('#tab-zoom') .should('be.visible'); diff --git a/frontend/cypress/tests/codeView/codeView_renderFilterHash.cy.js b/frontend/cypress/tests/codeView/codeView_renderFilterHash.cy.js index c42998b856..1cc456e0e4 100644 --- a/frontend/cypress/tests/codeView/codeView_renderFilterHash.cy.js +++ b/frontend/cypress/tests/codeView/codeView_renderFilterHash.cy.js @@ -302,6 +302,20 @@ describe('render filter hash', () => { .should('contain', 'mergegroup=reposense%2FRepoSense%5Bcypress%5D'); }); + it('show tags: url params should persist after change and reload', () => { + cy.get('#summary label.show-tags input:visible') + .should('be.visible') + .check(); + + cy.url() + .should('contain', 'viewRepoTags=true'); + + cy.reload(); + + cy.url() + .should('contain', 'viewRepoTags=true'); + }); + it('checked file types: url params should persist after change and reload', () => { cy.get('#summary label.filter-breakdown input:visible') .should('not.be.checked'); diff --git a/frontend/src/components/c-summary-charts.vue b/frontend/src/components/c-summary-charts.vue index 34f676bb88..f7677ca50b 100644 --- a/frontend/src/components/c-summary-charts.vue +++ b/frontend/src/components/c-summary-charts.vue @@ -141,6 +141,18 @@ v-if="sortGroupSelection.includes('totalCommits')" ) {{ getPercentile(i) }} %  span.tooltip-text.right-aligned {{ getPercentileExplanation(i) }} + .summary-charts__title--tags( + v-if="isViewingTagsByRepo" + ) + a.tag( + v-for="tag in getTags(repo)", + v-bind:href="getTagLink(repo[0], tag)", + target="_blank", + vbind:key="tag", + tabindex="-1" + ) + font-awesome-icon(icon="tags") + span  {{ tag }} .summary-charts__fileType--breakdown(v-if="filterBreakdown") template(v-if="filterGroupSelection !== 'groupByNone'") .summary-charts__fileType--breakdown__legend( @@ -261,6 +273,16 @@ v-if="filterGroupSelection === 'groupByNone' && sortGroupSelection.includes('totalCommits')" ) {{ getPercentile(j) }} %  span.tooltip-text.right-aligned {{ getPercentileExplanation(j) }} + .summary-chart__title--tags(v-if="isViewingTagsByAuthor") + a.tag( + v-for="tag in getTags(repo, user)", + v-bind:href="getTagLink(user, tag)", + target="_blank", + vbind:key="tag", + tabindex="-1" + ) + font-awesome-icon(icon="tags") + span  {{ tag }} .summary-chart__ramp( v-on:click="openTabZoomSubrange(user, $event, isGroupMerged(getGroupName(repo)))" @@ -374,6 +396,10 @@ export default defineComponent({ type: Number, default: undefined, }, + viewRepoTags: { + type: Boolean, + default: false, + }, }, data(): { drags: Array, @@ -420,6 +446,14 @@ export default defineComponent({ isChartWidgetMode(): boolean { return this.chartIndex !== undefined && this.chartIndex >= 0 && this.isChartGroupWidgetMode; }, + isViewingTagsByRepo() { + return this.filterGroupSelection === FilterGroupSelection.GroupByRepos && this.viewRepoTags; + }, + isViewingTagsByAuthor() { + return (this.filterGroupSelection === FilterGroupSelection.GroupByAuthors + || this.filterGroupSelection === FilterGroupSelection.GroupByNone) + && this.viewRepoTags; + }, ...mapState({ mergedGroups: (state: unknown) => (state as StoreState).mergedGroups, fileTypeColors: (state: unknown) => (state as StoreState).fileTypeColors, @@ -576,6 +610,10 @@ export default defineComponent({ } }, + getTagLink(repo: User, tag: string): string | undefined { + return window.filterUnsupported(`${window.getRepoLinkUnfiltered(repo.repoId)}releases/tag/${tag}`); + }, + // triggering opening of tabs // openTabAuthorship(user: User, repo: Array, index: number, isMerged: boolean): void { const { @@ -902,6 +940,16 @@ export default defineComponent({ chart.scrollIntoView({ block: 'nearest' }); } }, + + getTags(repo: Array, user?: User): Array { + if (user) repo = repo.filter((r) => r.name === user.name); + return [...new Set(repo.flatMap((r) => r.commits).flatMap((c) => c.commitResults).flatMap((r) => r.tags))] + .filter(Boolean) as Array; + }, }, }); + + diff --git a/frontend/src/styles/summary-chart.scss b/frontend/src/styles/summary-chart.scss index 8cfa722497..20dd8b8feb 100644 --- a/frontend/src/styles/summary-chart.scss +++ b/frontend/src/styles/summary-chart.scss @@ -97,6 +97,7 @@ &__title { align-items: center; display: flex; + flex-wrap: wrap; font-weight: bold; text-align: left; @@ -128,6 +129,10 @@ @include mini-font; display: inline; } + + &--tags { + margin: .25rem 0; + } } &__fileType--breakdown { diff --git a/frontend/src/styles/tags.scss b/frontend/src/styles/tags.scss new file mode 100644 index 0000000000..13aea03fac --- /dev/null +++ b/frontend/src/styles/tags.scss @@ -0,0 +1,16 @@ +@import 'colors'; + +/* Tags in commits */ +.tag { + @include mini-font; + background: mui-color('grey', '600'); + border-radius: 5px; + color: mui-color('white'); + display: inline-block; + margin: .2rem .2rem .2rem 0; + padding: 0 3px 0 3px; + + .fa-tags { + width: .65rem; + } +} diff --git a/frontend/src/views/c-summary.vue b/frontend/src/views/c-summary.vue index 777ec86c48..e9fc704997 100644 --- a/frontend/src/views/c-summary.vue +++ b/frontend/src/views/c-summary.vue @@ -73,6 +73,13 @@ v-bind:disabled="filterGroupSelection === 'groupByNone'" ) span merge all groups + label.show-tags + input.mui-checkbox( + type="checkbox", + v-model="viewRepoTags", + v-on:change="getFiltered" + ) + span show tags .error-message-box(v-if="Object.entries(errorMessages).length && !isWidgetMode") .error-message-box__close-button(v-on:click="dismissTab($event)") × .error-message-box__message The following issues occurred when analyzing the following repositories: @@ -123,7 +130,8 @@ v-bind:max-date="maxDate", v-bind:sort-group-selection="sortGroupSelection", v-bind:chart-group-index="chartGroupIndex", - v-bind:chart-index="chartIndex" + v-bind:chart-index="chartIndex", + v-bind:view-repo-tags="viewRepoTags" ) @@ -205,7 +213,8 @@ export default defineComponent({ chartGroupIndex: number | undefined, chartIndex: number | undefined, errorIsShowingMore: boolean, - numberOfErrorMessagesToShow: number + numberOfErrorMessagesToShow: number, + viewRepoTags: boolean, } { return { checkedFileTypes: [] as Array, @@ -235,6 +244,7 @@ export default defineComponent({ chartIndex: undefined as number | undefined, errorIsShowingMore: false, numberOfErrorMessagesToShow: 4, + viewRepoTags: false, }; }, computed: { @@ -388,7 +398,7 @@ export default defineComponent({ }, setSummaryHash(): void { - const { addHash, encodeHash } = window; + const { addHash, encodeHash, removeHash } = window; addHash('search', this.filterSearch); addHash('sort', this.sortGroupSelection); @@ -419,7 +429,13 @@ export default defineComponent({ : ''; addHash('checkedFileTypes', checkedFileTypesHash); } else { - window.removeHash('checkedFileTypes'); + removeHash('checkedFileTypes'); + } + + if (this.viewRepoTags) { + addHash('viewRepoTags', 'true'); + } else { + removeHash('viewRepoTags'); } encodeHash(); @@ -470,6 +486,9 @@ export default defineComponent({ if (hash.chartIndex) { this.chartIndex = parseInt(hash.chartIndex, 10); } + if (hash.viewRepoTags) { + this.viewRepoTags = convertBool(hash.viewRepoTags); + } }, getGroupName(group: Array): string { diff --git a/frontend/src/views/c-zoom.vue b/frontend/src/views/c-zoom.vue index af521cbfa5..4c2f151785 100644 --- a/frontend/src/views/c-zoom.vue +++ b/frontend/src/views/c-zoom.vue @@ -385,6 +385,7 @@ export default defineComponent({