From: Philippe Perrin Date: Fri, 29 Apr 2022 14:29:10 +0000 (+0200) Subject: SONAR-16346 Replace React legacy lifecycle methods in the issue app X-Git-Tag: 9.5.0.56709~182 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=3e6e2824174be0caecbb34cf133c22b711935683;p=sonarqube.git SONAR-16346 Replace React legacy lifecycle methods in the issue app --- diff --git a/server/sonar-web/src/main/js/apps/issues/components/IssuesApp.tsx b/server/sonar-web/src/main/js/apps/issues/components/IssuesApp.tsx index b00454f3fca..05089203608 100644 --- a/server/sonar-web/src/main/js/apps/issues/components/IssuesApp.tsx +++ b/server/sonar-web/src/main/js/apps/issues/components/IssuesApp.tsx @@ -75,6 +75,7 @@ import { areMyIssuesSelected, areQueriesEqual, getOpen, + getOpenIssue, parseFacets, parseQuery, Query, @@ -175,6 +176,18 @@ export default class App extends React.PureComponent { this.refreshBranchStatus = debounce(this.refreshBranchStatus, BRANCH_STATUS_REFRESH_INTERVAL); } + static getDerivedStateFromProps(props: Props, state: State) { + const { + location: { query } + } = props; + + return { + myIssues: areMyIssuesSelected(query), + query: parseQuery(query), + openIssue: getOpenIssue(props, state.issues) + }; + } + componentDidMount() { this.mounted = true; @@ -189,33 +202,11 @@ export default class App extends React.PureComponent { this.fetchFirstIssues(); } - componentWillReceiveProps(nextProps: Props) { - const { issues, selected } = this.state; - const openIssue = this.getOpenIssue(nextProps, issues); - - if (openIssue && openIssue.key !== selected) { - this.setState({ - locationsNavigator: false, - selected: openIssue.key, - selectedFlowIndex: undefined, - selectedLocationIndex: undefined - }); - } - - if (!openIssue) { - this.setState({ selectedFlowIndex: undefined, selectedLocationIndex: undefined }); - } - - this.setState({ - myIssues: areMyIssuesSelected(nextProps.location.query), - openIssue, - query: parseQuery(nextProps.location.query) - }); - } - componentDidUpdate(prevProps: Props, prevState: State) { const { query } = this.props.location; const { query: prevQuery } = prevProps.location; + const { openIssue } = this.state; + if ( prevProps.component !== this.props.component || !isSameBranchLike(prevProps.branchLike, this.props.branchLike) || @@ -231,6 +222,13 @@ export default class App extends React.PureComponent { // if user simply selected another issue // or if user went from the source code back to the list of issues this.scrollToSelectedIssue(); + } else if (openIssue && openIssue.key !== this.state.selected) { + this.setState({ + locationsNavigator: false, + selected: openIssue.key, + selectedFlowIndex: undefined, + selectedLocationIndex: undefined + }); } } @@ -308,11 +306,6 @@ export default class App extends React.PureComponent { return index !== -1 ? index : undefined; } - getOpenIssue = (props: Props, issues: Issue[]) => { - const open = getOpen(props.location.query); - return open ? issues.find(issue => issue.key === open) : undefined; - }; - selectNextIssue = () => { const { issues } = this.state; const selectedIndex = this.getSelectedIndex(); @@ -477,7 +470,7 @@ export default class App extends React.PureComponent { return fetchPromise.then( ({ effortTotal, facets, issues, paging, ...other }) => { if (this.mounted && areQueriesEqual(prevQuery, this.props.location.query)) { - const openIssue = this.getOpenIssue(this.props, issues); + const openIssue = getOpenIssue(this.props, issues); let selected: string | undefined = undefined; if (issues.length > 0) { selected = openIssue ? openIssue.key : issues[0].key; @@ -791,8 +784,7 @@ export default class App extends React.PureComponent { handleIssueChange = (issue: Issue) => { this.refreshBranchStatus(); this.setState(state => ({ - issues: state.issues.map(candidate => (candidate.key === issue.key ? issue : candidate)), - openIssue: state.openIssue && state.openIssue.key === issue.key ? issue : state.openIssue + issues: state.issues.map(candidate => (candidate.key === issue.key ? issue : candidate)) })); }; diff --git a/server/sonar-web/src/main/js/apps/issues/components/__tests__/IssuesApp-test.tsx b/server/sonar-web/src/main/js/apps/issues/components/__tests__/IssuesApp-test.tsx index 7b9c514592f..1727414d517 100644 --- a/server/sonar-web/src/main/js/apps/issues/components/__tests__/IssuesApp-test.tsx +++ b/server/sonar-web/src/main/js/apps/issues/components/__tests__/IssuesApp-test.tsx @@ -546,15 +546,22 @@ it('should refresh branch status if issues are updated', async () => { }); it('should update the open issue when it is changed', async () => { + (searchIssues as jest.Mock).mockImplementation(mockSearchIssuesResponse()); + const wrapper = shallowRender(); await waitAndUpdate(wrapper); - wrapper.setState({ openIssue: ISSUES[0] }); + const issue = wrapper.state().issues[0]; + wrapper.setProps({ location: mockLocation({ query: { open: issue.key } }) }); + await waitAndUpdate(wrapper); + + expect(wrapper.state().openIssue).toEqual(issue); - const updatedIssue: Issue = { ...ISSUES[0], type: 'SECURITY_HOTSPOT' }; + const updatedIssue: Issue = { ...issue, type: 'SECURITY_HOTSPOT' }; wrapper.instance().handleIssueChange(updatedIssue); - expect(wrapper.state().openIssue).toBe(updatedIssue); + await waitAndUpdate(wrapper); + expect(wrapper.state().openIssue).toEqual(updatedIssue); }); it('should handle createAfter query param with time', async () => { diff --git a/server/sonar-web/src/main/js/apps/issues/utils.ts b/server/sonar-web/src/main/js/apps/issues/utils.ts index 0805f292f4e..590e1a4af58 100644 --- a/server/sonar-web/src/main/js/apps/issues/utils.ts +++ b/server/sonar-web/src/main/js/apps/issues/utils.ts @@ -117,6 +117,11 @@ export function getOpen(query: RawQuery): string | undefined { return query.open; } +export function getOpenIssue(props: { location: { query: RawQuery } }, issues: Issue[]) { + const open = getOpen(props.location.query); + return open ? issues.find(issue => issue.key === open) : undefined; +} + export const areMyIssuesSelected = (query: RawQuery) => query.myIssues === 'true'; export function serializeQuery(query: Query): RawQuery {