Skip to content

Commit

Permalink
feat: use React.Profiler to render story
Browse files Browse the repository at this point in the history
  • Loading branch information
atanasster committed May 17, 2020
1 parent ad1f8c9 commit 656ea5b
Show file tree
Hide file tree
Showing 6 changed files with 139 additions and 107 deletions.
12 changes: 9 additions & 3 deletions plugins/axe-plugin/src/components/AllyDashboard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ import { AxeResults } from 'axe-core';
import { Chart } from 'react-google-charts';
import { Flex, Heading, Grid, Label, Checkbox } from 'theme-ui';
import {
axeResults,
axePasses,
axeViolations,
isTagSelected,
selectionList,
taggedList,
Expand All @@ -13,7 +14,7 @@ import {
type StatsStatus = 'passes' | 'violations';

const TagSelectionCheckbox: FC<{ tag: string }> = ({ tag }) => {
const isSelected = tag ? useRecoilValue(isTagSelected(tag)) : false;
const isSelected = useRecoilValue(isTagSelected(tag));
const tagged = useRecoilValue(taggedList);
const [selection, setSelection] = useRecoilState(selectionList);
const toggleTagSelected = (tag: string) => {
Expand Down Expand Up @@ -72,7 +73,12 @@ const collectStats = (
return success;
};
export const AllyDashboard: FC = () => {
const results = useRecoilValue(axeResults);
const passes = useRecoilValue(axePasses);
const violations = useRecoilValue(axeViolations);
const results = {
violations,
passes,
};
const stats = collectStats(
results,
'violations',
Expand Down
119 changes: 39 additions & 80 deletions plugins/axe-plugin/src/components/BaseAllyBlock.tsx
Original file line number Diff line number Diff line change
@@ -1,21 +1,9 @@
/* eslint-disable react/display-name */
import React, { FC, useRef, useEffect, useMemo } from 'react';
import React, { FC, useEffect } from 'react';
import { useRecoilState } from 'recoil';
import {
run as runAxe,
cleanup as cleanupAxe,
configure as configureAxe,
reset,
Spec,
} from 'axe-core';
import { Spec, cleanup } from 'axe-core';

import { Story } from '@component-controls/blocks';

import {
PanelContainer,
ActionItems,
resetTabCounter,
} from '@component-controls/components';
import { PanelContainer, ActionItems } from '@component-controls/components';
import { ViolationsTable, PassesTable, IncompleteTable } from './ResultsTable';
import { HighlightSelector } from './HighlightSelector';
import { axeResults } from './SelectionContext';
Expand All @@ -25,91 +13,62 @@ import { AllyDashboard } from './AllyDashboard';
* options
*/
export interface BaseAllyBlockProps {
storyId?: string;
options?: Spec;
}

/**
* Displays the [axe](https://github.com/dequelabs/axe-core) ally test results
*/
export const BaseAllyBlock: FC<BaseAllyBlockProps> = ({
storyId,
options = {},
}) => {
const storyRef = useRef<HTMLDivElement>(null);
const isMounted = useRef(false);
const isRunning = useRef(false);
const [results, setResults] = useRecoilState(axeResults);
export const BaseAllyBlock: FC<BaseAllyBlockProps> = ({ children }) => {
const [{ violations, passes, incomplete }] = useRecoilState(axeResults);

useEffect(() => {
resetTabCounter();
reset();
configureAxe(options);
isMounted.current = true;
if (isRunning.current === false) {
isRunning.current = true;
const el = storyRef.current?.firstChild;
runAxe(el ?? undefined)
.then(r => {
isRunning.current = false;
if (isMounted.current) {
setResults(r);
}
})
.catch(e => {
isRunning.current = false;
console.error(e);
});
}
return () => {
isMounted.current = false;
cleanupAxe();
cleanup();
};
}, [storyId, storyRef.current]);
const actions: ActionItems = useMemo(() => {
const actions: ActionItems = [
{
title: `dashboard`,
id: 'dashboard',
'aria-label': 'display the accessibility overview dashboard',
panel: <AllyDashboard />,
},
];
if (results?.violations?.length) {
actions.push({
title: `errors (${results.violations.length})`,
id: 'errors',
group: 'results',
'aria-label': 'display the accessibility violations',
panel: <ViolationsTable />,
});
}
if (results?.passes?.length) {
actions.push({
title: `passed (${results?.passes?.length})`,
id: 'passed',
group: 'results',
'aria-label': 'display the accessibility successfully passed tests',
panel: <PassesTable />,
});
}
return actions;
}, [results]);
if (results?.incomplete?.length) {
}, []);
const actions: ActionItems = [
{
title: `dashboard`,
id: 'dashboard',
'aria-label': 'display the accessibility overview dashboard',
panel: <AllyDashboard />,
},
];
if (violations?.length) {
actions.push({
title: `errors (${violations.length})`,
id: 'errors',
group: 'results',
'aria-label': 'display the accessibility violations',
panel: <ViolationsTable />,
});
}
if (passes?.length) {
actions.push({
title: `incomplete (${results.incomplete.length})`,
title: `passed (${passes?.length})`,
id: 'passed',
group: 'results',
'aria-label': 'display the accessibility successfully passed tests',
panel: <PassesTable />,
});
}

if (incomplete?.length) {
actions.push({
title: `incomplete (${incomplete.length})`,
id: 'incomplete',
group: 'results',
'aria-label': 'display the incomplete accessibility tests',
panel: <IncompleteTable />,
});
}

// console.log(results);

return (
<PanelContainer actions={actions} openTab="dashboard" visibleTabs={true}>
<HighlightSelector>
<Story key={storyId} id={storyId} ref={storyRef} />
</HighlightSelector>
<HighlightSelector>{children}</HighlightSelector>
</PanelContainer>
);
};
15 changes: 10 additions & 5 deletions plugins/axe-plugin/src/components/SelectionContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -66,20 +66,25 @@ export const selectionList = atom({
default: [],
});

export const isSelected = (selectionList: Selection) =>
export const isSelected = (targets?: Selection) =>
selector({
key: `isSelected_${selectionList.join('_')}`,
key: `isSelected_${targets ? targets.join('_') : 'none'}`,
get: ({ get }: any) => {
const selection = get(selectionList);
return selectionList.some((s: string) => selection.includes(s));
return targets
? targets.some((s: string) => selection.includes(s))
: false;
},
});

export const isTagSelected = (tag: string) =>
export const isTagSelected = (tag: string = '') =>
selector({
key: `isTagSelected_${tag}`,
get: ({ get }: any) => {
const tagged = get(taggedList);
return tagged[tag] ? isSelected(tagged[tag]) : false;
const selection = get(selectionList);
return tagged[tag]
? tagged[tag].some((s: string) => selection.includes(s))
: false;
},
});
46 changes: 42 additions & 4 deletions plugins/axe-plugin/src/index.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
/* eslint-disable react/display-name */
import React, { FC } from 'react';

import React, { FC, useRef } from 'react';
import { useSetRecoilState } from 'recoil';
import { run as runAxe, configure as configureAxe, reset } from 'axe-core';
import { resetTabCounter } from '@component-controls/components';
import {
StoryBlockContainer,
StoryBlockContainerProps,
Story,
} from '@component-controls/blocks';
import { Spec } from 'axe-core';

import { BaseAllyBlock } from './components/BaseAllyBlock';
import { axeResults } from './components/SelectionContext';

interface AxeAllyBlockOwmProps {
axeOptions?: Spec;
Expand All @@ -22,10 +26,44 @@ export const AxeAllyBlock: FC<AxeAllyBlockProps> = ({
axeOptions,
...props
}) => {
const setResults = useSetRecoilState(axeResults);
const storyRef = useRef<HTMLDivElement>(null);
const isRunning = useRef(false);
const collectResults = () => {
const canvas = storyRef.current?.firstChild;
if (canvas && isRunning.current === false) {
isRunning.current = true;
reset();
configureAxe(axeOptions || {});
resetTabCounter();
runAxe(canvas)
.then(results => {
setResults(results);
console.log(results);
isRunning.current = false;
})
.catch(e => {
console.error('error running axe-core', e);
isRunning.current = false;
});
}
};
const onRender = () => {
collectResults();
};

return (
<StoryBlockContainer {...props}>
{({ story }) => (
<BaseAllyBlock storyId={story?.id} options={axeOptions} />
{({ story: { id: storyId } = {} }) => (
<>
<React.Suspense fallback={<div>testing...</div>}>
<BaseAllyBlock options={axeOptions}>
<React.Profiler id="axe-plugin-scan" onRender={onRender}>
<Story key={storyId} id={storyId} ref={storyRef} />
</React.Profiler>
</BaseAllyBlock>
</React.Suspense>
</>
)}
</StoryBlockContainer>
);
Expand Down
33 changes: 18 additions & 15 deletions ui/blocks/src/context/block/BlockContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { RecoilRoot } from 'recoil';
import { StoryStore } from '@component-controls/store';
import { BlockDataContextProvider } from './BlockDataContext';
import { BlockControlsContextProvider } from './BlockControlsContext';
import { ErrorBoundary } from './ErrorBoundary';

export interface BlockContextInputProps {
/**
Expand Down Expand Up @@ -46,21 +47,23 @@ export const BlockContextProvider: React.FC<BlockContextInputProps> = ({
options,
}) => {
return (
<RecoilRoot>
<BlockContext.Provider
value={{
storyId,
storeProvider: store,
options,
}}
>
<BlockDataContextProvider store={store} storyId={storyId}>
<BlockControlsContextProvider store={store}>
{children}
</BlockControlsContextProvider>
</BlockDataContextProvider>
</BlockContext.Provider>
</RecoilRoot>
<ErrorBoundary>
<RecoilRoot>
<BlockContext.Provider
value={{
storyId,
storeProvider: store,
options,
}}
>
<BlockDataContextProvider store={store} storyId={storyId}>
<BlockControlsContextProvider store={store}>
{children}
</BlockControlsContextProvider>
</BlockDataContextProvider>
</BlockContext.Provider>
</RecoilRoot>
</ErrorBoundary>
);
};

Expand Down
21 changes: 21 additions & 0 deletions ui/blocks/src/context/block/ErrorBoundary.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import React, { Component } from 'react';

export class ErrorBoundary extends Component {
state = {
error: undefined,
};
componentDidCatch(error: Error | null, info: object) {
this.setState({ error: error || new Error('ERROR WAS NOT PROPAGATED') });
console.error(error, info);
}
render() {
const { children } = this.props;
const { error } = this.state;

if (error) {
return <h1>Error caught.</h1>;
}

return children;
}
}

0 comments on commit 656ea5b

Please sign in to comment.