diff --git a/.eslintrc.js b/.eslintrc.js
index 108c4697..b0c7a0cc 100644
--- a/.eslintrc.js
+++ b/.eslintrc.js
@@ -6,6 +6,8 @@ module.exports = {
'comma-dangle': ['error', 'always-multiline'],
'rulesdir/no-api-in-views': 'off',
'rulesdir/no-multiple-api-calls': 'off',
+ '@lwc/lwc/no-async-await': 'off',
+ 'es/no-nullish-coalescing-operators' : 'off'
},
settings: {
'import/resolver': {
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 903c8aff..6da33bfb 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,7 @@
+#1.4.0
+- Updated the extension to work with new GitHub UI
+- Updated the extension to work with GitHub's new PR merge experience
+
#1.3.74
- Moved the previous query string params to Onyx
diff --git a/assets/manifest-firefox.json b/assets/manifest-firefox.json
index e008db29..bc596a0b 100644
--- a/assets/manifest-firefox.json
+++ b/assets/manifest-firefox.json
@@ -2,7 +2,7 @@
"manifest_version": 3,
"name": "K2 for GitHub",
- "version": "1.3.74",
+ "version": "1.4.0",
"description": "Manage your Kernel Scheduling from directly inside GitHub",
"browser_specific_settings": {
diff --git a/assets/manifest.json b/assets/manifest.json
index 81329de3..605d7380 100644
--- a/assets/manifest.json
+++ b/assets/manifest.json
@@ -2,7 +2,7 @@
"manifest_version": 3,
"name": "K2 for GitHub",
- "version": "1.3.74",
+ "version": "1.4.0",
"description": "Manage your Kernel Scheduling from directly inside GitHub",
"icons": {
diff --git a/package.json b/package.json
index 2b724bae..469d3127 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "k2-extension",
- "version": "1.3.74",
+ "version": "1.4.0",
"description": "A Chrome Extension for Kernel Schedule",
"private": true,
"scripts": {
diff --git a/src/css/content.scss b/src/css/content.scss
index 4d425ae8..11ad4e4a 100644
--- a/src/css/content.scss
+++ b/src/css/content.scss
@@ -570,3 +570,28 @@ $color-dark-yellow: #DAA520;
color: rgb(230, 237, 243)
}
}
+
+.loader {
+ width: 14px;
+ aspect-ratio: 1;
+ border-radius: 50%;
+ border: 2px solid #514b82;
+ animation:
+ l20-1 0.8s infinite linear alternate,
+ l20-2 1.6s infinite linear;
+}
+@keyframes l20-1{
+ 0% {clip-path: polygon(50% 50%,0 0, 50% 0%, 50% 0%, 50% 0%, 50% 0%, 50% 0% )}
+ 12.5% {clip-path: polygon(50% 50%,0 0, 50% 0%, 100% 0%, 100% 0%, 100% 0%, 100% 0% )}
+ 25% {clip-path: polygon(50% 50%,0 0, 50% 0%, 100% 0%, 100% 100%, 100% 100%, 100% 100% )}
+ 50% {clip-path: polygon(50% 50%,0 0, 50% 0%, 100% 0%, 100% 100%, 50% 100%, 0% 100% )}
+ 62.5% {clip-path: polygon(50% 50%,100% 0, 100% 0%, 100% 0%, 100% 100%, 50% 100%, 0% 100% )}
+ 75% {clip-path: polygon(50% 50%,100% 100%, 100% 100%, 100% 100%, 100% 100%, 50% 100%, 0% 100% )}
+ 100% {clip-path: polygon(50% 50%,50% 100%, 50% 100%, 50% 100%, 50% 100%, 50% 100%, 0% 100% )}
+}
+@keyframes l20-2{
+ 0% {transform:scaleY(1) rotate(0deg)}
+ 49.99%{transform:scaleY(1) rotate(135deg)}
+ 50% {transform:scaleY(-1) rotate(0deg)}
+ 100% {transform:scaleY(-1) rotate(-135deg)}
+}
diff --git a/src/js/lib/pages/github/_base.js b/src/js/lib/pages/github/_base.js
index 72a7e59a..ae2485b6 100644
--- a/src/js/lib/pages/github/_base.js
+++ b/src/js/lib/pages/github/_base.js
@@ -1,4 +1,5 @@
import $ from 'jquery';
+import * as API from '../../api';
/**
* This class is to be extended by each of the distinct types of webpages that the extension works on
@@ -7,6 +8,43 @@ import $ from 'jquery';
export default function () {
const Page = {};
+ const REVIEWER_CHECKLIST_URL = 'https://raw.githubusercontent.com/Expensify/App/main/contributingGuides/REVIEWER_CHECKLIST.md';
+ const BUGZERO_CHECKLIST_URL = 'https://raw.githubusercontent.com/Expensify/App/main/contributingGuides/BUGZERO_CHECKLIST.md';
+
+ /**
+ * Gets the contents of the reviewer checklist from GitHub and then posts it as a comment to the current PR
+ * @param {Event} e
+ * @param {'bugzero' | 'reviewer'} checklistType Type of target checklist
+ */
+ const copyReviewerChecklist = async (e, checklistType) => {
+ const checklistUrl = checklistType === 'bugzero' ? BUGZERO_CHECKLIST_URL : REVIEWER_CHECKLIST_URL;
+
+ e.preventDefault();
+
+ // Get the button element
+ const button = e.target;
+
+ // Save the original content of the button
+ const originalContent = button.innerHTML;
+
+ // Replace the button content with a loader
+ button.innerHTML = '
';
+
+ try {
+ // Fetch the checklist contents
+ const response = await fetch(checklistUrl);
+ const fileContents = await response.text();
+
+ // Call the API to add the comment
+ await API.addComment(fileContents);
+ } catch (error) {
+ console.error('Error fetching the checklist:', error);
+ } finally {
+ // Restore the original button content
+ button.innerHTML = originalContent;
+ }
+ };
+
/**
* A unique identifier for each page
*/
@@ -47,11 +85,34 @@ export default function () {
Page.setup = function () {};
Page.getRepoOwner = function () {
- return $('.author a span').text();
+ return document.querySelectorAll('.AppHeader-context-item-label.Truncate-text')[0] // Org name next to GitHub logo
+ .textContent.trim();
};
Page.getRepo = function () {
- return $('.js-current-repository').text();
+ return document.querySelectorAll('.AppHeader-context-item-label.Truncate-text')[1] // Repo name next to GitHub logo
+ .textContent.trim();
+ };
+
+ /**
+ * Renders buttons for copying checklists in issue/PR bodies
+ * @param {'bugzero' | 'reviewer'} checklistType Type of target checklist
+ */
+ Page.renderCopyChecklistButtons = function (checklistType) {
+ // Look through all the comments on the page to find one that has the template for the copy/paste checklist button
+ // eslint-disable-next-line rulesdir/prefer-underscore-method
+ $('.markdown-body > p').each((i, el) => {
+ const commentHtml = $(el).html();
+
+ // When the button template is found, replace it with an HTML button and then put that back into the DOM so someone can click on it
+ if (commentHtml && commentHtml.indexOf('you can simply click: [this button]') > -1) {
+ const newHtml = commentHtml.replace('[this button]', '');
+ $(el).html(newHtml);
+
+ // Now that the button is on the page, add a click handler to it (always remove all handlers first so that we know there will always be one handler attached)
+ $('.k2-copy-checklist').off().on('click', e => copyReviewerChecklist(e, checklistType));
+ }
+ });
};
return Page;
diff --git a/src/js/lib/pages/github/createpr.js b/src/js/lib/pages/github/createpr.js
deleted file mode 100644
index 7a12a3a3..00000000
--- a/src/js/lib/pages/github/createpr.js
+++ /dev/null
@@ -1,30 +0,0 @@
-import $ from 'jquery';
-import Base from './_base';
-
-/**
- * This class handles what happs on the create PR page
- * the code is duplicated in pr.js so copy over any changes
- *
- * @returns {Object}
- */
-export default function () {
- const CreatePrPage = new Base();
-
- /**
- * Regex for the create new PR page
- */
- CreatePrPage.urlPath = '^(/[\\w-]+/[\\w-]+/compare/.*)$';
-
- /**
- * Runs on page load, adds qa guidelines content and event listener to show/hide the guidelines
- */
- CreatePrPage.setup = function () {
- // eslint-disable-next-line no-undef
- addQAGuidelines();
-
- // eslint-disable-next-line no-undef
- $('#k2-extension-qa-guidelines-toggle').on('change', toggleQAGuidelines);
- };
-
- return CreatePrPage;
-}
diff --git a/src/js/lib/pages/github/issue.js b/src/js/lib/pages/github/issue.js
index a537ebc5..b636041a 100644
--- a/src/js/lib/pages/github/issue.js
+++ b/src/js/lib/pages/github/issue.js
@@ -13,50 +13,16 @@ import * as API from '../../api';
let clearErrorTimeoutID;
function catchError(e) {
- $('.gh-header-actions .k2-element').remove();
- $('.gh-header-actions').append('OOPS!');
+ $('div[data-component="PH_Actions"] .k2-element').remove(); // K2 elements in action buttons
+ $('div[data-component="PH_Actions"]') // Action buttons next to issue title
+ .append('OOPS!');
console.error(e);
clearTimeout(clearErrorTimeoutID);
clearErrorTimeoutID = setTimeout(() => {
- $('.gh-header-actions .k2-element').remove();
+ $('div[data-component="PH_Actions"] .k2-element').remove();
}, 30000);
}
-/**
- * Gets the contents of the reviewer checklist from GitHub and then posts it as a comment to the current PR
- * @param {Event} e
- */
-const copyReviewerChecklist = (e) => {
- e.preventDefault();
- const pathToChecklist = 'https://raw.githubusercontent.com/Expensify/App/main/contributingGuides/BUGZERO_CHECKLIST.md';
- $.get(pathToChecklist)
- .done((fileContents) => {
- if (!fileContents) {
- console.error(`could not load contents of ${pathToChecklist} for some reason`);
- return;
- }
-
- API.addComment(fileContents);
- });
-};
-
-const renderCopyChecklistButton = () => {
- // Look through all the comments on the page to find one that has the template for the copy/paste checklist button
- // eslint-disable-next-line rulesdir/prefer-underscore-method
- $('.js-comment-body').each((i, el) => {
- const commentHtml = $(el).html();
-
- // When the button template is found, replace it with an HTML button and then put that back into the DOM so someone can click on it
- if (commentHtml && commentHtml.indexOf('you can simply click: [this button]') > -1) {
- const newHtml = commentHtml.replace('[this button]', '');
- $(el).html(newHtml);
-
- // Now that the button is on the page, add a click handler to it (always remove all handlers first so that we know there will always be one handler attached)
- $('.k2-copy-checklist').off().on('click', copyReviewerChecklist);
- }
- });
-};
-
/**
* Sets the owner of an issue when it doesn't have an owner yet
* @param {String} owner to set
@@ -104,27 +70,32 @@ function replaceOwner(oldOwner, newOwner) {
/**
* This method is all about adding the "issue owner" functionality which melvin will use to see who should be providing ksv2 updates to an issue.
+ * @param {String | null} [issueOwner] GitHub username of the issue owner. Null means no owner, undefined means get the owner from issue body
*/
-const refreshAssignees = () => {
+const renderAssignees = (issueOwner) => {
// Always start by erasing whatever was drawn before (so it always starts from a clean slate)
- $('.js-issue-assignees .k2-element').remove();
+ $('div[data-testid="sidebar-section"] .k2-element').remove();
- // Check if there is an owner for the issue
- const ghDescription = $('.comment-body').text();
- const regexResult = ghDescription.match(/Current Issue Owner:\s@(?\S+)/i);
- const currentOwner = regexResult && regexResult.groups && regexResult.groups.owner;
+ let currentOwner = issueOwner;
+
+ // if issue owner is not provided, then try to get it from the issue body
+ if (currentOwner === undefined) {
+ const ghDescription = $('.markdown-body').first().text();
+ const regexResult = ghDescription.match(/Current Issue Owner:\s@(?\S+)/i);
+ currentOwner = regexResult && regexResult.groups && regexResult.groups.owner;
+ }
// Add buttons to each assignee
- $('.js-issue-assignees > p > span').each((i, el) => {
- const assignee = $(el).find('.assignee span').text();
+ $('div[data-testid="issue-assignees"]').each((i, el) => {
+ const assignee = $(el).text();
if (assignee === currentOwner) {
- $(el).append(`
+ $(el).closest('li').append(`
`);
} else {
- $(el).append(`
+ $(el).closest('li').append(`
@@ -133,15 +104,15 @@ const refreshAssignees = () => {
});
// Remove the owner with this button is clicked
- $('.k2-button-remove-owner').off('click').on('click', (e) => {
+ $('.k2-button-remove-owner').off('click').on('click', async (e) => {
e.preventDefault();
const owner = $(e.target).data('owner');
removeOwner(owner);
- return false;
+ renderAssignees(null);
});
// Make a new owner when this button is clicked
- $('.k2-button-make-owner').off('click').on('click', (e) => {
+ $('.k2-button-make-owner').off('click').on('click', async (e) => {
e.preventDefault();
const newOwner = $(e.target).data('owner');
if (currentOwner) {
@@ -149,14 +120,15 @@ const refreshAssignees = () => {
} else {
setOwner(newOwner);
}
- return false;
+ renderAssignees(newOwner);
});
};
const refreshPicker = function () {
// Add our wrappers to the DOM which all the React components will be rendered into
if (!$('.k2picker-wrapper').length) {
- $('.js-issue-labels').after(sidebarWrapperHTML);
+ $('div[data-testid="issue-viewer-metadata-pane"] > :nth-child(3)') // Labels section in right side panel
+ .after(sidebarWrapperHTML);
}
new K2picker().draw();
@@ -172,7 +144,7 @@ const refreshPicker = function () {
* @returns {Object}
*/
export default function () {
- let allreadySetup = false;
+ let alreadySetup = false;
ReactNativeOnyx.init({
keys: ONYXKEYS,
});
@@ -183,27 +155,29 @@ export default function () {
IssuePage.setup = function () {
// Prevent this function from running twice (it sometimes does that because of how chrome triggers the extension)
- if (allreadySetup) {
+ if (alreadySetup) {
return;
}
- allreadySetup = true;
+ alreadySetup = true;
// Draw them once when the page is loaded
setTimeout(refreshPicker, 500);
- setTimeout(refreshAssignees, 500);
+ setTimeout(renderAssignees, 500);
// Every second, check to see if the pickers are still there, and if not, redraw them
setInterval(() => {
if (!$('.k2picker-wrapper').length) {
refreshPicker();
}
- if (!$('.js-issue-assignees .k2-element').length) {
- refreshAssignees();
+
+ if (!$('div[data-testid="issue-viewer-metadata-pane"] > :nth-child(2) .k2-element') // Assignee section in right side panel
+ .length) {
+ renderAssignees();
}
}, 1000);
// Waiting 2 seconds to call this gives the page enough time to load so that there is a better chance that all the comments will be rendered
- setInterval(renderCopyChecklistButton, 2000);
+ setInterval(() => IssuePage.renderCopyChecklistButtons('bugzero'), 2000);
};
return IssuePage;
diff --git a/src/js/lib/pages/github/pr.js b/src/js/lib/pages/github/pr.js
index cc568192..e115ae8e 100644
--- a/src/js/lib/pages/github/pr.js
+++ b/src/js/lib/pages/github/pr.js
@@ -1,108 +1,58 @@
import $ from 'jquery';
import Base from './_base';
-import * as API from '../../api';
-/**
- * Check whether or not the current repo allows someone to merge their own PR that they created. This is limited
- * to specific repos in infra.
- *
- * @return {boolean}
- */
-function isSelfMergingAllowed() {
- const reposAllowSelfMerge = [
- 'salt',
- 'ops-configs',
- 'terraform',
- ];
- const repoName = window.location.pathname.split('/')[2].toLowerCase();
- return reposAllowSelfMerge.indexOf(repoName) > -1;
-}
-
-/**
- * Gets the contents of the reviewer checklist from GitHub and then posts it as a comment to the current PR
- * @param {Event} e
- */
-const copyReviewerChecklist = (e) => {
- e.preventDefault();
- const pathToChecklist = 'https://raw.githubusercontent.com/Expensify/App/main/contributingGuides/REVIEWER_CHECKLIST.md';
- $.get(pathToChecklist)
- .done((fileContents) => {
- if (!fileContents) {
- console.error(`could not load contents of ${pathToChecklist} for some reason`);
- return;
- }
-
- API.addComment(fileContents);
- });
-};
-
-const renderCopyChecklistButton = () => {
- // Look through all the comments on the page to find one that has the template for the copy/paste checklist button
- // eslint-disable-next-line rulesdir/prefer-underscore-method
- $('.js-comment-body').each((i, el) => {
- const commentHtml = $(el).html();
+const refreshHold = function () {
+ const prTitle = $('.js-issue-title').text();
- // When the button template is found, replace it with an HTML button and then put that back into the DOM so someone can click on it
- if (commentHtml && commentHtml.indexOf('you can simply click: [this button]') > -1) {
- const newHtml = commentHtml.replace('[this button]', '');
- $(el).html(newHtml);
+ const isNewMergeUI = $('div[data-testid="mergebox-partial"]').length;
- // Now that the button is on the page, add a click handler to it (always remove all handlers first so that we know there will always be one handler attached)
- $('.k2-copy-checklist').off().on('click', copyReviewerChecklist);
+ // Classic merge experience
+ if (!isNewMergeUI) {
+ if (prTitle.toLowerCase().indexOf('[hold') > -1 || prTitle.toLowerCase().indexOf('[wip') > -1) {
+ $('.branch-action') // Entire PR merge section
+ .removeClass('branch-action-state-clean')
+ .addClass('branch-action-state-dirty');
+ $('.merge-message button') // Merge pull request button
+ .removeClass('btn-primary')
+ .attr('disabled', 'disabled');
+ // eslint-disable-next-line rulesdir/prefer-underscore-method
+ $('.branch-action-item').last().find('.completeness-indicator') // "Merging status" section above the merge button
+ .removeClass('completeness-indicator-success')
+ .addClass('completeness-indicator-problem')
+ .end()
+ .find('.status-heading') // Header for the "merging status" section
+ .text('This pull request has a hold on it and cannot be merged')
+ .end()
+ .find('.status-meta') // Body text for the "merging status" section
+ .html('Remove the HOLD or WIP label from the title of the PR to make it mergeable')
+ .end()
+ .find('.octicon')
+ .removeClass('octicon-check')
+ .addClass('octicon-alert');
}
- });
-};
-
-const refreshHold = function () {
- const prTitle = $('.js-issue-title').text();
- const prAuthor = $('.pull-header-username').text();
- const getCurrentUser = API.getCurrentUser();
- const branchName = $('.head-ref').text();
+ return;
+ }
if (prTitle.toLowerCase().indexOf('[hold') > -1 || prTitle.toLowerCase().indexOf('[wip') > -1) {
- $('.branch-action')
- .removeClass('branch-action-state-clean')
- .addClass('branch-action-state-dirty');
- $('.merge-message button')
- .removeClass('btn-primary')
+ $('div[data-testid="mergebox-partial"] > div > div:last-of-type') // Entire PR merge section
+ .removeClass('borderColor-success-emphasis');
+ $('div[data-testid="mergebox-partial"] > div > div > div button').first() // Merge pull request button
+ .css({backgroundColor: 'var(--bgColor-neutral-muted)', borderColor: 'var(--bgColor-neutral-muted)'})
.attr('disabled', 'disabled');
- // eslint-disable-next-line rulesdir/prefer-underscore-method
- $('.branch-action-item').last().find('.completeness-indicator')
- .removeClass('completeness-indicator-success')
- .addClass('completeness-indicator-problem')
- .end()
- .find('.status-heading')
- .text('This pull request has a hold on it and cannot be merged')
- .end()
- .find('.status-meta')
- .html('Remove the HOLD or WIP label from the title of the PR to make it mergeable')
- .end()
- .find('.octicon')
- .removeClass('octicon-check')
- .addClass('octicon-alert');
- }
-
- if (!(branchName.toLowerCase() === 'master' || branchName.toLowerCase() === 'main') && !isSelfMergingAllowed() && getCurrentUser === prAuthor) {
- $('.branch-action')
- .removeClass('branch-action-state-clean')
- .addClass('branch-action-state-dirty');
- $('.merge-message button')
- .removeClass('btn-primary')
+ $('div[data-testid="mergebox-partial"] > div > div button[data-component="IconButton"]').first() // Dropdown button next to merge button
+ .css({backgroundColor: 'var(--bgColor-neutral-muted)', borderColor: 'var(--bgColor-neutral-muted)'})
.attr('disabled', 'disabled');
- // eslint-disable-next-line rulesdir/prefer-underscore-method
- $('.branch-action-item').last().find('.completeness-indicator')
- .removeClass('completeness-indicator-success')
- .addClass('completeness-indicator-problem')
- .end()
- .find('.status-heading')
- .text('You cannot merge your own PR.')
- .end()
- .find('.status-meta')
- .html('I\'m sorry Dave, I\'m afraid you can\'t merge your own PR')
- .end()
- .find('.octicon')
- .removeClass('octicon-check')
- .addClass('octicon-alert');
+ $('div[data-testid="mergebox-partial"] > div > div > div > div > div') // Container for merge pull request button
+ .css({borderColor: 'var(--bgColor-neutral-muted)'});
+ $('div[data-testid="mergeability-icon-wrapper"] div').css({backgroundColor: 'var(--bgColor-neutral-emphasis)'}); // Icon on the left side of the merge panel
+ $('div[data-testid="mergebox-partial"] > div > div > section:last-of-type svg') // "Merging status" section above the merge button
+ .parent()
+ .removeClass('bgColor-success-emphasis')
+ .css({backgroundColor: 'var(--bgColor-neutral-emphasis)'});
+ $('div[data-testid="mergebox-partial"] > div > div > section:last-of-type h3') // Header for the "merging status" section
+ .text('This pull request has a hold on it and cannot be merged');
+ $('div[data-testid="mergebox-partial"] > div > div > section:last-of-type p') // Body text for the "merging status" section
+ .html('Remove the HOLD or WIP label from the title of the PR to make it mergeable');
}
};
@@ -126,8 +76,9 @@ export default function () {
setInterval(refreshHold, 1000);
// Waiting 2 seconds to call this gives the page enough time to load so that there is a better chance that all the comments will be rendered
- setInterval(renderCopyChecklistButton, 2000);
+ setInterval(() => PrPage.renderCopyChecklistButtons('reviewer'), 2000);
};
return PrPage;
}
+
diff --git a/src/js/module/K2picker/K2PickerPicker.js b/src/js/module/K2picker/K2PickerPicker.js
index 6b26cd7c..d37f0098 100644
--- a/src/js/module/K2picker/K2PickerPicker.js
+++ b/src/js/module/K2picker/K2PickerPicker.js
@@ -20,7 +20,7 @@ class K2PickerPicker extends React.Component {
componentDidMount() {
// eslint-disable-next-line rulesdir/prefer-underscore-method
- $('.js-issue-labels .IssueLabel').each((i, el) => {
+ $('div[data-testid="issue-labels"] a > span').each((i, el) => {
const label = $(el).text().trim();
if (['Hourly', 'Daily', 'Weekly', 'Monthly'].indexOf(label) > -1) {
this.setActiveLabel(label);
diff --git a/src/js/module/K2pickerarea/K2PickerareaPicker.js b/src/js/module/K2pickerarea/K2PickerareaPicker.js
index 6266b0a9..cf9ad186 100644
--- a/src/js/module/K2pickerarea/K2PickerareaPicker.js
+++ b/src/js/module/K2pickerarea/K2PickerareaPicker.js
@@ -36,7 +36,7 @@ class K2PickerareaPicker extends React.Component {
},
};
// eslint-disable-next-line rulesdir/prefer-underscore-method
- $('.js-issue-labels .IssueLabel').each((i, el) => {
+ $('div[data-testid="issue-labels"] a > span').each((i, el) => {
const label = $(el).text().trim();
if (this.state[label]) {
this.state[label].className = this.state[label].className.replace('inactive', 'active');
diff --git a/src/js/module/K2pickertype/K2PickertypePicker.js b/src/js/module/K2pickertype/K2PickertypePicker.js
index 365fd6ba..d65fbea8 100644
--- a/src/js/module/K2pickertype/K2PickertypePicker.js
+++ b/src/js/module/K2pickertype/K2PickertypePicker.js
@@ -25,7 +25,7 @@ class K2PickertypePicker extends React.Component {
*/
componentDidMount() {
// eslint-disable-next-line rulesdir/prefer-underscore-method
- $('.js-issue-labels .IssueLabel').each((i, el) => {
+ $('div[data-testid="issue-labels"] a > span').each((i, el) => {
const label = $(el).text().trim();
if (['Improvement', 'Task', 'NewFeature'].indexOf(label) > -1) {
this.setActiveLabel(label);
diff --git a/src/js/module/ToggleReview/Toggle.js b/src/js/module/ToggleReview/Toggle.js
index d6ee3558..266562a9 100644
--- a/src/js/module/ToggleReview/Toggle.js
+++ b/src/js/module/ToggleReview/Toggle.js
@@ -17,7 +17,7 @@ class Toggle extends React.Component {
componentDidMount() {
// eslint-disable-next-line rulesdir/prefer-underscore-method
- $('.js-issue-labels .IssueLabel').each((i, el) => {
+ $('div[data-testid="issue-labels"] a > span').each((i, el) => {
const label = $(el).text().trim();
if (['Reviewing'].indexOf(label) > -1) {
this.setActiveLabel(label);