window.HTMLElement.prototype.scrollIntoView = jest.fn();
});
+it('should navigate to Why is this an issue tab', async () => {
+ renderProjectIssuesApp('project/issues?issues=issue2&open=issue2&id=myproject&why=1');
+ expect(
+ await screen.findByRole('tab', {
+ name: `coding_rules.description_section.title.root_cause`,
+ selected: true,
+ })
+ ).toBeInTheDocument();
+});
+
//Improve this to include all the bulk change fonctionality
it('should be able to bulk change', async () => {
const user = userEvent.setup();
width: 800px;
}
-.issues .issue {
- border: 2px solid transparent;
- cursor: pointer;
-}
-
-.issues .issue:focus-within,
-.issues .issue:hover {
- border: 2px dashed var(--blue);
- transition: all 0.3s ease;
- outline: 0;
-}
-
.issues .issue a:focus,
.issues .issue button:focus {
box-shadow: none;
padding-top: var(--gridSize);
padding-bottom: var(--gridSize);
background-color: var(--issueBgColor);
- transition: all 0.3s ease, border 0s ease;
+ transition: all 0.3s ease;
+ border: 2px solid transparent;
cursor: pointer;
}
margin-top: 5px;
}
-.issue.selected + .issue {
- border-top-color: transparent;
-}
-
.issue-row {
display: flex;
margin-bottom: 5px;
.issue-row-meta {
padding-right: 5px;
white-space: nowrap;
- margin-top: 2px;
}
.issue-message {
}
.issue-meta {
- line-height: 16px;
+ line-height: var(--smallFontSize);
font-size: var(--smallFontSize);
display: flex;
}
white-space: nowrap;
}
-.issue-see-rule {
- border-bottom: none;
- font-size: var(--smallFontSize);
- margin-top: 5px;
-}
-
.issue-changelog {
width: 450px;
max-height: 320px;
background-color: var(--secondIssueBgColor);
}
-.issue-message-box.secondary-issue:hover {
+.issue-message-box.secondary-issue:hover,
+.issue:focus-within,
+.issue:hover {
border: 2px dashed var(--blue);
outline: 0;
cursor: pointer;
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import * as React from 'react';
-import { ButtonLink } from '../../../components/controls/buttons';
+import { Link } from 'react-router-dom';
+import { getBranchLikeQuery } from '../../../helpers/branch-like';
import { translate } from '../../../helpers/l10n';
-import { MessageFormatting } from '../../../types/issues';
+import { getComponentIssuesUrl } from '../../../helpers/urls';
+import { BranchLike } from '../../../types/branch-like';
import { RuleStatus } from '../../../types/rules';
-import { WorkspaceContext } from '../../workspace/context';
+import { Issue } from '../../../types/types';
import { IssueMessageHighlighting } from '../IssueMessageHighlighting';
import IssueMessageTags from './IssueMessageTags';
export interface IssueMessageProps {
- engine?: string;
- quickFixAvailable?: boolean;
+ issue: Issue;
+ branchLike?: BranchLike;
displayWhyIsThisAnIssue?: boolean;
- message: string;
- messageFormattings?: MessageFormatting[];
- ruleKey: string;
- ruleStatus?: RuleStatus;
}
export default function IssueMessage(props: IssueMessageProps) {
- const {
- engine,
- quickFixAvailable,
- message,
- messageFormattings,
- ruleKey,
- ruleStatus,
- displayWhyIsThisAnIssue,
- } = props;
+ const { issue, branchLike, displayWhyIsThisAnIssue } = props;
- const { openRule } = React.useContext(WorkspaceContext);
+ const { externalRuleEngine, quickFixAvailable, message, messageFormattings, ruleStatus } = issue;
+
+ const whyIsThisAnIssueUrl = getComponentIssuesUrl(issue.project, {
+ ...getBranchLikeQuery(branchLike),
+ files: issue.componentLongName,
+ open: issue.key,
+ resolved: 'false',
+ why: '1',
+ });
return (
<>
<IssueMessageHighlighting message={message} messageFormattings={messageFormattings} />
</span>
<IssueMessageTags
- engine={engine}
+ engine={externalRuleEngine}
quickFixAvailable={quickFixAvailable}
- ruleStatus={ruleStatus}
+ ruleStatus={ruleStatus as RuleStatus | undefined}
/>
</div>
{displayWhyIsThisAnIssue && (
- <ButtonLink
+ <Link
aria-label={translate('issue.why_this_issue.long')}
- className="issue-see-rule spacer-right text-baseline"
- onClick={() =>
- openRule({
- key: ruleKey,
- })
- }
+ className="spacer-right"
+ target="_blank"
+ to={whyIsThisAnIssueUrl}
>
{translate('issue.why_this_issue')}
- </ButtonLink>
+ </Link>
)}
</>
);
import { formatMeasure } from '../../../helpers/measures';
import { getComponentIssuesUrl } from '../../../helpers/urls';
import { BranchLike } from '../../../types/branch-like';
-import { RuleStatus } from '../../../types/rules';
import { Issue } from '../../../types/types';
import LocationIndex from '../../common/LocationIndex';
import IssueChangelog from './IssueChangelog';
return (
<div className="issue-row">
<IssueMessage
- engine={issue.externalRuleEngine}
- quickFixAvailable={issue.quickFixAvailable}
+ issue={issue}
+ branchLike={props.branchLike}
displayWhyIsThisAnIssue={displayWhyIsThisAnIssue}
- message={issue.message}
- messageFormattings={issue.messageFormattings}
- ruleKey={issue.rule}
- ruleStatus={issue.ruleStatus as RuleStatus | undefined}
/>
<div className="issue-row-meta">
<div className="issue-meta-list">
*/
import { shallow } from 'enzyme';
import * as React from 'react';
+import { mockBranch } from '../../../../helpers/mocks/branch-like';
+import { mockIssue } from '../../../../helpers/testMocks';
import { RuleStatus } from '../../../../types/rules';
-import { ButtonLink } from '../../../controls/buttons';
import IssueMessage, { IssueMessageProps } from '../IssueMessage';
jest.mock('react', () => {
it('should render correctly', () => {
expect(shallowRender()).toMatchSnapshot('default');
- expect(shallowRender({ engine: 'js' })).toMatchSnapshot('with engine info');
- expect(shallowRender({ quickFixAvailable: true })).toMatchSnapshot('with quick fix');
- expect(shallowRender({ ruleStatus: RuleStatus.Deprecated })).toMatchSnapshot(
- 'is deprecated rule'
+ expect(shallowRender({ issue: mockIssue(false, { externalRuleEngine: 'js' }) })).toMatchSnapshot(
+ 'with engine info'
);
- expect(shallowRender({ ruleStatus: RuleStatus.Removed })).toMatchSnapshot('is removed rule');
+ expect(shallowRender({ issue: mockIssue(false, { quickFixAvailable: true }) })).toMatchSnapshot(
+ 'with quick fix'
+ );
+ expect(
+ shallowRender({ issue: mockIssue(false, { ruleStatus: RuleStatus.Deprecated }) })
+ ).toMatchSnapshot('is deprecated rule');
+ expect(
+ shallowRender({ issue: mockIssue(false, { ruleStatus: RuleStatus.Removed }) })
+ ).toMatchSnapshot('is removed rule');
expect(shallowRender({ displayWhyIsThisAnIssue: false })).toMatchSnapshot(
'hide why is it an issue'
);
});
-it('should open why is this an issue workspace', () => {
- const openRule = jest.fn();
- (React.useContext as jest.Mock).mockImplementationOnce(() => ({
- externalRulesRepoNames: {},
- openRule,
- }));
- const wrapper = shallowRender();
- wrapper.find(ButtonLink).simulate('click');
-
- expect(openRule).toHaveBeenCalled();
-});
-
function shallowRender(props: Partial<IssueMessageProps> = {}) {
return shallow<IssueMessageProps>(
<IssueMessage
- message="Reduce the number of conditional operators (4) used in the expression"
+ issue={mockIssue(false, {
+ message: 'Reduce the number of conditional operators (4) used in the expression',
+ })}
displayWhyIsThisAnIssue={true}
- ruleKey="javascript:S1067"
+ branchLike={mockBranch()}
{...props}
/>
);
</span>
<IssueMessageTags />
</div>
- <ButtonLink
+ <Link
aria-label="issue.why_this_issue.long"
- className="issue-see-rule spacer-right text-baseline"
- onClick={[Function]}
+ className="spacer-right"
+ target="_blank"
+ to={
+ {
+ "hash": "",
+ "pathname": "/project/issues",
+ "search": "?branch=branch-6.7&files=main.js&open=AVsae-CQS-9G3txfbFN2&resolved=false&why=1&id=myproject",
+ }
+ }
>
issue.why_this_issue
- </ButtonLink>
+ </Link>
</Fragment>
`;
ruleStatus="DEPRECATED"
/>
</div>
- <ButtonLink
+ <Link
aria-label="issue.why_this_issue.long"
- className="issue-see-rule spacer-right text-baseline"
- onClick={[Function]}
+ className="spacer-right"
+ target="_blank"
+ to={
+ {
+ "hash": "",
+ "pathname": "/project/issues",
+ "search": "?branch=branch-6.7&files=main.js&open=AVsae-CQS-9G3txfbFN2&resolved=false&why=1&id=myproject",
+ }
+ }
>
issue.why_this_issue
- </ButtonLink>
+ </Link>
</Fragment>
`;
ruleStatus="REMOVED"
/>
</div>
- <ButtonLink
+ <Link
aria-label="issue.why_this_issue.long"
- className="issue-see-rule spacer-right text-baseline"
- onClick={[Function]}
+ className="spacer-right"
+ target="_blank"
+ to={
+ {
+ "hash": "",
+ "pathname": "/project/issues",
+ "search": "?branch=branch-6.7&files=main.js&open=AVsae-CQS-9G3txfbFN2&resolved=false&why=1&id=myproject",
+ }
+ }
>
issue.why_this_issue
- </ButtonLink>
+ </Link>
</Fragment>
`;
engine="js"
/>
</div>
- <ButtonLink
+ <Link
aria-label="issue.why_this_issue.long"
- className="issue-see-rule spacer-right text-baseline"
- onClick={[Function]}
+ className="spacer-right"
+ target="_blank"
+ to={
+ {
+ "hash": "",
+ "pathname": "/project/issues",
+ "search": "?branch=branch-6.7&files=main.js&open=AVsae-CQS-9G3txfbFN2&resolved=false&why=1&id=myproject",
+ }
+ }
>
issue.why_this_issue
- </ButtonLink>
+ </Link>
</Fragment>
`;
quickFixAvailable={true}
/>
</div>
- <ButtonLink
+ <Link
aria-label="issue.why_this_issue.long"
- className="issue-see-rule spacer-right text-baseline"
- onClick={[Function]}
+ className="spacer-right"
+ target="_blank"
+ to={
+ {
+ "hash": "",
+ "pathname": "/project/issues",
+ "search": "?branch=branch-6.7&files=main.js&open=AVsae-CQS-9G3txfbFN2&resolved=false&why=1&id=myproject",
+ }
+ }
>
issue.why_this_issue
- </ButtonLink>
+ </Link>
</Fragment>
`;
className="issue-row"
>
<IssueMessage
- engine="foo"
- message="Reduce the number of conditional operators (4) used in the expression"
- ruleKey="javascript:S1067"
+ issue={
+ {
+ "actions": [],
+ "component": "main.js",
+ "componentEnabled": true,
+ "componentLongName": "main.js",
+ "componentQualifier": "FIL",
+ "componentUuid": "foo1234",
+ "creationDate": "2017-03-01T09:36:01+0100",
+ "externalRuleEngine": "foo",
+ "flows": [],
+ "flowsWithType": [],
+ "key": "AVsae-CQS-9G3txfbFN2",
+ "line": 25,
+ "message": "Reduce the number of conditional operators (4) used in the expression",
+ "project": "myproject",
+ "projectKey": "foo",
+ "projectName": "Foo",
+ "rule": "javascript:S1067",
+ "ruleName": "foo",
+ "secondaryLocations": [],
+ "severity": "MAJOR",
+ "status": "OPEN",
+ "textRange": {
+ "endLine": 26,
+ "endOffset": 15,
+ "startLine": 25,
+ "startOffset": 0,
+ },
+ "transitions": [],
+ "type": "BUG",
+ }
+ }
/>
<div
className="issue-row-meta"
className="issue-row"
>
<IssueMessage
- engine="foo"
- message="Reduce the number of conditional operators (4) used in the expression"
- ruleKey="javascript:S1067"
+ issue={
+ {
+ "actions": [],
+ "component": "main.js",
+ "componentEnabled": true,
+ "componentLongName": "main.js",
+ "componentQualifier": "FIL",
+ "componentUuid": "foo1234",
+ "creationDate": "2017-03-01T09:36:01+0100",
+ "externalRuleEngine": "foo",
+ "flows": [],
+ "flowsWithType": [],
+ "key": "AVsae-CQS-9G3txfbFN2",
+ "line": 25,
+ "message": "Reduce the number of conditional operators (4) used in the expression",
+ "project": "myproject",
+ "projectKey": "foo",
+ "projectName": "Foo",
+ "rule": "javascript:S1067",
+ "ruleName": "foo",
+ "secondaryLocations": [],
+ "severity": "MAJOR",
+ "status": "OPEN",
+ "textRange": {
+ "endLine": 26,
+ "endOffset": 15,
+ "startLine": 25,
+ "startOffset": 0,
+ },
+ "transitions": [],
+ "type": "BUG",
+ }
+ }
/>
<div
className="issue-row-meta"
className="issue-row"
>
<IssueMessage
- message="Reduce the number of conditional operators (4) used in the expression"
- ruleKey="javascript:S1067"
+ issue={
+ {
+ "actions": [],
+ "component": "main.js",
+ "componentEnabled": true,
+ "componentLongName": "main.js",
+ "componentQualifier": "FIL",
+ "componentUuid": "foo1234",
+ "creationDate": "2017-03-01T09:36:01+0100",
+ "flows": [
+ [
+ {
+ "component": "main.js",
+ "textRange": {
+ "endLine": 2,
+ "endOffset": 2,
+ "startLine": 1,
+ "startOffset": 1,
+ },
+ },
+ {
+ "component": "main.js",
+ "textRange": {
+ "endLine": 2,
+ "endOffset": 2,
+ "startLine": 1,
+ "startOffset": 1,
+ },
+ },
+ {
+ "component": "main.js",
+ "textRange": {
+ "endLine": 2,
+ "endOffset": 2,
+ "startLine": 1,
+ "startOffset": 1,
+ },
+ },
+ ],
+ [
+ {
+ "component": "main.js",
+ "textRange": {
+ "endLine": 2,
+ "endOffset": 2,
+ "startLine": 1,
+ "startOffset": 1,
+ },
+ },
+ {
+ "component": "main.js",
+ "textRange": {
+ "endLine": 2,
+ "endOffset": 2,
+ "startLine": 1,
+ "startOffset": 1,
+ },
+ },
+ ],
+ ],
+ "flowsWithType": [],
+ "key": "AVsae-CQS-9G3txfbFN2",
+ "line": 25,
+ "message": "Reduce the number of conditional operators (4) used in the expression",
+ "project": "myproject",
+ "projectKey": "foo",
+ "projectName": "Foo",
+ "rule": "javascript:S1067",
+ "ruleName": "foo",
+ "secondaryLocations": [
+ {
+ "component": "main.js",
+ "textRange": {
+ "endLine": 2,
+ "endOffset": 2,
+ "startLine": 1,
+ "startOffset": 1,
+ },
+ },
+ {
+ "component": "main.js",
+ "textRange": {
+ "endLine": 2,
+ "endOffset": 2,
+ "startLine": 1,
+ "startOffset": 1,
+ },
+ },
+ ],
+ "severity": "MAJOR",
+ "status": "OPEN",
+ "textRange": {
+ "endLine": 26,
+ "endOffset": 15,
+ "startLine": 25,
+ "startOffset": 0,
+ },
+ "transitions": [],
+ "type": "BUG",
+ }
+ }
/>
<div
className="issue-row-meta"
className="issue-row"
>
<IssueMessage
- message="Reduce the number of conditional operators (4) used in the expression"
- ruleKey="javascript:S1067"
+ branchLike={
+ {
+ "analysisDate": "2018-01-01",
+ "excludedFromPurge": true,
+ "isMain": false,
+ "name": "branch-6.7",
+ }
+ }
+ issue={
+ {
+ "actions": [],
+ "component": "main.js",
+ "componentEnabled": true,
+ "componentLongName": "main.js",
+ "componentQualifier": "FIL",
+ "componentUuid": "foo1234",
+ "creationDate": "2017-03-01T09:36:01+0100",
+ "flows": [
+ [
+ {
+ "component": "main.js",
+ "textRange": {
+ "endLine": 2,
+ "endOffset": 2,
+ "startLine": 1,
+ "startOffset": 1,
+ },
+ },
+ {
+ "component": "main.js",
+ "textRange": {
+ "endLine": 2,
+ "endOffset": 2,
+ "startLine": 1,
+ "startOffset": 1,
+ },
+ },
+ {
+ "component": "main.js",
+ "textRange": {
+ "endLine": 2,
+ "endOffset": 2,
+ "startLine": 1,
+ "startOffset": 1,
+ },
+ },
+ ],
+ [
+ {
+ "component": "main.js",
+ "textRange": {
+ "endLine": 2,
+ "endOffset": 2,
+ "startLine": 1,
+ "startOffset": 1,
+ },
+ },
+ {
+ "component": "main.js",
+ "textRange": {
+ "endLine": 2,
+ "endOffset": 2,
+ "startLine": 1,
+ "startOffset": 1,
+ },
+ },
+ ],
+ ],
+ "flowsWithType": [],
+ "key": "AVsae-CQS-9G3txfbFN2",
+ "line": 25,
+ "message": "Reduce the number of conditional operators (4) used in the expression",
+ "project": "myproject",
+ "projectKey": "foo",
+ "projectName": "Foo",
+ "rule": "javascript:S1067",
+ "ruleName": "foo",
+ "secondaryLocations": [
+ {
+ "component": "main.js",
+ "textRange": {
+ "endLine": 2,
+ "endOffset": 2,
+ "startLine": 1,
+ "startOffset": 1,
+ },
+ },
+ {
+ "component": "main.js",
+ "textRange": {
+ "endLine": 2,
+ "endOffset": 2,
+ "startLine": 1,
+ "startOffset": 1,
+ },
+ },
+ ],
+ "severity": "MAJOR",
+ "status": "OPEN",
+ "textRange": {
+ "endLine": 26,
+ "endOffset": 15,
+ "startLine": 25,
+ "startOffset": 0,
+ },
+ "transitions": [],
+ "type": "BUG",
+ }
+ }
/>
<div
className="issue-row-meta"
import classNames from 'classnames';
import { cloneDeep, debounce, groupBy } from 'lodash';
import * as React from 'react';
+import { Location } from 'react-router-dom';
import { dismissNotice } from '../../api/users';
import { CurrentUserContextInterface } from '../../app/components/current-user/CurrentUserContext';
import withCurrentUserContext from '../../app/components/current-user/withCurrentUserContext';
import { NoticeType } from '../../types/users';
import ScreenPositionHelper from '../common/ScreenPositionHelper';
import BoxedTabs, { getTabId, getTabPanelId } from '../controls/BoxedTabs';
+import withLocation from '../hoc/withLocation';
import MoreInfoRuleDescription from './MoreInfoRuleDescription';
import RuleDescription from './RuleDescription';
import './style.css';
ruleDescriptionContextKey?: string;
codeTabContent?: React.ReactNode;
scrollInTab?: boolean;
+ location: Location;
}
interface State {
componentDidMount() {
this.setState((prevState) => this.computeState(prevState));
this.attachScrollEvent();
+
+ const tabs = this.computeTabs(Boolean(this.state.displayEducationalPrinciplesNotification));
+
+ const query = new URLSearchParams(this.props.location.search);
+ if (query.has('why')) {
+ this.setState({
+ selectedTab: tabs.find((tab) => tab.key === TabKeys.WhyIsThisAnIssue) || tabs[0],
+ });
+ }
}
componentDidUpdate(prevProps: RuleTabViewerProps, prevState: State) {
}
}
-export default withCurrentUserContext(RuleTabViewer);
+export default withCurrentUserContext(withLocation(RuleTabViewer));
import WorkspaceComponentViewer from './WorkspaceComponentViewer';
import WorkspaceNav from './WorkspaceNav';
import WorkspacePortal from './WorkspacePortal';
-import WorkspaceRuleViewer from './WorkspaceRuleViewer';
const WORKSPACE = 'sonarqube-workspace';
interface State {
externalRulesRepoNames: Dict<string>;
height: number;
maximized?: boolean;
- open: { component?: string; rule?: string };
+ open: { component?: string };
rules: RuleDescriptor[];
}
this.setState({ open: { component: componentKey } });
};
- handleOpenRule = (rule: RuleDescriptor) => {
- this.setState((state: State) => ({
- open: { rule: rule.key },
- rules: uniqBy([...state.rules, rule], (r) => r.key),
- }));
- };
-
- handleRuleReopen = (ruleKey: string) => {
- this.setState({ open: { rule: ruleKey } });
- };
-
handleComponentClose = (componentKey: string) => {
this.setState((state: State) => ({
components: state.components.filter((x) => x.key !== componentKey),
}));
};
- handleRuleClose = (ruleKey: string) => {
- this.setState((state: State) => ({
- rules: state.rules.filter((x) => x.key !== ruleKey),
- open: {
- ...state.open,
- rule: state.open.rule === ruleKey ? undefined : state.open.rule,
- },
- }));
- };
-
handleComponentLoad = (details: { key: string; name: string; qualifier: string }) => {
if (this.mounted) {
const { key, name, qualifier } = details;
}
};
- handleRuleLoad = (details: { key: string; name: string }) => {
- if (this.mounted) {
- const { key, name } = details;
- this.setState((state: State) => ({
- rules: state.rules.map((rule) => (rule.key === key ? { ...rule, name } : rule)),
- }));
- }
- };
-
handleCollapse = () => {
this.setState({ open: {} });
};
const { components, externalRulesRepoNames, height, maximized, open, rules } = this.state;
const openComponent = open.component && components.find((x) => x.key === open.component);
- const openRule = open.rule && rules.find((x) => x.key === open.rule);
const actualHeight = maximized ? window.innerHeight * MAX_HEIGHT : height;
value={{
externalRulesRepoNames,
openComponent: this.handleOpenComponent,
- openRule: this.handleOpenRule,
}}
>
{this.props.children}
components={components}
onComponentClose={this.handleComponentClose}
onComponentOpen={this.handleComponentReopen}
- onRuleClose={this.handleRuleClose}
- onRuleOpen={this.handleRuleReopen}
open={open}
- rules={rules}
/>
)}
{openComponent && (
onResize={this.handleResize}
/>
)}
- {openRule && (
- <WorkspaceRuleViewer
- height={actualHeight}
- maximized={maximized}
- onClose={this.handleRuleClose}
- onCollapse={this.handleCollapse}
- onLoad={this.handleRuleLoad}
- onMaximize={this.handleMaximize}
- onMinimize={this.handleMinimize}
- onResize={this.handleResize}
- rule={openRule}
- />
- )}
</WorkspacePortal>
</WorkspaceContext.Provider>
);
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import * as React from 'react';
-import { ComponentDescriptor, RuleDescriptor } from './context';
+import { ComponentDescriptor } from './context';
import WorkspaceNavComponent from './WorkspaceNavComponent';
-import WorkspaceNavRule from './WorkspaceNavRule';
export interface Props {
components: ComponentDescriptor[];
- rules: RuleDescriptor[];
onComponentClose: (componentKey: string) => void;
onComponentOpen: (componentKey: string) => void;
- onRuleClose: (ruleKey: string) => void;
- onRuleOpen: (ruleKey: string) => void;
open: { component?: string; rule?: string };
}
export default function WorkspaceNav(props: Props) {
// do not show a tab for the currently open component/rule
const components = props.components.filter((x) => x.key !== props.open.component);
- const rules = props.rules.filter((x) => x.key !== props.open.rule);
return (
<nav className="workspace-nav">
onOpen={props.onComponentOpen}
/>
))}
-
- {rules.map((rule) => (
- <WorkspaceNavRule
- key={`rule-${rule.key}`}
- onClose={props.onRuleClose}
- onOpen={props.onRuleOpen}
- rule={rule}
- />
- ))}
</ul>
</nav>
);
import { get, save } from '../../../helpers/storage';
import { waitAndUpdate } from '../../../helpers/testUtils';
import { ComponentQualifier } from '../../../types/component';
-import Workspace, {
- INITIAL_HEIGHT,
- MAX_HEIGHT,
- MIN_HEIGHT,
- TYPE_KEY,
- WorkspaceTypes,
-} from '../Workspace';
+import Workspace, { INITIAL_HEIGHT, MIN_HEIGHT, TYPE_KEY, WorkspaceTypes } from '../Workspace';
jest.mock('../../../helpers/storage', () => {
return {
open: { component: 'foo' },
})
).toMatchSnapshot('open component');
- expect(
- shallowRender({
- rules: [{ key: 'foo' }],
- open: { rule: 'foo' },
- })
- ).toMatchSnapshot('open rule');
});
it('should correctly load data from local storage', () => {
});
const instance = wrapper.instance();
- // Load an non-existent element won't do anything.
- instance.handleRuleLoad({ key: 'baz', name: 'Baz' });
- expect(wrapper.state().rules).toEqual([rule]);
-
instance.handleComponentLoad({ key: 'baz', name: 'Baz', qualifier: ComponentQualifier.TestFile });
expect(wrapper.state().components).toEqual([component]);
- // Load an existing element will update some of its properties.
- instance.handleRuleLoad({ key: 'bar', name: 'Bar' });
- expect(wrapper.state().rules).toEqual([{ ...rule, name: 'Bar' }]);
-
instance.handleComponentLoad({ key: 'foo', name: 'Foo', qualifier: ComponentQualifier.File });
expect(wrapper.state().components).toEqual([
{ ...component, name: 'Foo', qualifier: ComponentQualifier.File },
(get as jest.Mock).mockReturnValue(
JSON.stringify([{ [TYPE_KEY]: WorkspaceTypes.Rule, key: 'foo' }])
);
- const wrapper = shallowRender({ open: { rule: 'foo' } });
+ const wrapper = shallowRender();
const instance = wrapper.instance();
instance.handleMaximize();
expect(wrapper.state().maximized).toBe(true);
- // We cannot fetch by reference, as the viewer component is lazy loaded. Find
- // by string instead.
- expect(wrapper.find('WorkspaceRuleViewer').props().height).toBe(WINDOW_HEIGHT * MAX_HEIGHT);
instance.handleMinimize();
expect(wrapper.state().maximized).toBe(false);
- expect(wrapper.find('WorkspaceRuleViewer').props().height).toBe(INITIAL_HEIGHT);
instance.handleResize(-200);
expect(wrapper.state().height).toBe(INITIAL_HEIGHT + 200);
});
it('should be openable/collapsible', () => {
- const rule = {
- key: 'baz',
- name: 'Baz',
- };
const component = {
branchLike: mockBranch(),
key: 'foo',
instance.handleOpenComponent(component);
expect(wrapper.state().open).toEqual({ component: 'foo' });
- instance.handleOpenRule(rule);
- expect(wrapper.state().open).toEqual({ rule: 'baz' });
-
instance.handleCollapse();
expect(wrapper.state().open).toEqual({});
expect(wrapper.state().open).toEqual({ component: 'foo' });
instance.handleComponentClose('foo');
expect(wrapper.state().open).toEqual({});
-
- instance.handleRuleReopen(rule.key);
- expect(wrapper.state().open).toEqual({ rule: 'baz' });
-
- instance.handleRuleClose('bar');
- expect(wrapper.state().open).toEqual({ rule: 'baz' });
- instance.handleRuleClose('baz');
- expect(wrapper.state().open).toEqual({});
});
function shallowRender(state?: Partial<Workspace['state']>) {
expect(shallowRender({ open: { component: 'bar' } })).toMatchSnapshot();
});
-it('should not render open rule', () => {
- expect(shallowRender({ open: { rule: 'qux' } })).toMatchSnapshot();
-});
-
function shallowRender(props?: Partial<Props>) {
const components = [
{ branchLike: undefined, key: 'foo' },
{ branchLike: undefined, key: 'bar' },
];
- const rules = [{ key: 'qux' }];
return shallow(
<WorkspaceNav
components={components}
onComponentClose={jest.fn()}
onComponentOpen={jest.fn()}
- onRuleClose={jest.fn()}
- onRuleOpen={jest.fn()}
open={{}}
- rules={rules}
{...props}
/>
);
{
"externalRulesRepoNames": {},
"openComponent": [Function],
- "openRule": [Function],
}
}
>
{
"externalRulesRepoNames": {},
"openComponent": [Function],
- "openRule": [Function],
}
}
>
}
onComponentClose={[Function]}
onComponentOpen={[Function]}
- onRuleClose={[Function]}
- onRuleOpen={[Function]}
open={
{
"component": "foo",
}
}
- rules={[]}
/>
<withBranchStatusActions(WorkspaceComponentViewer)
component={
</WorkspacePortal>
</ContextProvider>
`;
-
-exports[`should render correctly: open rule 1`] = `
-<ContextProvider
- value={
- {
- "externalRulesRepoNames": {},
- "openComponent": [Function],
- "openRule": [Function],
- }
- }
->
- <div
- className="child"
- />
- <WorkspacePortal>
- <WorkspaceNav
- components={[]}
- onComponentClose={[Function]}
- onComponentOpen={[Function]}
- onRuleClose={[Function]}
- onRuleOpen={[Function]}
- open={
- {
- "rule": "foo",
- }
- }
- rules={
- [
- {
- "key": "foo",
- },
- ]
- }
- />
- <WorkspaceRuleViewer
- height={300}
- onClose={[Function]}
- onCollapse={[Function]}
- onLoad={[Function]}
- onMaximize={[Function]}
- onMinimize={[Function]}
- onResize={[Function]}
- rule={
- {
- "key": "foo",
- }
- }
- />
- </WorkspacePortal>
-</ContextProvider>
-`;
onClose={[MockFunction]}
onOpen={[MockFunction]}
/>
- <WorkspaceNavRule
- key="rule-qux"
- onClose={[MockFunction]}
- onOpen={[MockFunction]}
- rule={
- {
- "key": "qux",
- }
- }
- />
- </ul>
-</nav>
-`;
-
-exports[`should not render open rule 1`] = `
-<nav
- className="workspace-nav"
->
- <ul
- className="workspace-nav-list"
- >
- <WorkspaceNavComponent
- component={
- {
- "branchLike": undefined,
- "key": "foo",
- }
- }
- key="component-foo"
- onClose={[MockFunction]}
- onOpen={[MockFunction]}
- />
- <WorkspaceNavComponent
- component={
- {
- "branchLike": undefined,
- "key": "bar",
- }
- }
- key="component-bar"
- onClose={[MockFunction]}
- onOpen={[MockFunction]}
- />
</ul>
</nav>
`;
onClose={[MockFunction]}
onOpen={[MockFunction]}
/>
- <WorkspaceNavRule
- key="rule-qux"
- onClose={[MockFunction]}
- onOpen={[MockFunction]}
- rule={
- {
- "key": "qux",
- }
- }
- />
</ul>
</nav>
`;
export interface WorkspaceContextShape {
externalRulesRepoNames: Dict<string>;
openComponent: (component: ComponentDescriptor) => void;
- openRule: (rule: RuleDescriptor) => void;
}
export const WorkspaceContext = createContext<WorkspaceContextShape>({
externalRulesRepoNames: {},
openComponent: () => {},
- openRule: () => {},
});