diff --git a/addons/a11y/package.json b/addons/a11y/package.json index 64da24efde99..89639f6d4e76 100644 --- a/addons/a11y/package.json +++ b/addons/a11y/package.json @@ -40,6 +40,7 @@ "memoizerific": "^1.11.3", "react": "^16.8.4", "react-redux": "^7.0.2", + "react-sizeme": "^2.5.2", "redux": "^4.0.1", "util-deprecate": "^1.0.2" }, diff --git a/addons/a11y/src/components/A11YPanel.tsx b/addons/a11y/src/components/A11YPanel.tsx index 81532df57ff1..5ca6069ce9f7 100644 --- a/addons/a11y/src/components/A11YPanel.tsx +++ b/addons/a11y/src/components/A11YPanel.tsx @@ -175,10 +175,9 @@ export class A11YPanel extends Component { label: {violations.length} Violations, panel: ( ), items: violations, @@ -188,10 +187,9 @@ export class A11YPanel extends Component { label: {passes.length} Passes, panel: ( ), items: passes, @@ -201,10 +199,9 @@ export class A11YPanel extends Component { label: {incomplete.length} Incomplete, panel: ( ), items: incomplete, diff --git a/addons/a11y/src/components/ColorBlindness.tsx b/addons/a11y/src/components/ColorBlindness.tsx index c353e3dc334e..fa168ee5b12e 100644 --- a/addons/a11y/src/components/ColorBlindness.tsx +++ b/addons/a11y/src/components/ColorBlindness.tsx @@ -83,7 +83,7 @@ export class ColorBlindness extends Component ({ id: i, - title: i, + title: i.charAt(0).toUpperCase() + i.slice(1), onClick: () => { this.setFilter(i); }, diff --git a/addons/a11y/src/components/Report/Elements.tsx b/addons/a11y/src/components/Report/Elements.tsx index 3dc1d345df10..89b94f3f9cea 100644 --- a/addons/a11y/src/components/Report/Elements.tsx +++ b/addons/a11y/src/components/Report/Elements.tsx @@ -14,25 +14,25 @@ const Item = styled.li({ const ItemTitle = styled.span(({ theme }) => ({ borderBottom: `1px solid ${theme.appBorderColor}`, width: '100%', - display: 'inline-block', + display: 'flex', paddingBottom: '6px', marginBottom: '6px', + justifyContent: 'space-between', })); const HighlightToggleElement = styled.span({ fontWeight: 'normal', - float: 'right', + alignSelf: 'center', paddingRight: '15px', input: { margin: 0 }, }); interface ElementProps { element: NodeResult; - passes: boolean; type: RuleType; } -const Element: FunctionComponent = ({ element, passes, type }) => { +const Element: FunctionComponent = ({ element, type }) => { const { any, all, none } = element; const rules = [...any, ...all, ...none]; const highlightToggleId = `${type}-${element.target[0]}`; @@ -51,21 +51,20 @@ const Element: FunctionComponent = ({ element, passes, type }) => /> - + ); }; interface ElementsProps { elements: NodeResult[]; - passes: boolean; type: RuleType; } -export const Elements: FunctionComponent = ({ elements, passes, type }) => ( +export const Elements: FunctionComponent = ({ elements, type }) => (
    {elements.map((element, index) => ( - + ))}
); diff --git a/addons/a11y/src/components/Report/Item.tsx b/addons/a11y/src/components/Report/Item.tsx index 29e35499f9a3..d1303153439f 100644 --- a/addons/a11y/src/components/Report/Item.tsx +++ b/addons/a11y/src/components/Report/Item.tsx @@ -12,6 +12,7 @@ import HighlightToggle from './HighlightToggle'; const Wrapper = styled.div(({ theme }) => ({ display: 'flex', + width: '100%', borderBottom: `1px solid ${theme.appBorderColor}`, '&:hover': { background: theme.background.hoverable, @@ -49,14 +50,12 @@ const HighlightToggleElement = styled.span({ fontWeight: 'normal', float: 'right', marginRight: '15px', - marginTop: '10px', - + alignSelf: 'center', input: { margin: 0 }, }); interface ItemProps { item: Result; - passes: boolean; type: RuleType; } @@ -75,7 +74,7 @@ export class Item extends Component { })); render() { - const { item, passes, type } = this.props; + const { item, type } = this.props; const { open } = this.state; const highlightToggleId = `${type}-${item.id}`; @@ -104,7 +103,7 @@ export class Item extends Component { {open ? ( - + ) : null} diff --git a/addons/a11y/src/components/Report/Rules.tsx b/addons/a11y/src/components/Report/Rules.tsx index 91fb964536e6..c2e180a018ba 100644 --- a/addons/a11y/src/components/Report/Rules.tsx +++ b/addons/a11y/src/components/Report/Rules.tsx @@ -1,8 +1,9 @@ import React, { FunctionComponent } from 'react'; import { styled } from '@storybook/theming'; - -import { Icons } from '@storybook/components'; +import { Badge, Icons } from '@storybook/components'; import { CheckResult } from 'axe-core'; +import { RuleType } from '../A11YPanel'; +import { SizeMe } from 'react-sizeme'; const impactColors = { minor: '#f1c40f', @@ -15,18 +16,33 @@ const impactColors = { const List = styled.div({ display: 'flex', flexDirection: 'column', - padding: '4px', + paddingBottom: '4px', + paddingRight: '4px', + paddingTop: '4px', fontWeight: '400', } as any); -const Item = styled.div({ - display: 'flex', - flexDirection: 'row', - marginBottom: '6px', +const Item = styled.div(({ elementWidth }: { elementWidth: number }) => { + const maxWidthBeforeBreak = 407; + return { + flexDirection: elementWidth > maxWidthBeforeBreak ? 'row' : 'inherit', + marginBottom: elementWidth > maxWidthBeforeBreak ? '6px' : '12px', + display: elementWidth > maxWidthBeforeBreak ? 'flex' : 'block', + }; }); +const StyledBadge = styled(Badge)(({ status }: { status: string }) => ({ + padding: '2px 8px', + marginBottom: '3px', + minWidth: '65px', + maxWidth: 'fit-content', + width: '100%', + textAlign: 'center', +})); + const Message = styled.div({ paddingLeft: '6px', + paddingRight: '23px', }); const Status = styled.div(({ passes, impact }: { passes: boolean; impact: string }) => ({ @@ -40,30 +56,63 @@ const Status = styled.div(({ passes, impact }: { passes: boolean; impact: string }, })); +export enum ImpactValue { + MINOR = 'minor', + MODERATE = 'moderate', + SERIOUS = 'serious', + CRITICAL = 'critical', +} + interface RuleProps { rule: CheckResult; - passes: boolean; } -const Rule: FunctionComponent = ({ rule, passes }) => ( - - - {passes ? : } - - {rule.message} - -); +const formatSeverityText = (severity: string) => { + return severity + .charAt(0) + .toUpperCase() + .concat(severity.slice(1)); +}; + +const Rule: FunctionComponent = ({ rule }) => { + let badgeType: any = null; + switch (rule.impact) { + case ImpactValue.CRITICAL: + badgeType = 'critical'; + break; + case ImpactValue.SERIOUS: + badgeType = 'negative'; + break; + case ImpactValue.MODERATE: + badgeType = 'warning'; + break; + case ImpactValue.MINOR: + badgeType = 'neutral'; + break; + default: + break; + } + return ( + + {({ size }: { size: any }) => ( + + {formatSeverityText(rule.impact)} + {rule.message} + + )} + + ); +}; interface RulesProps { rules: CheckResult[]; - passes: boolean; } -export const Rules: FunctionComponent = ({ rules, passes }) => { +export const Rules: FunctionComponent = ({ rules }) => { return ( {rules.map((rule, index) => ( - + ))} ); diff --git a/addons/a11y/src/components/Report/__snapshots__/HighlightToggle.test.js.snap b/addons/a11y/src/components/Report/__snapshots__/HighlightToggle.test.js.snap index 1aea8f83cef1..b7fd858fa08c 100644 --- a/addons/a11y/src/components/Report/__snapshots__/HighlightToggle.test.js.snap +++ b/addons/a11y/src/components/Report/__snapshots__/HighlightToggle.test.js.snap @@ -125,6 +125,7 @@ exports[`HighlightToggle component should match snapshot 1`] = ` "app": "#F6F9FC", "bar": "#FFFFFF", "content": "#FFFFFF", + "critical": "#FF4400", "gridCellSize": 10, "hoverable": "rgba(0,0,0,.05)", "negative": "#FEDED2", @@ -256,6 +257,7 @@ exports[`HighlightToggle component should match snapshot 1`] = ` "color": Object { "ancillary": "#22a699", "border": "rgba(0,0,0,.1)", + "critical": "#FFFFFF", "dark": "#666666", "darker": "#444444", "darkest": "#333333", diff --git a/addons/a11y/src/components/Report/index.tsx b/addons/a11y/src/components/Report/index.tsx index 2388438ed033..62f080337eb3 100644 --- a/addons/a11y/src/components/Report/index.tsx +++ b/addons/a11y/src/components/Report/index.tsx @@ -7,14 +7,13 @@ import { RuleType } from '../A11YPanel'; export interface ReportProps { items: Result[]; empty: string; - passes: boolean; type: RuleType; } -export const Report: FunctionComponent = ({ items, empty, type, passes }) => ( +export const Report: FunctionComponent = ({ items, empty, type }) => ( {items && items.length ? ( - items.map(item => ) + items.map(item => ) ) : ( {empty} )} diff --git a/addons/a11y/src/components/Tabs.tsx b/addons/a11y/src/components/Tabs.tsx index 297c4ba893c8..2d546245b58a 100644 --- a/addons/a11y/src/components/Tabs.tsx +++ b/addons/a11y/src/components/Tabs.tsx @@ -1,13 +1,13 @@ import React, { Component, SyntheticEvent } from 'react'; -import { styled } from '@storybook/theming'; +import { styled, themes } from '@storybook/theming'; import store, { clearElements } from '../redux-config'; import HighlightToggle from './Report/HighlightToggle'; import { NodeResult, Result } from 'axe-core'; import { RuleType } from './A11YPanel'; +import { SizeMe } from 'react-sizeme'; -// TODO: reuse the Tabs component from @storybook/theming instead -// of re-building identical functionality +// TODO: reuse the Tabs component from @storybook/theming instead of re-building identical functionality const Container = styled.div({ width: '100%', @@ -18,26 +18,34 @@ const Container = styled.div({ const HighlightToggleLabel = styled.label(({ theme }) => ({ cursor: 'pointer', userSelect: 'none', + marginBottom: '3px', + marginRight: '3px', color: theme.color.dark, })); -const GlobalToggleWrapper = styled.div(({ theme }) => ({ - padding: '10px 15px 10px 0', - cursor: 'pointer', - fontSize: theme.typography.size.s2 - 1, - height: 40, - border: 'none', - - display: 'flex', - alignItems: 'center', - - input: { - marginLeft: 10, - marginRight: 0, - marginTop: 0, - marginBottom: 0, - }, -})); +const GlobalToggle = styled.div(({ elementWidth }: { elementWidth: number }) => { + const maxWidthBeforeBreak = 450; + return { + cursor: 'pointer', + fontSize: '14px', + padding: elementWidth > maxWidthBeforeBreak ? '12px 15px 10px 0' : '12px 0px 3px 12px', + height: '40px', + border: 'none', + marginTop: elementWidth > maxWidthBeforeBreak ? '-40px' : '0px', + float: elementWidth > maxWidthBeforeBreak ? 'right' : 'left', + display: elementWidth > maxWidthBeforeBreak ? 'flex' : 'block', + alignItems: 'center', + width: elementWidth > maxWidthBeforeBreak ? 'auto' : '100%', + borderBottom: elementWidth > maxWidthBeforeBreak ? 'none' : '1px solid rgba(0,0,0,.1)', + + input: { + marginLeft: '10', + marginRight: '0', + marginTop: '0', + marginBottom: '0', + }, + }; +}); const Item = styled.button( ({ theme }) => ({ @@ -113,34 +121,40 @@ export class Tabs extends Component { const highlightToggleId = `${tabs[active].type}-global-checkbox`; const highlightLabel = `Highlight results`; return ( - - - - {tabs.map((tab, index) => ( - - {tab.label} - - ))} - - - - {highlightLabel} - - - - - {tabs[active].panel} - + + {({ size }: { size: any }) => ( + + + + {tabs.map((tab, index) => ( + + {tab.label} + + ))} + + + {tabs[active].items.length > 0 ? ( + + + {highlightLabel} + + + + ) : null} + {tabs[active].panel} + + )} + ); } } diff --git a/addons/a11y/src/components/__snapshots__/A11YPanel.test.js.snap b/addons/a11y/src/components/__snapshots__/A11YPanel.test.js.snap index 274e9cf544d2..d550f4963147 100644 --- a/addons/a11y/src/components/__snapshots__/A11YPanel.test.js.snap +++ b/addons/a11y/src/components/__snapshots__/A11YPanel.test.js.snap @@ -103,133 +103,12 @@ exports[`A11YPanel should render loader when it's running 1`] = ` `; exports[`A11YPanel should render report 1`] = ` -.emotion-14 { +.emotion-0 { overflow-y: auto; overflow-x: auto; } -.emotion-13 { - width: 100%; - position: relative; - min-height: 100%; -} - -.emotion-10 { - box-shadow: rgba(0,0,0,.1) 0 -1px 0 0 inset; - background: rgba(0,0,0,.05); - display: -webkit-box; - display: -webkit-flex; - display: -ms-flexbox; - display: flex; - -webkit-box-pack: justify; - -webkit-justify-content: space-between; - -ms-flex-pack: justify; - justify-content: space-between; - white-space: nowrap; -} - -.emotion-1 { - -webkit-text-decoration: none; - text-decoration: none; - padding: 10px 15px; - cursor: pointer; - font-weight: 700; - font-size: 13px; - line-height: 1; - height: 40px; - border: none; - border-top: 3px solid transparent; - border-bottom: 3px solid transparent; - background: transparent; - opacity: 1; - border-bottom: 3px solid #1EA7FD; -} - -.emotion-1:focus { - outline: 0 none; - border-bottom: 3px solid #1EA7FD; -} - -.emotion-0 { - color: #FF4400; -} - -.emotion-3 { - -webkit-text-decoration: none; - text-decoration: none; - padding: 10px 15px; - cursor: pointer; - font-weight: 700; - font-size: 13px; - line-height: 1; - height: 40px; - border: none; - border-top: 3px solid transparent; - border-bottom: 3px solid transparent; - background: transparent; -} - -.emotion-3:focus { - outline: 0 none; - border-bottom: 3px solid #1EA7FD; -} - -.emotion-2 { - color: #66BF3C; -} - .emotion-4 { - color: #E69D00; -} - -.emotion-9 { - padding: 10px 15px 10px 0; - cursor: pointer; - font-size: 13px; - height: 40px; - border: none; - display: -webkit-box; - display: -webkit-flex; - display: -ms-flexbox; - display: flex; - -webkit-align-items: center; - -webkit-box-align: center; - -ms-flex-align: center; - align-items: center; -} - -.emotion-9 input { - margin-left: 10px; - margin-right: 0; - margin-top: 0; - margin-bottom: 0; -} - -.emotion-7 { - cursor: pointer; - -webkit-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - user-select: none; - color: #666666; -} - -.emotion-8 { - cursor: not-allowed; -} - -.emotion-12 { - padding: 30px; - text-align: center; - color: #333333; - font-size: 13px; -} - -.emotion-11 { - font-weight: 700; -} - -.emotion-18 { position: absolute; bottom: 0; right: 0; @@ -241,7 +120,7 @@ exports[`A11YPanel should render report 1`] = ` background: #FFFFFF; } -.emotion-17 { +.emotion-3 { border: 0 none; padding: 4px 10px; cursor: pointer; @@ -264,16 +143,16 @@ exports[`A11YPanel should render report 1`] = ` border-radius: 4px 0 0 0; } -.emotion-17:not(:last-child) { +.emotion-3:not(:last-child) { border-right: 1px solid rgba(0,0,0,.1); } -.emotion-17 + * { +.emotion-3 + * { border-left: 1px solid rgba(0,0,0,.1); border-radius: 0; } -.emotion-17:focus { +.emotion-3:focus { box-shadow: #1EA7FD 0 -3px 0 0 inset; outline: 0 none; } @@ -334,27 +213,25 @@ exports[`A11YPanel should render report 1`] = ` "inserted": Object { "0": true, "110qmus": true, - "152wg9i": true, "1551xjo": true, "15paq49": true, "176o2y5": true, "1977chw": true, - "1cwfnw4": true, - "1fp6daz": true, + "19mcg9j": true, + "1ez3l8h": true, + "1kbt4a0": true, "1l7fvsg": true, "1myfomu": true, - "1s6ajii": true, "1vwgrhn": true, "4ryd4s": true, "6hqipu": true, "animation-u07e3c": true, + "aq4p19": true, "fg630j": true, - "g90fw7": true, "iau1th": true, "jb2puo": true, "kqzqgg": true, "l0u0ek": true, - "nuzmgr": true, "qacwg0": true, "qb28": true, "snh8f7": true, @@ -364,7 +241,7 @@ exports[`A11YPanel should render report 1`] = ` "key": "css", "nonce": undefined, "registered": Object { - "emotion-14": "overflow-y:auto;overflow-x:auto;", + "emotion-0": "overflow-y:auto;overflow-x:auto;", "css-1977chw": "height:10px;width:10px;min-width:10px;color:#999999;margin-right:10px;transition:transform 0.1s ease-in-out;align-self:center;display:inline-flex;", "css-1l7fvsg": "height:12px;width:12px;margin-right:4px;", "css-jb2puo": "height:12px;width:12px;margin-right:4px;animation:animation-u07e3c 1s linear infinite;;", @@ -605,175 +482,163 @@ exports[`A11YPanel should render report 1`] = ` data-emotion="css" > - .emotion-14{overflow-y:auto;overflow-x:auto;} - - - , - "ctr": 37, + "ctr": 35, "isSpeedy": false, "key": "css", "nonce": undefined, @@ -833,175 +708,163 @@ exports[`A11YPanel should render report 1`] = ` data-emotion="css" > - .emotion-14{overflow-y:auto;overflow-x:auto;} - , - , , , , , , , - , , , , , , , , , , , , , , , , , , , , , , ,