Skip to content

Commit

Permalink
New: Update browser extension to support new severity values
Browse files Browse the repository at this point in the history
Fix #3181
Fix #3069
Close #3340
  • Loading branch information
sarvaje authored and molant committed Nov 18, 2019
1 parent 9806266 commit fb663cc
Show file tree
Hide file tree
Showing 15 changed files with 282 additions and 43 deletions.
34 changes: 33 additions & 1 deletion packages/extension-browser/src/_locales/en/messages.json
Expand Up @@ -67,6 +67,14 @@
"message": "Error details",
"description": "Text displayed for option to expand and show error details."
},
"errorIssuesLabel": {
"message": "Errors: $1",
"description": "Label for count of hints with severity error, e.g. 'Errors: 2'."
},
"errorLabel": {
"message": "Error",
"description": "Label for option to choose error as minimum hint severity."
},
"errorMessage": {
"message": "There was an error running webhint. Please try again. If the error still occurs: ",
"description": "Message informing users what to do when an error occurs."
Expand All @@ -93,7 +101,11 @@
},
"hintIssuesLabel": {
"message": "Hints: $1",
"description": "Label for count of hints with issues, e.g. 'Hints: 2'."
"description": "Label for count of hints with severity hint, e.g. 'Hints: 2'."
},
"hintLabel": {
"message": "Hint",
"description": "Label for option to choose hint as minimum hint severity."
},
"hintsTitle": {
"message": "Hints",
Expand All @@ -103,6 +115,14 @@
"message": "Ignored resources",
"description": "Title of the ignored resouces sub-section on the configuration page."
},
"informationIssuesLabel": {
"message": "Informations: $1",
"description": "Label for count of hints with severity information, e.g. 'Informations: 2'."
},
"informationLabel": {
"message": "Information",
"description": "Label for option to choose information as minimum hint severity."
},
"learnWhyLabel": {
"message": "Learn why this is important",
"description": "Label for link to additional documentation for a passed test."
Expand Down Expand Up @@ -163,6 +183,10 @@
"message": "Settings",
"description": "Label settings details/summary."
},
"severityTitle": {
"message": "Minimum hint severity",
"description": "Title of the minimum hint severity sub-section on the configuration page."
},
"showPassedHintsLabel": {
"message": "Show passed hints",
"description": "Label for toggle to hide/show groups of hints which passed."
Expand Down Expand Up @@ -214,5 +238,13 @@
"telemetry": {
"message": "Enable telemetry",
"description": "Label for the setting to enable/disable telemetry."
},
"warningIssuesLabel": {
"message": "Warnings: $1",
"description": "Label for count of hints with severity warning, e.g. 'Warnings: 2'."
},
"warningLabel": {
"message": "Warning",
"description": "Label for option to choose warning as minimum hint severity."
}
}
9 changes: 7 additions & 2 deletions packages/extension-browser/src/content-script/webhint.ts
Expand Up @@ -19,6 +19,7 @@ import WebExtensionConnector from './connector';
import WebExtensionFormatter from './formatter';

import hints from '../shared/hints.import';
import { Severity } from '@hint/utils-types';

