Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feature(extension): Html Report Card Change, View menu and Filter addition #2024 #2027

Merged
merged 12 commits into from
Sep 12, 2024
3 changes: 3 additions & 0 deletions report-react/src/SavedReport.scss
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@
margin: 1rem 0rem 0rem -1rem;
}
}
.reportTreeGridEmptyText {
margin-top: 5rem;
}

.cds--modal-content {
padding: 1rem 1rem 0rem 1rem;
Expand Down
183 changes: 128 additions & 55 deletions report-react/src/SavedReport.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,63 +20,63 @@ import ScoreCard from './ScoreCard';
import SummScoreCard from './SummScoreCard';
import ReportChecklist from './report/ReportChecklist';
import ReportRules from './report/ReportRules';
import { ComposedModal, ModalHeader, ModalBody, Grid, Column, Theme, Tabs, TabList, TabPanel, Tab, TabPanels } from '@carbon/react';
import { ComposedModal, ModalHeader, ModalBody, Grid, Column, Theme,Dropdown,MultiSelect} from '@carbon/react';
import { UtilIssueReact } from "./util/UtilIssueReact";
import { Violation16,NeedsReview16,Recommendation16 } from "./util/UtilImages";
import ReportElements from "./report/ReportElements";

const Violation16 = <svg version="1.1" x="0px" y="0px" width="16px" height="16px" viewBox="0 0 16 16">
<rect style={{ fill: "none" }} width="16" height="16" />
<path style={{ fill: "#A2191F" }} d="M8,1C4.1,1,1,4.1,1,8s3.1,7,7,7s7-3.1,7-7S11.9,1,8,1z M10.7,11.5L4.5,5.3l0.8-0.8l6.2,6.2L10.7,11.5z" />
<path style={{ fill: "#FFFFFF", fillOpacity: 0 }} d="M10.7,11.5L4.5,5.3l0.8-0.8l6.2,6.2L10.7,11.5z" />
</svg>

const NeedsReview16 = <svg version="1.1" x="0px" y="0px"
width="16px" height="16px" viewBox="0 0 16 16">
<rect style={{ fill: "none" }} width="16" height="16" />
<path style={{ fill: "#F1C21B" }} d="M14.9,13.3l-6.5-12C8.3,1,8,0.9,7.8,1.1c-0.1,0-0.2,0.1-0.2,0.2l-6.5,12c-0.1,0.1-0.1,0.3,0,0.5
C1.2,13.9,1.3,14,1.5,14h13c0.2,0,0.3-0.1,0.4-0.2C15,13.6,15,13.4,14.9,13.3z M7.4,4h1.1v5H7.4V4z M8,11.8c-0.4,0-0.8-0.4-0.8-0.8
s0.4-0.8,0.8-0.8c0.4,0,0.8,0.4,0.8,0.8S8.4,11.8,8,11.8z"/>
<g>
<g>
<g>
<rect x="7.45" y="4" width="1.1" height="5" />
</g>
</g>
<g>
<g>
<circle cx="8" cy="11" r="0.8" />
</g>
</g>
</g>
</svg>

const Recommendation16 = <svg version="1.1" x="0px" y="0px"
width="16px" height="16px" viewBox="0 0 16 16">
<rect style={{ fill: "none" }} width="16" height="16" />
<path style={{ fill: "#0043CE" }} d="M14,15H2c-0.6,0-1-0.4-1-1V2c0-0.6,0.4-1,1-1h12c0.6,0,1,0.4,1,1v12C15,14.6,14.6,15,14,15z" />
<text transform="matrix(1 0 0 1 5.9528 12.5044)" style={{ fill: "#FFFFFF", fontFamily: "IBMPlexSerif", fontSize: "12.9996px" }}>i</text>
</svg>

interface SavedReportProps {
reportData: ISavedReportData | null
}

interface SavedReportState {
selectedItem: IReportItem | null
selectedItem: IReportItem | null;
reportViewState:'Requirements'|'Rules'|'Element roles';
selectedItems: Array<{ id: string; text: string }>;

}
const filterItems = [
{ id: '0', text: 'Violations' },
{ id: '1', text: 'Needs review' },
{ id: '2', text: 'Recommendations' },
// { id: '3', text: 'Hidden' },
]
const viewItems = ["Element roles", "Requirements","Rules"];


export class SavedReport extends React.Component<SavedReportProps, SavedReportState> {
state: SavedReportState = {
selectedItem: null
selectedItem: null,
reportViewState: "Element roles",
selectedItems: filterItems.filter(item => item.id!=='3'),

}


selectItem(item: IReportItem) {
this.setState({ selectedItem: item });
}

clearItem() {
this.setState({ selectedItem: null });
}


handleFilterChange = (selectedItems: Array<{ id: string; text: string }>) => {
this.setState({selectedItems})

};
handleCardClick=(filterItem:string)=>{
if(this.state.selectedItems.some((item)=>item.text===filterItem)){
let updatedFilter=this.state.selectedItems.filter((item)=>item.text!==filterItem);
this.setState({selectedItems:updatedFilter});
}else{
let updatedFilter=filterItems.filter((item)=>item.text===filterItem)[0]
this.setState({selectedItems:[...this.state.selectedItems,updatedFilter]})
}
}
render() {

if (!this.props.reportData) {
return <React.Fragment>Report Error</React.Fragment>
}
Expand All @@ -98,6 +98,24 @@ export class SavedReport extends React.Component<SavedReportProps, SavedReportSt
++recommendation;
}
}
const selectedFilters = this.state.selectedItems.map(item => item.text);

const filteredReport = {
...this.props.reportData.report,
results: this.props.reportData.report.results.filter(issue => {
if (selectedFilters.includes("Violations") && issue.value[0] === "VIOLATION" && issue.value[1] === "FAIL") {
return true;
}
if (selectedFilters.includes("Needs review") && issue.value[0] === "VIOLATION" && (issue.value[1] === "POTENTIAL" || issue.value[1] === "MANUAL")) {
return true;
}
if (selectedFilters.includes("Recommendations") && issue.value[0] === "RECOMMENDATION") {
return true;
}
return false;
})
};

return <div
role="main"
id="main-content"
Expand Down Expand Up @@ -126,17 +144,18 @@ export class SavedReport extends React.Component<SavedReportProps, SavedReportSt
<div className="url"><strong>Scanned page:</strong> {this.props.reportData.tabURL}</div>
</Column>
<Column sm={2} md={4} lg={4}>
<ScoreCard count={violations} title="Violations" icon={Violation16}>
<ScoreCard count={violations} title="Violations" icon={Violation16} checked={this.state.selectedItems.some((item)=>item.text==="Violations")}
handleCardClick={this.handleCardClick}>
Accessibility failures that need to be corrected
</ScoreCard>
</Column>
<Column sm={2} md={4} lg={4}>
<ScoreCard count={needReview} title="Needs review" icon={NeedsReview16}>
<ScoreCard count={needReview} title="Needs review" icon={NeedsReview16} checked={this.state.selectedItems.some((item)=>item.text==="Needs review")} handleCardClick={this.handleCardClick}>
Issues that may not be a violation; manual review is needed
</ScoreCard>
</Column>
<Column sm={2} md={4} lg={4}>
<ScoreCard count={recommendation} title="Recommendations" icon={Recommendation16}>
<ScoreCard count={recommendation} title="Recommendations" icon={Recommendation16} checked={this.state.selectedItems.some((item)=>item.text==="Recommendations")} handleCardClick={this.handleCardClick}>
Opportunities to apply best practices to further improve accessibility
</ScoreCard>
</Column>
Expand All @@ -146,24 +165,78 @@ export class SavedReport extends React.Component<SavedReportProps, SavedReportSt
<Grid>
<Column sm={4} md={8} lg={{offset: 4, span: 12}}>
<div className="summReport">
<Tabs>
<TabList aria-label="Report details">
<Tab>Requirements</Tab>
<Tab>Rules</Tab>
</TabList>
<TabPanels>
<TabPanel>
<div style={{margin: "0rem -1rem"}} role="table" aria-label="Issues grouped by checkpoint">
<ReportChecklist selectItem={this.selectItem.bind(this)} report={this.props.reportData.report} ruleset={rs} />
<div style={{display:"flex",float:"right",gap:"1rem"}}>
<div style={{flex: "1 1 0", marginRight: "0px", margin: "-1px 0px" }}>
<Dropdown
className="viewMulti"
ariaLabel="Select report view"
id="reportView"
size="sm"
items={viewItems}
light={false}
type="default"
style={{width:"160px", float: "right"}}
selectedItem={this.state.reportViewState}
onChange={async (evt: any) => {
// set state
this.setState({ reportViewState: evt.selectedItem });
}}
/>
</div>
<div style={{flex: "1 1 0", margin: "-1px 0px",minWidth:"230px"}}>
<MultiSelect
className="viewMulti"
ariaLabel="Issue type filter"
label="Filter"
size="sm"
hideLabel={true}
id="filterSelection"
items={filterItems}
itemToString={(item:any) => (item ? item.text : '')}
itemToElement={(item:any) => {
if (item && item.id === "0") {
return <span>{UtilIssueReact.valueSingToIcon("Violation", "reportSecIcon")} {item.text}</span>
} else if (item && item.id === "1") {
return <span>{UtilIssueReact.valueSingToIcon("Needs review", "reportSecIcon")} {item.text}</span>
} else if (item && item.id === "2") {
return <span>{UtilIssueReact.valueSingToIcon("Recommendation", "reportSecIcon")} {item.text}</span>
} else if (item && item.id === "3") {
return <span>{UtilIssueReact.valueSingToIcon("ViewOff", "reportSecIcon")} {item.text}</span>
}
return <></>
}
}
light={false}
type="default"
selectedItems={this.state.selectedItems}
initialSelectedItems={this.state.selectedItems}
onChange={(event: { selectedItems: Array<{ id: string; text: string }> }) => this.handleFilterChange(event.selectedItems)}

/>
</div>

</div>
{filteredReport.results.length>0 && <>
{this.state.reportViewState === "Element roles" && <>
<div style={{marginTop:"4rem"}} role="table" aria-label="Issues grouped by Element roles">
<ReportElements selectItem={this.selectItem.bind(this)} report={filteredReport} />
</div>
</>}

{this.state.reportViewState === "Requirements" && <>
<div style={{marginTop:"4rem"}} role="table" aria-label="Issues grouped by Requirements">
<ReportChecklist selectItem={this.selectItem.bind(this)} report={filteredReport} ruleset={rs} />
</div>
</TabPanel>
<TabPanel>
<div style={{margin: "0rem -1rem"}} role="table" aria-label="Issues grouped by checkpoint">
<ReportRules selectItem={this.selectItem.bind(this)} report={this.props.reportData.report} />
</>}
{this.state.reportViewState === "Rules" && <>
<div style={{marginTop:"4rem"}} role="table" aria-label="Issues grouped by Rules">
<ReportRules selectItem={this.selectItem.bind(this)} report={filteredReport} />
</div>
</TabPanel>
</TabPanels>
</Tabs>
</>}
</>}
{filteredReport.results.length===0 &&
<div className="reportTreeGridEmptyText">No issues detected for the chosen filter criteria. To see all issues, select all issue types, and do not filter hidden issues.</div>}

</div>
</Column>
</Grid>
Expand Down
14 changes: 11 additions & 3 deletions report-react/src/ScoreCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,22 +14,30 @@
limitations under the License.
*****************************************************************************/
import React, { ReactNode } from "react";
import { SelectableTile } from '@carbon/react';
import "./ScoreCard.scss";

interface ScoreCardProps {
title: string
icon?: ReactNode
count?: number
children?: any
checked?:Boolean
handleCardClick?:(item:string)=>void
}

export default class ScoreCard extends React.Component<ScoreCardProps, {}> {

render() {
return <div className="scoreCard">
<div><span className="title">{this.props.title}</span><span style={{verticalAlign:"top",float:"right"}}>{this.props.icon}</span></div>
return <div >
<SelectableTile className="scoreCard"
selected={this.props.checked} onClick={() => this.props.handleCardClick && this.props.handleCardClick(this.props.title)}

>
<div><span className="title">{this.props.title}</span><span style={{verticalAlign:"middle"}}>&nbsp;{this.props.icon}</span></div>
<div className="score">{this.props.count}</div>
<div className="description">{this.props.children}</div>
</SelectableTile>
</div>
}
}
2 changes: 1 addition & 1 deletion report-react/src/SummScoreCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ export default class SummScoreCard extends React.Component<SummScoreCardProps, {
let summaryNumbers = this.calcSummary(this.props.report);
let currentStatus = summaryNumbers[3].toFixed(0);

return <div className="scoreCard" style={{border: "1px solid #9E63FB", backgroundColor:'#E8DAFF'}}>
return <div className="scoreCard" style={{border: "1px solid #9E63FB", backgroundColor:'#be95ff'}}>

<Grid>
<Column sm={2} md={4} lg={4} className="scLeft">
Expand Down
10 changes: 9 additions & 1 deletion report-react/src/report/ReportElements.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ import "./report.scss";
import { IReport, IReportItem, valueMap } from "../IReport";
import ReportRow from "./ReportRow";
import { Grid, Column } from "@carbon/react";
import { UtilIssue } from "../util/UtilIssue";
import { IssueValue } from "../util/UtilIssueReact";

interface IReportElementsState {
}
Expand Down Expand Up @@ -49,7 +51,7 @@ export default class ReportElements extends React.Component<IReportElementsProps
let thisGroup = groupMap[item.path.aria];
if (!thisGroup) {
thisGroup = {
title: item.path.aria,
title: (item.path.aria),
counts: {},
items: []
}
Expand All @@ -59,7 +61,13 @@ export default class ReportElements extends React.Component<IReportElementsProps
thisGroup.items.push(item);
let val = valueMap[item.value[0]][item.value[1]] || item.value[0] + "_" + item.value[1];
thisGroup.counts[val] = (thisGroup.counts[val] || 0) + 1;

}
groups.sort((groupA, groupB) => groupA.title.localeCompare(groupB.title));
for (const group of groups) {
group.items.sort((a, b) => UtilIssue.valueToOrder(a.value as IssueValue)-UtilIssue.valueToOrder(b.value as IssueValue));
}


return <div className="report" role="rowgroup">
<Grid className="reportHeader">
Expand Down
35 changes: 2 additions & 33 deletions report-react/src/report/ReportRow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,44 +17,13 @@
import React, { RefObject } from "react";

import { IReportItem, valueMap, ICheckpoint, IReport } from "../IReport";

import Popup from "@carbon/icons-react/lib/Popup";
import ChevronUp from "@carbon/icons-react/lib/ChevronUp";
import ChevronDown from "@carbon/icons-react/lib/ChevronDown";
import { Grid, Column } from "@carbon/react";
import { Violation16,NeedsReview16,Recommendation16 } from "../util/UtilImages";


const Violation16 = <svg version="1.1" x="0px" y="0px" width="16px" height="16px" viewBox="0 0 16 16">
<rect style={{ fill: "none" }} width="16" height="16" />
<path style={{ fill: "#A2191F" }} d="M8,1C4.1,1,1,4.1,1,8s3.1,7,7,7s7-3.1,7-7S11.9,1,8,1z M10.7,11.5L4.5,5.3l0.8-0.8l6.2,6.2L10.7,11.5z" />
<path style={{ fill: "#FFFFFF", fillOpacity: 0 }} d="M10.7,11.5L4.5,5.3l0.8-0.8l6.2,6.2L10.7,11.5z" />
</svg>

const NeedsReview16 = <svg version="1.1" x="0px" y="0px"
width="16px" height="16px" viewBox="0 0 16 16">
<rect style={{ fill: "none" }} width="16" height="16" />
<path style={{ fill: "#F1C21B" }} d="M14.9,13.3l-6.5-12C8.3,1,8,0.9,7.8,1.1c-0.1,0-0.2,0.1-0.2,0.2l-6.5,12c-0.1,0.1-0.1,0.3,0,0.5
C1.2,13.9,1.3,14,1.5,14h13c0.2,0,0.3-0.1,0.4-0.2C15,13.6,15,13.4,14.9,13.3z M7.4,4h1.1v5H7.4V4z M8,11.8c-0.4,0-0.8-0.4-0.8-0.8
s0.4-0.8,0.8-0.8c0.4,0,0.8,0.4,0.8,0.8S8.4,11.8,8,11.8z"/>
<g>
<g>
<g>
<rect x="7.45" y="4" width="1.1" height="5" />
</g>
</g>
<g>
<g>
<circle cx="8" cy="11" r="0.8" />
</g>
</g>
</g>
</svg>

const Recommendation16 = <svg version="1.1" x="0px" y="0px"
width="16px" height="16px" viewBox="0 0 16 16">
<rect style={{ fill: "none" }} width="16" height="16" />
<path style={{ fill: "#0043CE" }} d="M14,15H2c-0.6,0-1-0.4-1-1V2c0-0.6,0.4-1,1-1h12c0.6,0,1,0.4,1,1v12C15,14.6,14.6,15,14,15z" />
<text transform="matrix(1 0 0 1 5.9528 12.5044)" style={{ fill: "#FFFFFF", fontFamily: "IBMPlexSerif", fontSize: "12.9996px" }}>i</text>
</svg>

export interface IReportRowGroup {
checkpoint?: ICheckpoint,
Expand Down
Loading
Loading