Skip to content

Commit

Permalink
Merge pull request #123 from guardian/ts/refactor-class-components
Browse files Browse the repository at this point in the history
Refactor more class components
  • Loading branch information
tjsilver committed Aug 27, 2020
2 parents 2ca1b82 + b063616 commit 9cfa0d2
Show file tree
Hide file tree
Showing 3 changed files with 151 additions and 154 deletions.
7 changes: 2 additions & 5 deletions src/ts/components/Controls.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ const getErrorFeedbackLink = (pluginState: IPluginState<IMatch> | undefined, fee
/**
* Controls to open and close Typerighter and check document.
*/
const controls = ({
const Controls = ({
store,
requestMatchesForDocument,
fetchCategories,
Expand Down Expand Up @@ -70,9 +70,6 @@ const controls = ({
setPluginState(store.getState());

fetchAllCategories();

return () =>
store.removeEventListener(STORE_EVENT_NEW_STATE, setPluginState);
}, []);

const pluginIsActive =
Expand Down Expand Up @@ -166,4 +163,4 @@ const controls = ({
}


export default controls;
export default Controls;
124 changes: 60 additions & 64 deletions src/ts/components/Results.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { Component } from "react";
import React, { useState, useEffect } from "react";
import sortBy from "lodash/sortBy";
import Store, { STORE_EVENT_NEW_STATE } from "../state/store";
import { ApplySuggestionOptions } from "../commands";
Expand All @@ -22,33 +22,67 @@ interface IProps {
/**
* Displays current matches and allows users to apply suggestions.
*/
class Results extends Component<
IProps,
{
pluginState: IPluginState<IMatch> | undefined;
groupResults: boolean;
loadingBarVisible: boolean;
}
> {
public componentWillMount() {
this.props.store.on(STORE_EVENT_NEW_STATE, this.handleNewState);
this.setState({ pluginState: this.props.store.getState() });
}

public render() {
const {
applySuggestions,
selectMatch,
indicateHighlight,
stopHighlight,
contactHref,
editorScrollElement,
getScrollOffset
} = this.props;
const { pluginState } = this.state;
const Results = ({
store,
applySuggestions,
selectMatch,
indicateHighlight,
stopHighlight,
contactHref,
editorScrollElement,
getScrollOffset
}: IProps) => {

const [pluginState, setPluginState] = useState<IPluginState<IMatch> | undefined>(undefined);
const [loadingBarVisible, setLoadingBarVisible] = useState<boolean>(false);

const handleNewState = (pluginState: IPluginState<IMatch>) => {
setPluginState({
...pluginState,
currentMatches: sortBy(pluginState.currentMatches, "from")

});
const oldKeys = pluginState
? Object.keys(pluginState.requestsInFlight)
: [];
const newKeys = Object.keys(pluginState.requestsInFlight);
if (oldKeys.length && !newKeys.length) {
setTimeout(maybeResetLoadingBar, 300);
}
if (!loadingBarVisible && newKeys.length) {
setLoadingBarVisible(true);
}
};

useEffect(() => {
store.on(STORE_EVENT_NEW_STATE, newState => {
handleNewState(newState);
});
setPluginState(store.getState());
}, []);

const getPercentRemaining = () => {
if (!pluginState) {
return 0;
}
return selectPercentRemaining(pluginState);
};



const maybeResetLoadingBar = () => {
if (
!pluginState ||
!!Object.keys(pluginState.requestsInFlight)
) {
setLoadingBarVisible(false);
}
};

const { currentMatches = [], requestsInFlight, selectedMatch } = pluginState || { selectedMatch: undefined };
const hasMatches = !!(currentMatches && currentMatches.length);
const percentRemaining = this.getPercentRemaining();
const percentRemaining = getPercentRemaining();
const isLoading =
!!requestsInFlight && !!Object.keys(requestsInFlight).length;

Expand All @@ -68,7 +102,7 @@ class Results extends Component<
</a>
</div>
)}
{this.state.loadingBarVisible && (
{loadingBarVisible && (
<div
className="LoadingBar"
style={{
Expand Down Expand Up @@ -107,42 +141,4 @@ class Results extends Component<
);
}

private handleNewState = (pluginState: IPluginState<IMatch>) => {
this.setState({
pluginState: {
...pluginState,
currentMatches: sortBy(pluginState.currentMatches, "from")
}
});
const oldKeys = this.state.pluginState
? Object.keys(this.state.pluginState.requestsInFlight)
: [];
const newKeys = Object.keys(pluginState.requestsInFlight);
if (oldKeys.length && !newKeys.length) {
setTimeout(this.maybeResetLoadingBar, 300);
}
if (!this.state.loadingBarVisible && newKeys.length) {
this.setState({ loadingBarVisible: true });
}
};

private maybeResetLoadingBar = () => {
if (
!this.state.pluginState ||
!!Object.keys(this.state.pluginState.requestsInFlight)
) {
this.setState({ loadingBarVisible: false });
}
};

private getPercentRemaining = () => {
const state = this.state.pluginState;
if (!state) {
return 0;
}
return selectPercentRemaining(state);
};

}

export default Results;
174 changes: 89 additions & 85 deletions src/ts/components/SidebarMatch.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import compact from "lodash/compact";

import React, { Component } from "react";
import React, { useState } from "react";

import { IMatch } from "../interfaces/IMatch";
import {
Expand All @@ -25,97 +25,42 @@ interface IProps {
getScrollOffset: () => number;
}

interface IState {
isOpen: boolean;
}

/**
* Display information for a single match
*/
class SidebarMatch extends Component<IProps, IState> {
public state = {
isOpen: false
};

public render() {
const { match, matchColours, applySuggestions, selectedMatch } = this.props;
const color = getColourForMatch(match, matchColours, false).borderColour;
const hasSuggestions = !!match.suggestions && !!match.suggestions.length;
const suggestions = compact([
match.replacement,
...(match.suggestions || [])
]);
return (
<div
className={`SidebarMatch__container ${
selectedMatch === match.matchId
? "SidebarMatch__container--is-selected"
: ""
}`}
style={{ borderLeft: `2px solid ${color}` }}
onMouseEnter={this.handleMouseEnter}
onMouseLeave={this.handleMouseLeave}
onClick={this.scrollToRange}
title="Click to scroll to this match"
>
<div
className={"SidebarMatch__header"}
onClick={hasSuggestions ? this.toggleOpen : undefined}
>
<div className="SidebarMatch__header-label">
<div>
<div className="SidebarMatch__header-match-text">
{match.matchedText}
</div>
<div
className="SidebarMatch__header-description"
dangerouslySetInnerHTML={{
__html: getHtmlFromMarkdown(match.message)
}}
></div>
</div>
<div className="SidebarMatch__header-meta">
<div className="SidebarMatch__header-category">
{titleCase(match.category.name)}
</div>
{hasSuggestions && (
<div className="SidebarMatch__header-toggle-status">
{this.state.isOpen ? "-" : "+"}
</div>
)}
</div>
</div>
</div>
{this.state.isOpen && (
<div className="SidebarMatch__content">
{suggestions.length && (
<div className="SidebarMatch__suggestion-list">
<SuggestionList
applySuggestions={applySuggestions}
matchId={match.matchId}
matchedText={match.matchedText}
suggestions={suggestions}
/>
</div>
)}
</div>
)}
</div>
);
}
private toggleOpen = () => {
this.setState({ isOpen: !this.state.isOpen });
const SidebarMatch = ({
match,
matchColours,
applySuggestions,
indicateHighlight,
stopHighlight,
selectedMatch,
editorScrollElement,
getScrollOffset,
}: IProps) => {

const [isOpen, setIsOpen] = useState<boolean>(false);

const color = getColourForMatch(match, matchColours, false).borderColour;
const hasSuggestions = !!match.suggestions && !!match.suggestions.length;
const suggestions = compact([
match.replacement,
...(match.suggestions || [])
]);

const toggleOpen = () => {
setIsOpen(!isOpen);
};

private scrollToRange = (e: React.MouseEvent<HTMLDivElement, MouseEvent>) => {
const scrollToRange = (e: React.MouseEvent<HTMLDivElement, MouseEvent>) => {
e.preventDefault();
e.stopPropagation();

const { editorScrollElement, match, getScrollOffset } = this.props;
if (!editorScrollElement) {
return;
}

const decorationElement = maybeGetDecorationElement(match.matchId);

if (decorationElement) {
Expand All @@ -127,13 +72,72 @@ class SidebarMatch extends Component<IProps, IState> {
}
};

private handleMouseEnter = () => {
this.props.indicateHighlight(this.props.match.matchId);
const handleMouseEnter = () => {
indicateHighlight(match.matchId);
};

private handleMouseLeave = () => {
this.props.stopHighlight();
const handleMouseLeave = () => {
stopHighlight();
};
}


return (
<div
className={`SidebarMatch__container ${
selectedMatch === match.matchId
? "SidebarMatch__container--is-selected"
: ""
}`}
style={{ borderLeft: `2px solid ${color}` }}
onMouseEnter={handleMouseEnter}
onMouseLeave={handleMouseLeave}
onClick={scrollToRange}
title="Click to scroll to this match"
>
<div
className={"SidebarMatch__header"}
onClick={hasSuggestions ? toggleOpen : undefined}
>
<div className="SidebarMatch__header-label">
<div>
<div className="SidebarMatch__header-match-text">
{match.matchedText}
</div>
<div
className="SidebarMatch__header-description"
dangerouslySetInnerHTML={{
__html: getHtmlFromMarkdown(match.message)
}}
></div>
</div>
<div className="SidebarMatch__header-meta">
<div className="SidebarMatch__header-category">
{titleCase(match.category.name)}
</div>
{hasSuggestions && (
<div className="SidebarMatch__header-toggle-status">
{isOpen ? "-" : "+"}
</div>
)}
</div>
</div>
</div>
{isOpen && (
<div className="SidebarMatch__content">
{suggestions.length && (
<div className="SidebarMatch__suggestion-list">
<SuggestionList
applySuggestions={applySuggestions}
matchId={match.matchId}
matchedText={match.matchedText}
suggestions={suggestions}
/>
</div>
)}
</div>
)}
</div>
);
};

export default SidebarMatch;

0 comments on commit 9cfa0d2

Please sign in to comment.