const reportError = (message: string, stack: string) => {
browser.runtime.sendMessage({
Expand Down Expand Up @@ -64,7 +65,7 @@ const main = async (userConfig: Config) => {
const category = hint.meta.docs && hint.meta.docs.category || 'other';
const enabled = !(userConfig.disabledCategories && userConfig.disabledCategories.includes(category));

o[hint.meta.id] = enabled ? 'warning' : 'off';
o[hint.meta.id] = enabled ? 'default' : 'off';

if (enabled) {
enabledHints.push(hint);
Expand Down Expand Up @@ -102,8 +103,12 @@ const main = async (userConfig: Config) => {
const engine = new Engine(config, resources);
const problems = await engine.executeOn(new URL(location.href));

const filteredProblems = problems.filter((problem) => {
return problem.severity >= (userConfig.severityThreshold || Severity.warning);
});

engine.formatters.forEach((formatter) => {
formatter.format(problems, {
formatter.format(filteredProblems, {
resources,
target: location.href
});
Expand Down
8 changes: 8 additions & 0 deletions packages/extension-browser/src/devtools/views/app.fluent.css
Expand Up @@ -23,6 +23,8 @@
--status-bg-error: #d83b01;
--status-bg-pass: #00864b;
--status-bg-warn: #f2c811;
--status-bg-info: #cecece;
--status-bg-hint: #9451A0;

--shadow-color: #00000080;
--scrollbar-shadow: inset 0 0 1px var(--shadow-color);
Expand All @@ -39,6 +41,12 @@
--icon-pwa-url: url(../../images/icon-pwa.svg);
--icon-security-url: url(../../images/icon-security.svg);

/* Severity icons */
--icon-severity-error-url: url(../../images/icon-severity-error.svg);
--icon-severity-hint-url: url(../../images/icon-severity-hint.svg);
--icon-severity-info-url: url(../../images/icon-severity-info.svg);
--icon-severity-warning-url: url(../../images/icon-severity-warning.svg);

/* Syntax highlighting colors */
/* JS */
--code-boolean-color: #0000ff;
Expand Down
8 changes: 8 additions & 0 deletions packages/extension-browser/src/devtools/views/app.photon.css
Expand Up @@ -23,6 +23,8 @@
--status-bg-error: #d83b01;
--status-bg-pass: #00864b;
--status-bg-warn: #f2c811;
--status-bg-info: #cecece;
--status-bg-hint: #9451A0;

--shadow-color: #00000080;
--scrollbar-shadow: inset 0 0 1px var(--shadow-color);
Expand All @@ -37,6 +39,12 @@
--icon-pwa-url: url(../../images/icon-pwa.svg);
--icon-security-url: url(../../images/icon-security.svg);

/* Severity icons */
--icon-severity-error-url: url(../../images/icon-severity-error.svg);
--icon-severity-hint-url: url(../../images/icon-severity-hint.svg);
--icon-severity-info-url: url(../../images/icon-severity-info.svg);
--icon-severity-warning-url: url(../../images/icon-severity-warning.svg);

/* Syntax highlighting colors */
/* JS */
--code-boolean-color: #0000ff;
Expand Down
Expand Up @@ -16,6 +16,7 @@ import CategoriesConfig from './config/sections/categories';
import ResourcesConfig from './config/sections/resources';
import ConfigHeader from './config/header';
import Settings from '../controls/settings';
import SeveritiesConfig from './config/sections/severities';

import { resolveIgnoreQuery } from './config/sections/resources';

Expand Down Expand Up @@ -70,6 +71,10 @@ const ConfigPage = ({ disabled, onStart, onTelemetryChange, isTelemetryEnabled }
setConfig({ ...config, ignoredUrls });
}, [config]);

const onSeverityChange = useCallback((severityThreshold?: string) => {
setConfig({ ...config, severityThreshold });
}, [config]);

const onRestoreClick = useCallback(() => {
setConfig({});
}, []);
Expand All @@ -82,6 +87,7 @@ const ConfigPage = ({ disabled, onStart, onTelemetryChange, isTelemetryEnabled }
<CategoriesConfig disabled={config.disabledCategories} onChange={onCategoriesChange} />
<BrowsersConfig query={config.browserslist} onChange={onBrowsersChange} />
<ResourcesConfig query={config.ignoredUrls} onChange={onResourcesChange} />
<SeveritiesConfig query={config.severityThreshold} onChange={onSeverityChange} />
</div>
<Button className={styles.button} onClick={onRestoreClick}>
{getMessage('restoreDefaultsLabel')}
Expand Down
@@ -0,0 +1,96 @@
import * as React from 'react';
import { useCallback } from 'react';

import { getMessage } from '../../../../utils/i18n';
import { useUniqueId } from '../../../../utils/ids';

import LabelText from '../../../controls/label-text';
import Radio from '../../../controls/radio';

import ConfigLabel from '../label';
import ConfigSection from '../section';
import { Severity } from '@hint/utils-types';

type Props = {
className?: string;
query?: string;
onChange: (query?: string) => void;
};

/**
* Display options to exclude resources matching a given query from a scan.
*/
const SeveritySection = ({ className, query, onChange }: Props) => {
const onErrorSelected = useCallback(() => {
onChange(Severity.error.toString());
}, [onChange]);

const onWarningSelected = useCallback(() => {
onChange(Severity.warning.toString());
}, [onChange]);

const onInformationSelected = useCallback(() => {
onChange(Severity.information.toString());
}, [onChange]);

const onHintSelected = useCallback(() => {
onChange(Severity.hint.toString());
}, [onChange]);

const groupLabelId = useUniqueId();
const errorLabelId = useUniqueId();
const warningLabelId = useUniqueId();
const informationLabelId = useUniqueId();
const hintLabelId = useUniqueId();

return (
<ConfigSection className={className} title={getMessage('severityTitle')} titleId={groupLabelId}>
<ConfigLabel>
<Radio
aria-labelledby={`${groupLabelId} ${errorLabelId}`}
name="severity"
checked={query === Severity.error.toString()}
onChange={onErrorSelected}
/>
<LabelText id={errorLabelId}>
{getMessage('errorLabel')}
</LabelText>
</ConfigLabel>
<ConfigLabel>
<Radio
aria-labelledby={`${groupLabelId} ${warningLabelId}`}
checked={!query || query === Severity.warning.toString()}
name="severity"
onChange={onWarningSelected}
/>
<LabelText id={warningLabelId}>
{getMessage('warningLabel')}
</LabelText>
</ConfigLabel>
<ConfigLabel>
<Radio
aria-labelledby={`${groupLabelId} ${informationLabelId}`}
checked={query === Severity.information.toString()}
name="severity"
onChange={onInformationSelected}
/>
<LabelText id={informationLabelId}>
{getMessage('informationLabel')}
</LabelText>
</ConfigLabel>
<ConfigLabel>
<Radio
aria-labelledby={`${groupLabelId} ${hintLabelId}`}
checked={query === Severity.hint.toString()}
name="severity"
onChange={onHintSelected}
/>
<LabelText id={hintLabelId}>
{getMessage('hintLabel')}
</LabelText>
</ConfigLabel>
</ConfigSection>
);
};

export default SeveritySection;
Expand Up @@ -6,30 +6,14 @@
padding-left: 1rem; /* 16px */
}

.status {
border-radius: 1.25rem; /* 20px */
.summary {
box-sizing: border-box;
color: var(--base-color);
display: inline-block;
font-size: var(--font-size-sm);
font-weight: bold;
line-height: 1.25rem; /* 20px */
min-width: 4rem; /* 64px */
padding: 0 0.5rem; /* 0 8px */
text-align: center;
white-space: nowrap;
}

.status.pass {
background-color: var(--status-bg-pass);
color: var(--light-color);
}

.status.warn {
background-color: var(--status-bg-warn);
color: var(--dark-color);
}

.status.error {
background-color: var(--status-bg-error);
color: var(--light-color);
}
Expand Up @@ -14,23 +14,18 @@ import MessageGroup from './message-group';

import * as styles from './hint.css';

const hasError = (problems: ProblemData[]) => {
return problems.some((problem) => {
return problem.severity === Severity.error;
});
};

/**
* Group problems which have the same message string.
*/
const groupProblems = (problems: ProblemData[]) => {
const groupProblems = (problems: ProblemData[], field: 'message' | 'severity') => {
const groups = new Map<string, ProblemData[]>();

for (const problem of problems) {
const group = groups.get(problem.message) || [];
const problemFieldValue = problem[field].toString();
const group = groups.get(problemFieldValue) || [];

group.push(problem);
groups.set(problem.message, group);
groups.set(problemFieldValue, group);
}

return groups;
Expand All @@ -57,23 +52,47 @@ const getSortedGroupKeys = (groups: Map<string, ProblemData[]>) => {
});
};

const Hint = ({ name, problems, helpURL }: HintResults) => {
const groups = groupProblems(problems);
let statusStyle = styles.pass;
const getSummaryMessage = (problems: ProblemData[]): string => {
if (!problems.length) {
return getMessage('noIssuesLabel');
}

const messages = [];
const groups = groupProblems(problems, 'severity');
const errorGroup = groups.get(Severity.error.toString());
const warningGroup = groups.get(Severity.warning.toString());
const informationGroup = groups.get(Severity.information.toString());
const hintGroup = groups.get(Severity.hint.toString());

if (problems.length) {
statusStyle = hasError(problems) ? styles.error : styles.warn;
if (errorGroup) {
messages.push(getMessage('errorIssuesLabel', errorGroup.length.toString()));
}
if (warningGroup) {
messages.push(getMessage('warningIssuesLabel', warningGroup.length.toString()));
}
if (hintGroup) {
messages.push(getMessage('hintIssuesLabel', hintGroup.length.toString()));
}
if (informationGroup) {
messages.push(getMessage('informationIssuesLabel', informationGroup.length.toString()));
}

return messages.join(', ');
};

const Hint = ({ name, problems, helpURL }: HintResults) => {
const groups = groupProblems(problems, 'message');
const summary = getSummaryMessage(problems);

return (
<details>
<Summary>
<span>
{name}
{name}:
</span>
{' '}
<span className={`${styles.status} ${statusStyle}`}>
{!problems.length ? getMessage('noIssuesLabel') : getMessage('hintIssuesLabel', groups.size.toString())}
<span className={`${styles.summary}`}>
{summary}
</span>
</Summary>
<div className={styles.results}>
Expand All @@ -82,7 +101,7 @@ const Hint = ({ name, problems, helpURL }: HintResults) => {
</ExternalLink>
{getSortedGroupKeys(groups).map(
(message) => {
return <MessageGroup key={message} message={message} problems={groups.get(message)!} />;
return <MessageGroup key={message} message={message} severity={groups.get(message)![0].severity} problems={groups.get(message)!} />;
}
)}
</div>
Expand Down

0 comments on commit fb663cc

Please sign in to comment.