From f52f54a5b7db8ef2c8867aa7b6f5ea5bf1e8d821 Mon Sep 17 00:00:00 2001 From: madalynrose Date: Tue, 30 Apr 2024 11:57:21 -0400 Subject: [PATCH 1/9] grab data from any checks for display in table --- src/assessments/types/requirement.ts | 2 + src/background/assessment-data-converter.ts | 26 +++++-- src/background/stores/assessment-store.ts | 1 + .../property-bag/target-size-property-bag.ts | 16 +++++ src/scanner/target-size-utils.ts | 68 +++++++++++++++++++ 5 files changed, 108 insertions(+), 5 deletions(-) create mode 100644 src/common/types/property-bag/target-size-property-bag.ts create mode 100644 src/scanner/target-size-utils.ts diff --git a/src/assessments/types/requirement.ts b/src/assessments/types/requirement.ts index c8fa1b5b421..61f3b5c1a1b 100644 --- a/src/assessments/types/requirement.ts +++ b/src/assessments/types/requirement.ts @@ -23,6 +23,7 @@ import { ContentPageComponent } from 'views/content/content-page'; import { IGetMessageGenerator } from '../assessment-default-message-generator'; import { InstanceTableColumn, InstanceTableHeaderType } from './instance-table-data'; import { ReportInstanceFields } from './report-instance-field'; +import { ChecksType } from 'background/assessment-data-converter'; export interface Requirement { key: string; @@ -60,6 +61,7 @@ export interface Requirement { getDefaultMessage?: IGetMessageGenerator; instanceTableHeaderType?: InstanceTableHeaderType; getCompletedRequirementDetailsForTelemetry?: (assessmentData: AssessmentData) => any; + generatePropertyBagFrom?: (ruleResult: DecoratedAxeNodeResult, checkName: ChecksType) => any; } export type VisualHelperToggleConfigDeps = { diff --git a/src/background/assessment-data-converter.ts b/src/background/assessment-data-converter.ts index 2de034f0387..e998a490ef7 100644 --- a/src/background/assessment-data-converter.ts +++ b/src/background/assessment-data-converter.ts @@ -36,6 +36,10 @@ export class AssessmentDataConverter { getInstanceStatus: (result: DecoratedAxeNodeResult) => ManualTestStatus, isVisualizationSupported: (result: DecoratedAxeNodeResult) => boolean, getIncludedAlwaysRules: () => string[], + generatePropertyBagFrom?: ( + ruleResult: DecoratedAxeNodeResult, + checkName: ChecksType, + ) => any, ): AssessmentInstancesMap { let instancesMap: AssessmentInstancesMap = {}; @@ -62,6 +66,7 @@ export class AssessmentDataConverter { ruleResult, getInstanceStatus, isVisualizationSupported, + generatePropertyBagFrom, ); } }); @@ -110,6 +115,10 @@ export class AssessmentDataConverter { ruleResult: DecoratedAxeNodeResult, getInstanceStatus: (result: DecoratedAxeNodeResult) => ManualTestStatus, isVisualizationSupported: (result: DecoratedAxeNodeResult) => boolean, + generatePropertyBagFrom?: ( + ruleResult: DecoratedAxeNodeResult, + checkName: ChecksType, + ) => any, ): GeneratedAssessmentInstance { const target: Target = elementAxeResult.target; let testStepResults = {}; @@ -130,7 +139,7 @@ export class AssessmentDataConverter { ); let actualPropertyBag = { - ...this.getPropertyBagFromAnyChecks(ruleResult), + ...this.getPropertyBagFromAnyChecks(ruleResult, generatePropertyBagFrom), ...propertyBag, }; actualPropertyBag = isEmpty(actualPropertyBag) ? null : actualPropertyBag; @@ -203,8 +212,16 @@ export class AssessmentDataConverter { }; } - private getPropertyBagFromAnyChecks(ruleResult: DecoratedAxeNodeResult): any { - return this.getPropertyBagFrom(ruleResult, 'any'); + private getPropertyBagFromAnyChecks( + ruleResult: DecoratedAxeNodeResult, + generatePropertyBagFrom?: ( + ruleResult: DecoratedAxeNodeResult, + checkName: ChecksType, + ) => any, + ): any { + return generatePropertyBagFrom + ? generatePropertyBagFrom(ruleResult, 'any') + : this.getPropertyBagFrom(ruleResult, 'any'); } private getPropertyBagFrom(ruleResult: DecoratedAxeNodeResult, checkName: ChecksType): any { @@ -215,7 +232,6 @@ export class AssessmentDataConverter { ) { return ruleResult[checkName][0].data; } - return null; } @@ -234,4 +250,4 @@ export class AssessmentDataConverter { } } -type ChecksType = 'any' | 'all'; +export type ChecksType = 'any' | 'all'; diff --git a/src/background/stores/assessment-store.ts b/src/background/stores/assessment-store.ts index 8388a2d0f8f..7cb0a13e103 100644 --- a/src/background/stores/assessment-store.ts +++ b/src/background/stores/assessment-store.ts @@ -409,6 +409,7 @@ export class AssessmentStore extends PersistentStore { stepConfig.getInstanceStatus, stepConfig.isVisualizationSupportedForResult, getIncludedAlwaysRules, + stepConfig.generatePropertyBagFrom, ); assessmentData.generatedAssessmentInstancesMap = generatedAssessmentInstancesMap; assessmentData.testStepStatus[step].isStepScanned = true; diff --git a/src/common/types/property-bag/target-size-property-bag.ts b/src/common/types/property-bag/target-size-property-bag.ts new file mode 100644 index 00000000000..67e361430ba --- /dev/null +++ b/src/common/types/property-bag/target-size-property-bag.ts @@ -0,0 +1,16 @@ +/* + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. + */ +import { ColumnValueBag } from './column-value-bag'; + +export interface TargetSizePropertyBag extends ColumnValueBag { + width?: number; + height?: number; + closestOffset?: number; + messageKey?: string; + sizeMessage?: string; + offsetMessage?: string; + minSize?: number; + minOffset?: number; +} diff --git a/src/scanner/target-size-utils.ts b/src/scanner/target-size-utils.ts new file mode 100644 index 00000000000..4ff66a70bc9 --- /dev/null +++ b/src/scanner/target-size-utils.ts @@ -0,0 +1,68 @@ +/* + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. + */ +import { DictionaryStringTo } from 'types/common-types'; +import { TargetSizePropertyBag } from 'common/types/property-bag/target-size-property-bag'; + +const targetSizeMessageMap: DictionaryStringTo = { + size: { + pass: { + default: props => + `Element has sufficient touch target size (${props.height}px by ${props.width}px).`, + obscured: props => `OBSCURED`, + large: props => `LARGE`, + }, + fail: { + default: props => + `Element has insufficient touch target size (${props.height}px by ${props.width}px, should be at least ${props.minSize}px by ${props.minSize}px) `, + partiallyObscured: props => + `Element has insufficient touch target size (${props.height}px by ${props.width}px, should be at least ${props.minSize}px by ${props.minSize}px) because it is partially obscured.`, + }, + incomplete: { + default: props => + `Element has negative tabindex with insufficient touch target size (${props.height}px by ${props.width}px, should be at least ${props.minSize}px by ${props.minSize}px). This may be OK if the element is not a touch target.`, + contentOverflow: props => + `Unknown: Element touch target size could not be accurately determined due to overflow content.`, + partiallyObscured: props => + `Unknown: Element with negative tabindex has insufficient touch target size because it is partially obscured. This may be OK if the element is not a touch target.`, + partiallyObscuredNonTabbable: props => + `Unknown: Element has insufficient touch target size because it is partially obscured by a neighbor with negative tabindex. This may be OK if the neighbor is not a touch target.`, + tooManyRects: props => + `Unknown: Could not determine element target size because there are too many overlapping elements. `, + }, + }, + offset: { + pass: { + default: props => + `Element has sufficient offset from its closest neighbor (${props.closestOffset}px)`, + large: props => `LARGE`, + }, + fail: { + default: props => + `Element has insufficient offset to its closest neighbor (${props.closestOffset}px in diameter, should be at least ${props.minOffset}px in diameter)`, + }, + incomplete: { + default: props => + `Element with negative tabindex has insufficient offset to its closest neighbor. This may be OK if the element is not a touch target.`, + nonTabbableNeighbor: props => + `Element has sufficient offset from its closest neighbor and the closest neighbor has a negative tabindex. This may be OK if the neighbor is not a touch target.`, + }, + }, +}; +export const getTargetSizeMessage = ( + rule: string, + status: string, + propertyBag: TargetSizePropertyBag, +): string => { + console.log('getTargetSizeMessage', rule, status, propertyBag); + const messageKey = propertyBag.messageKey || 'default'; + const messageProps = { + height: propertyBag.height, + width: propertyBag.width, + closestOffset: propertyBag.closestOffset, + minOffset: propertyBag.minOffset, + minSize: propertyBag.minSize, + }; + return targetSizeMessageMap[rule.split('-')[1]][status][messageKey](messageProps); +}; From 61264fe5a9b223127001dbb4fc5d0a048385e36f Mon Sep 17 00:00:00 2001 From: madalynrose Date: Tue, 30 Apr 2024 11:58:13 -0400 Subject: [PATCH 2/9] use any checks to display messages in instances table --- .../pointer-motion/test-steps/target-size.tsx | 81 ++++++++++++++++++- src/injected/scanner-utils.ts | 11 +++ 2 files changed, 91 insertions(+), 1 deletion(-) diff --git a/src/assessments/pointer-motion/test-steps/target-size.tsx b/src/assessments/pointer-motion/test-steps/target-size.tsx index c69cb51cb7b..7695aabe1a3 100644 --- a/src/assessments/pointer-motion/test-steps/target-size.tsx +++ b/src/assessments/pointer-motion/test-steps/target-size.tsx @@ -3,10 +3,23 @@ import { link } from 'content/link'; import * as content from 'content/test/pointer-motion/target-size'; import * as React from 'react'; +import { PropertyBagColumnRendererConfig } from '../../../common/types/property-bag/property-bag-column-renderer-config'; import { ManualTestRecordYourResults } from '../../common/manual-test-record-your-results'; import * as Markup from '../../markup'; import { Requirement } from '../../types/requirement'; import { PointerMotionTestStep } from './test-steps'; +import { TargetSizePropertyBag } from 'common/types/property-bag/target-size-property-bag'; +import { PropertyBagColumnRendererFactory } from 'assessments/common/property-bag-column-renderer-factory'; +import { ReportInstanceField } from 'assessments/types/report-instance-field'; +import { AnalyzerProvider } from 'injected/analyzers/analyzer-provider'; +import { AnalyzerConfigurationFactory } from 'assessments/common/analyzer-configuration-factory'; +import { AnalyzerConfiguration } from 'injected/analyzers/analyzer'; +import { ScannerUtils } from 'injected/scanner-utils'; +import { AssessmentVisualizationEnabledToggle } from 'DetailsView/components/assessment-visualization-enabled-toggle'; +import { DecoratedAxeNodeResult } from 'common/types/store-data/visualization-scan-result-data'; +import { isEmpty } from 'lodash'; +import { ChecksType } from 'background/assessment-data-converter'; +import { getTargetSizeMessage } from 'scanner/target-size-utils'; const description: JSX.Element = ( @@ -73,12 +86,78 @@ const howToTest: JSX.Element = ( ); +const displayPropertyBagConfig: PropertyBagColumnRendererConfig[] = [ + { + propertyName: 'sizeMessage', + displayName: 'Size', + defaultValue: null, + }, + { + propertyName: 'offsetMessage', + displayName: 'Offset', + defaultValue: null, + }, +]; + +const generateTargetSizePropertyBagFrom = ( + ruleResult: DecoratedAxeNodeResult, + checkName: ChecksType, +): TargetSizePropertyBag => { + if ( + ruleResult[checkName] && + !isEmpty(ruleResult[checkName]) && + ruleResult[checkName].some(r => r.data) + ) { + const status = + ruleResult.status === true + ? 'pass' + : ruleResult.status === false + ? 'fail' + : 'incomplete'; + const data = Object.assign( + {}, + ...ruleResult[checkName].map(r => { + console.log(r); + return { + ...r.data, + [`${r.id.split('-')[1]}Message`]: getTargetSizeMessage(r.id, status, r.data), + }; + }), + ); + return data; + } + return null; +}; + export const TargetSize: Requirement = { key: PointerMotionTestStep.targetSize, name: 'Target size', description, howToTest, ...content, - isManual: true, + isManual: false, + columnsConfig: [ + { + key: 'touch-target-info', + name: 'Touch target info', + onRender: + PropertyBagColumnRendererFactory.getRenderer( + displayPropertyBagConfig, + ), + }, + ], + reportInstanceFields: ReportInstanceField.fromColumns(displayPropertyBagConfig), + getAnalyzer: (provider: AnalyzerProvider, analyzerConfig: AnalyzerConfiguration) => + provider.createRuleAnalyzer( + AnalyzerConfigurationFactory.forScanner({ + rules: ['target-size'], + resultProcessor: (scanner: ScannerUtils) => scanner.getAllApplicableInstances, + ...analyzerConfig, + }), + ), guidanceLinks: [link.WCAG_2_5_8], + getDrawer: provider => provider.createHighlightBoxDrawer(), + getVisualHelperToggle: props => , + generatePropertyBagFrom: generateTargetSizePropertyBagFrom, + // getCompletedRequirementDetailsForTelemetry: labelInNameGetCompletedRequirementDetails, }; diff --git a/src/injected/scanner-utils.ts b/src/injected/scanner-utils.ts index b0974551566..7abdc339cb8 100644 --- a/src/injected/scanner-utils.ts +++ b/src/injected/scanner-utils.ts @@ -52,6 +52,17 @@ export class ScannerUtils { return resultsMap; }; + public getAllApplicableInstances = ( + results: ScanResults, + ): DictionaryStringTo => { + const resultsMap: DictionaryStringTo = {}; + this.addFailuresToDictionary(resultsMap, results.violations); + this.addIncompletesToDictionary(resultsMap, results.incomplete); + this.addPassesToDictionary(resultsMap, results.passes); + + return resultsMap; + }; + public getAllCompletedInstances = ( results: ScanResults, ): DictionaryStringTo => { From 9512320c2d4e991eafa324cd8e2364b3c54ed854 Mon Sep 17 00:00:00 2001 From: madalynrose Date: Fri, 3 May 2024 13:25:22 -0400 Subject: [PATCH 3/9] things make it to the page --- .../property-bag-column-renderer-factory.tsx | 48 +++++++ ...property-bag-component-column-renderer.tsx | 96 +++++++++++++ .../pointer-motion/test-steps/target-size.tsx | 25 +++- .../types/report-instance-field.ts | 1 + .../property-bag-column-renderer-config.ts | 5 + .../property-bag/target-size-property-bag.ts | 7 +- src/scanner/target-size-utils.ts | 68 --------- src/scanner/target-size-utils.tsx | 129 ++++++++++++++++++ .../target-size/fail-size-default.html | 24 ++++ .../fail-size-po-offset-default.html | 29 ++++ .../incomplete-offset-default.html | 4 + ...complete-offset-non-tabbable-neighbor.html | 23 ++++ .../incomplete-offset-too-many-rects.html | 4 + .../incomplete-size-content-overflow.html | 23 ++++ .../target-size/incomplete-size-default.html | 28 ++++ .../incomplete-size-partially-obscured.html | 34 +++++ ...plete-size-partially-obscurred-no-tab.html | 30 ++++ .../target-size/pass-offset-default.html | 28 ++++ .../target-size/pass-offset-large.html | 24 ++++ .../target-size/pass-size-default.html | 23 ++++ .../target-size/pass-size-large.html | 23 ++++ .../target-size/pass-size-obscured.html | 27 ++++ 22 files changed, 626 insertions(+), 77 deletions(-) create mode 100644 src/assessments/common/property-bag-component-column-renderer.tsx delete mode 100644 src/scanner/target-size-utils.ts create mode 100644 src/scanner/target-size-utils.tsx create mode 100644 src/tests/end-to-end/test-resources/target-size/fail-size-default.html create mode 100644 src/tests/end-to-end/test-resources/target-size/fail-size-po-offset-default.html create mode 100644 src/tests/end-to-end/test-resources/target-size/incomplete-offset-default.html create mode 100644 src/tests/end-to-end/test-resources/target-size/incomplete-offset-non-tabbable-neighbor.html create mode 100644 src/tests/end-to-end/test-resources/target-size/incomplete-offset-too-many-rects.html create mode 100644 src/tests/end-to-end/test-resources/target-size/incomplete-size-content-overflow.html create mode 100644 src/tests/end-to-end/test-resources/target-size/incomplete-size-default.html create mode 100644 src/tests/end-to-end/test-resources/target-size/incomplete-size-partially-obscured.html create mode 100644 src/tests/end-to-end/test-resources/target-size/incomplete-size-partially-obscurred-no-tab.html create mode 100644 src/tests/end-to-end/test-resources/target-size/pass-offset-default.html create mode 100644 src/tests/end-to-end/test-resources/target-size/pass-offset-large.html create mode 100644 src/tests/end-to-end/test-resources/target-size/pass-size-default.html create mode 100644 src/tests/end-to-end/test-resources/target-size/pass-size-large.html create mode 100644 src/tests/end-to-end/test-resources/target-size/pass-size-obscured.html diff --git a/src/assessments/common/property-bag-column-renderer-factory.tsx b/src/assessments/common/property-bag-column-renderer-factory.tsx index 1d0d8ce48d4..d03ba37ed1f 100644 --- a/src/assessments/common/property-bag-column-renderer-factory.tsx +++ b/src/assessments/common/property-bag-column-renderer-factory.tsx @@ -4,6 +4,7 @@ import { InstanceTableRow } from 'assessments/types/instance-table-data'; import { ColumnValueBag } from 'common/types/property-bag/column-value-bag'; import { PropertyBagColumnRendererConfig } from 'common/types/property-bag/property-bag-column-renderer-config'; import { propertyBagColumnRenderer } from './property-bag-column-renderer'; +import * as React from 'react'; export class PropertyBagColumnRendererFactory { public static getRenderer( @@ -14,3 +15,50 @@ export class PropertyBagColumnRendererFactory { }; } } + +export class PropertyBagColumnRendererWithComputationFactory { + public static getRenderer( + configs: PropertyBagColumnRendererConfig[], + ): (item: InstanceTableRow) => JSX.Element { + return item => { + return propertyBagColumnRendererWithComputation(item, configs); + }; + } +} + +export function propertyBagColumnRendererWithComputation( + item: InstanceTableRow, + configs: PropertyBagColumnRendererConfig[], +): JSX.Element { + const mapper = (config: PropertyBagColumnRendererConfig, index: number) => { + if (item.instance.propertyBag == null) { + return null; + } + if (config.compute == null) { + return null; + } + if ( + config.neededPropertyBagValues.some( + key => item.instance.propertyBag.hasOwnProperty(key) === false, + ) && + config.defaultValue == null + ) { + return null; + } + return render(config, index); + }; + + const render = (config: PropertyBagColumnRendererConfig, index: number) => { + console.log(item.instance); + const ValueComponent = config.compute(item.instance.propertyBag); + console.log('get by calling compute', ValueComponent); + return ( +
+ {`${config.displayName}: `} + +
+ ); + }; + + return
{configs.map(mapper)}
; +} diff --git a/src/assessments/common/property-bag-component-column-renderer.tsx b/src/assessments/common/property-bag-component-column-renderer.tsx new file mode 100644 index 00000000000..2290637a5c4 --- /dev/null +++ b/src/assessments/common/property-bag-component-column-renderer.tsx @@ -0,0 +1,96 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +import { InstanceTableRow } from 'assessments/types/instance-table-data'; +import { NamedFC } from 'common/react/named-fc'; +import { ColumnValueBag } from 'common/types/property-bag/column-value-bag'; +import { PropertyBagColumnRendererConfig } from 'common/types/property-bag/property-bag-column-renderer-config'; +import { isEmpty } from 'lodash'; +import * as React from 'react'; + +import { DictionaryStringTo } from 'types/common-types'; +import { Term } from 'assessments/markup'; +import { PropertyBagColumnRenderer } from 'assessments/common/property-bag-column-renderer'; + +export class PropertyBagComponentColumnRenderer< + TPropertyBag extends ColumnValueBag, +> extends PropertyBagColumnRenderer { + private render = ( + config: PropertyBagColumnRendererConfig, + value: any, + index: number, + ) => { + return ( +
+ {`${config.displayName}: `} + {this.renderValue(config, value)} +
+ ); + }; + + private mapper = (config: PropertyBagColumnRendererConfig, index: number) => { + if (this.item.instance.propertyBag == null) { + return null; + } + const value = this.item.instance.propertyBag[config.propertyName]; + if (value == null && config.defaultValue == null) { + return null; + } + + return this.render(config, value, index); + }; + + protected renderValue = (config: PropertyBagColumnRendererConfig, value: any) => { + if (config.expand) { + return this.renderProperties(config, value); + } + return {() => value}; + // const valueToRender = value || config.defaultValue; + // if (typeof valueToRender === 'string') { + // return {valueToRender}; + // } + + // const ValueComponent = valueToRender; + // console.log(ValueComponent, ); + // return ( + // + // Hello, this is a test + // + // ); + }; + + private renderProperties = ( + config: PropertyBagColumnRendererConfig, + propertyMap: DictionaryStringTo, + ) => { + if (isEmpty(propertyMap)) { + return {config.defaultValue}; + } + + return Object.keys(propertyMap).map(key => { + return ( +
+ {this.renderInnerKeyValue(key, propertyMap[key])} +
+ ); + }); + }; + + private renderInnerKeyValue = (key: string, value: string) => { + if (value === null) { + return {`${key}`}; + } else { + return ( + + {`${key}: `} + {value} + + ); + } + }; + + public getRenderer = (): JSX.Element => { + return
{this.configs.map(this.mapper)}
; + }; +} diff --git a/src/assessments/pointer-motion/test-steps/target-size.tsx b/src/assessments/pointer-motion/test-steps/target-size.tsx index 7695aabe1a3..9a96bc1793f 100644 --- a/src/assessments/pointer-motion/test-steps/target-size.tsx +++ b/src/assessments/pointer-motion/test-steps/target-size.tsx @@ -9,7 +9,7 @@ import * as Markup from '../../markup'; import { Requirement } from '../../types/requirement'; import { PointerMotionTestStep } from './test-steps'; import { TargetSizePropertyBag } from 'common/types/property-bag/target-size-property-bag'; -import { PropertyBagColumnRendererFactory } from 'assessments/common/property-bag-column-renderer-factory'; +import { PropertyBagColumnRendererWithComputationFactory } from 'assessments/common/property-bag-column-renderer-factory'; import { ReportInstanceField } from 'assessments/types/report-instance-field'; import { AnalyzerProvider } from 'injected/analyzers/analyzer-provider'; import { AnalyzerConfigurationFactory } from 'assessments/common/analyzer-configuration-factory'; @@ -19,7 +19,7 @@ import { AssessmentVisualizationEnabledToggle } from 'DetailsView/components/ass import { DecoratedAxeNodeResult } from 'common/types/store-data/visualization-scan-result-data'; import { isEmpty } from 'lodash'; import { ChecksType } from 'background/assessment-data-converter'; -import { getTargetSizeMessage } from 'scanner/target-size-utils'; +import { getTargetSizeColumnComponents } from 'scanner/target-size-utils'; const description: JSX.Element = ( @@ -85,17 +85,30 @@ const howToTest: JSX.Element = ( ); +const computePropertyBagValue: ( + ruleType: 'size' | 'offset', +) => (propertyBag: TargetSizePropertyBag) => typeof React.Component = + (ruleType: 'size' | 'offset') => (propertyBag: TargetSizePropertyBag) => { + const targetSizeColumnComponentGetter = + getTargetSizeColumnComponents(ruleType)(propertyBag); + console.log('get using property bag values', targetSizeColumnComponentGetter); + return targetSizeColumnComponentGetter; + }; const displayPropertyBagConfig: PropertyBagColumnRendererConfig[] = [ { propertyName: 'sizeMessage', displayName: 'Size', defaultValue: null, + neededPropertyBagValues: ['height', 'width', 'minSize'], + compute: computePropertyBagValue('size'), }, { propertyName: 'offsetMessage', displayName: 'Offset', defaultValue: null, + neededPropertyBagValues: ['closestOffset', 'minOffset'], + compute: computePropertyBagValue('offset'), }, ]; @@ -117,18 +130,18 @@ const generateTargetSizePropertyBagFrom = ( const data = Object.assign( {}, ...ruleResult[checkName].map(r => { - console.log(r); return { ...r.data, - [`${r.id.split('-')[1]}Message`]: getTargetSizeMessage(r.id, status, r.data), + [`${r.id.split('-')[1]}Status`]: status, + [`${r.id.split('-')[1]}MessageKey`]: r.data.messageKey, }; }), ); + console.log(data); return data; } return null; }; - export const TargetSize: Requirement = { key: PointerMotionTestStep.targetSize, name: 'Target size', @@ -141,7 +154,7 @@ export const TargetSize: Requirement = { key: 'touch-target-info', name: 'Touch target info', onRender: - PropertyBagColumnRendererFactory.getRenderer( + PropertyBagColumnRendererWithComputationFactory.getRenderer( displayPropertyBagConfig, ), }, diff --git a/src/assessments/types/report-instance-field.ts b/src/assessments/types/report-instance-field.ts index 3340d52aa69..1ae93a841c0 100644 --- a/src/assessments/types/report-instance-field.ts +++ b/src/assessments/types/report-instance-field.ts @@ -64,6 +64,7 @@ const common: { [key in CommonReportInstanceFieldKey]: ReportInstanceField } = { }; function isValid(value: ColumnValue): ColumnValue { + console.log('valid??', isValid); if (!value) { return false; } diff --git a/src/common/types/property-bag/property-bag-column-renderer-config.ts b/src/common/types/property-bag/property-bag-column-renderer-config.ts index 581e3029cfa..fac2a840832 100644 --- a/src/common/types/property-bag/property-bag-column-renderer-config.ts +++ b/src/common/types/property-bag/property-bag-column-renderer-config.ts @@ -1,6 +1,8 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. +import { TargetSizePropertyBag } from 'common/types/property-bag/target-size-property-bag'; import { ColumnValue, ColumnValueBag } from './column-value-bag'; +import * as React from 'react'; export const NoValue = '(no value)'; export interface PropertyBagColumnRendererConfig { @@ -8,4 +10,7 @@ export interface PropertyBagColumnRendererConfig typeof React.Component; + render?: (element: JSX.Element) => JSX.Element; } diff --git a/src/common/types/property-bag/target-size-property-bag.ts b/src/common/types/property-bag/target-size-property-bag.ts index 67e361430ba..82a1386616b 100644 --- a/src/common/types/property-bag/target-size-property-bag.ts +++ b/src/common/types/property-bag/target-size-property-bag.ts @@ -8,9 +8,10 @@ export interface TargetSizePropertyBag extends ColumnValueBag { width?: number; height?: number; closestOffset?: number; - messageKey?: string; - sizeMessage?: string; - offsetMessage?: string; + sizeMessageKey?: string; + offsetMessageKey?: string; + sizeStatus?: string; + offsetStatus?: string; minSize?: number; minOffset?: number; } diff --git a/src/scanner/target-size-utils.ts b/src/scanner/target-size-utils.ts deleted file mode 100644 index 4ff66a70bc9..00000000000 --- a/src/scanner/target-size-utils.ts +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. - */ -import { DictionaryStringTo } from 'types/common-types'; -import { TargetSizePropertyBag } from 'common/types/property-bag/target-size-property-bag'; - -const targetSizeMessageMap: DictionaryStringTo = { - size: { - pass: { - default: props => - `Element has sufficient touch target size (${props.height}px by ${props.width}px).`, - obscured: props => `OBSCURED`, - large: props => `LARGE`, - }, - fail: { - default: props => - `Element has insufficient touch target size (${props.height}px by ${props.width}px, should be at least ${props.minSize}px by ${props.minSize}px) `, - partiallyObscured: props => - `Element has insufficient touch target size (${props.height}px by ${props.width}px, should be at least ${props.minSize}px by ${props.minSize}px) because it is partially obscured.`, - }, - incomplete: { - default: props => - `Element has negative tabindex with insufficient touch target size (${props.height}px by ${props.width}px, should be at least ${props.minSize}px by ${props.minSize}px). This may be OK if the element is not a touch target.`, - contentOverflow: props => - `Unknown: Element touch target size could not be accurately determined due to overflow content.`, - partiallyObscured: props => - `Unknown: Element with negative tabindex has insufficient touch target size because it is partially obscured. This may be OK if the element is not a touch target.`, - partiallyObscuredNonTabbable: props => - `Unknown: Element has insufficient touch target size because it is partially obscured by a neighbor with negative tabindex. This may be OK if the neighbor is not a touch target.`, - tooManyRects: props => - `Unknown: Could not determine element target size because there are too many overlapping elements. `, - }, - }, - offset: { - pass: { - default: props => - `Element has sufficient offset from its closest neighbor (${props.closestOffset}px)`, - large: props => `LARGE`, - }, - fail: { - default: props => - `Element has insufficient offset to its closest neighbor (${props.closestOffset}px in diameter, should be at least ${props.minOffset}px in diameter)`, - }, - incomplete: { - default: props => - `Element with negative tabindex has insufficient offset to its closest neighbor. This may be OK if the element is not a touch target.`, - nonTabbableNeighbor: props => - `Element has sufficient offset from its closest neighbor and the closest neighbor has a negative tabindex. This may be OK if the neighbor is not a touch target.`, - }, - }, -}; -export const getTargetSizeMessage = ( - rule: string, - status: string, - propertyBag: TargetSizePropertyBag, -): string => { - console.log('getTargetSizeMessage', rule, status, propertyBag); - const messageKey = propertyBag.messageKey || 'default'; - const messageProps = { - height: propertyBag.height, - width: propertyBag.width, - closestOffset: propertyBag.closestOffset, - minOffset: propertyBag.minOffset, - minSize: propertyBag.minSize, - }; - return targetSizeMessageMap[rule.split('-')[1]][status][messageKey](messageProps); -}; diff --git a/src/scanner/target-size-utils.tsx b/src/scanner/target-size-utils.tsx new file mode 100644 index 00000000000..d3f63291822 --- /dev/null +++ b/src/scanner/target-size-utils.tsx @@ -0,0 +1,129 @@ +/* + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. + */ +import { DictionaryStringTo } from 'types/common-types'; +import { TargetSizePropertyBag } from 'common/types/property-bag/target-size-property-bag'; +import * as React from 'react'; +import * as Markup from 'assessments/markup'; + +const targetSizeMessageMap: DictionaryStringTo = { + size: { + pass: { + default: props => ( + + Element has sufficient touch target size ({props.height}px by {props.width} + px). + + ), + obscured: props => <>OBSCURED, + large: props => <>LARGE, //4.9 + }, + fail: { + default: props => ( + + Element has insufficient touch target size ( + {props.height}px by {props.width} + px, should be at least {props.minSize}px by {props.minSize}px){' '} + + ), + partiallyObscured: props => ( + + Element has insufficient touch target size ( + {props.height}px by {props.width} + px, should be at least {props.minSize}px by {props.minSize}px) because it is + partially obscured. + + ), + }, + incomplete: { + default: props => ( + + Element has negative tabindex with insufficient touch + target size ({props.height}px by {props.width}px, should be at least{' '} + {props.minSize}px by + {props.minSize}px). This may be OK if the element is not a touch target. + + ), + contentOverflow: props => ( + + Unknown: Element touch target size could not be + accurately determined due to overflow content. + + ), + partiallyObscured: props => ( + + Unknown: Element with negative tabindex has + insufficient touch target size because it is partially obscured. This may be OK + if the element is not a touch target. + + ), + partiallyObscuredNonTabbable: props => ( + + Unknown: Element has insufficient touch target size + because it is partially obscured by a neighbor with negative tabindex. This may + be OK if the neighbor is not a touch target. + + ), + tooManyRects: props => ( + + Unknown: Could not determine element target size + because there are too many overlapping elements. + + ), + }, + }, + offset: { + pass: { + default: props => ( + + Element has sufficient offset from its closest neighbor ({props.closestOffset} + px) + + ), + large: props => <>LARGE, + }, + fail: { + default: props => ( + + Element has insufficient offset to its closest neighbor ({props.closestOffset} + px in diameter, should be at least {props.minOffset}px in diameter) + + ), + }, + incomplete: { + default: props => ( + + Element with negative tabindex has insufficient offset to its closest neighbor. + This may be OK if the element is not a touch target. + + ), + nonTabbableNeighbor: props => ( + + Element has sufficient offset from its closest neighbor and the closest neighbor + has a negative tabindex. This may be OK if the neighbor is not a touch target. + + ), + }, + }, +}; + +const getTargetSizeMessageComponentForRule: ( + ruleType: 'size' | 'offset', + propertyBag: TargetSizePropertyBag, +) => typeof React.Component = (ruleType: 'size' | 'offset', propertyBag: TargetSizePropertyBag) => { + const status = propertyBag[`${ruleType}Status`] as string; + if (status == null) { + return null; + } + const messageKey = (propertyBag[`${ruleType}MessageKey`] as string) || 'default'; + console.log(ruleType, messageKey, status); + return targetSizeMessageMap[ruleType][status][messageKey]; +}; + +export const getTargetSizeColumnComponents: ( + ruleType: 'size' | 'offset', +) => (propertyBag: TargetSizePropertyBag) => typeof React.Component = + (ruleType: 'size' | 'offset') => (propertyBag: TargetSizePropertyBag) => { + return getTargetSizeMessageComponentForRule(ruleType, propertyBag); + }; diff --git a/src/tests/end-to-end/test-resources/target-size/fail-size-default.html b/src/tests/end-to-end/test-resources/target-size/fail-size-default.html new file mode 100644 index 00000000000..4332a3d52bf --- /dev/null +++ b/src/tests/end-to-end/test-resources/target-size/fail-size-default.html @@ -0,0 +1,24 @@ + + + + + + Target size + + + +

Target Size Rule Example Controls

+ +

Failing - Size - Default

+ + hmmm + + a + + mmmmm + + + diff --git a/src/tests/end-to-end/test-resources/target-size/fail-size-po-offset-default.html b/src/tests/end-to-end/test-resources/target-size/fail-size-po-offset-default.html new file mode 100644 index 00000000000..be9509678ef --- /dev/null +++ b/src/tests/end-to-end/test-resources/target-size/fail-size-po-offset-default.html @@ -0,0 +1,29 @@ + + + + + + Target size + + + +

Target Size Rule Example Controls

+ +

Failing - Size - Partially Obscured, Offset - Default

+ + + a + + b + + + + diff --git a/src/tests/end-to-end/test-resources/target-size/incomplete-offset-default.html b/src/tests/end-to-end/test-resources/target-size/incomplete-offset-default.html new file mode 100644 index 00000000000..1e6a9e7bed8 --- /dev/null +++ b/src/tests/end-to-end/test-resources/target-size/incomplete-offset-default.html @@ -0,0 +1,4 @@ + diff --git a/src/tests/end-to-end/test-resources/target-size/incomplete-offset-non-tabbable-neighbor.html b/src/tests/end-to-end/test-resources/target-size/incomplete-offset-non-tabbable-neighbor.html new file mode 100644 index 00000000000..e2fb3de80ae --- /dev/null +++ b/src/tests/end-to-end/test-resources/target-size/incomplete-offset-non-tabbable-neighbor.html @@ -0,0 +1,23 @@ + + + + + + Target size + + + +

Target Size Rule Example Controls

+ +

Incomplete - Offset - Non-Tabbable Neighbor

+ + b + a + c + + + + diff --git a/src/tests/end-to-end/test-resources/target-size/incomplete-offset-too-many-rects.html b/src/tests/end-to-end/test-resources/target-size/incomplete-offset-too-many-rects.html new file mode 100644 index 00000000000..1e6a9e7bed8 --- /dev/null +++ b/src/tests/end-to-end/test-resources/target-size/incomplete-offset-too-many-rects.html @@ -0,0 +1,4 @@ + diff --git a/src/tests/end-to-end/test-resources/target-size/incomplete-size-content-overflow.html b/src/tests/end-to-end/test-resources/target-size/incomplete-size-content-overflow.html new file mode 100644 index 00000000000..59461e1db32 --- /dev/null +++ b/src/tests/end-to-end/test-resources/target-size/incomplete-size-content-overflow.html @@ -0,0 +1,23 @@ + + + + + + Target size + + + +

Target Size Rule Example Controls

+ +

Incomplete - Size - Content Overflow

+ + + + + + + + diff --git a/src/tests/end-to-end/test-resources/target-size/incomplete-size-default.html b/src/tests/end-to-end/test-resources/target-size/incomplete-size-default.html new file mode 100644 index 00000000000..c03f695ab0c --- /dev/null +++ b/src/tests/end-to-end/test-resources/target-size/incomplete-size-default.html @@ -0,0 +1,28 @@ + + + + + + Target size + + + +

Target Size Rule Example Controls

+ +

Incomplete - Size - Default

+ + + a + + + + + diff --git a/src/tests/end-to-end/test-resources/target-size/incomplete-size-partially-obscured.html b/src/tests/end-to-end/test-resources/target-size/incomplete-size-partially-obscured.html new file mode 100644 index 00000000000..6fba7b0b20a --- /dev/null +++ b/src/tests/end-to-end/test-resources/target-size/incomplete-size-partially-obscured.html @@ -0,0 +1,34 @@ + + + + + + Target size + + + +

Target Size Rule Example Controls

+ +

Incomplete - Size - Partially Obscured

+ + + a + + b + + + + diff --git a/src/tests/end-to-end/test-resources/target-size/incomplete-size-partially-obscurred-no-tab.html b/src/tests/end-to-end/test-resources/target-size/incomplete-size-partially-obscurred-no-tab.html new file mode 100644 index 00000000000..e60ea657611 --- /dev/null +++ b/src/tests/end-to-end/test-resources/target-size/incomplete-size-partially-obscurred-no-tab.html @@ -0,0 +1,30 @@ + + + + + + Target size + + + +

Target Size Rule Example Controls

+ +

Incomplete - Size - Partially Obscured Non-Tabbable

+ + + a + + x + + + + diff --git a/src/tests/end-to-end/test-resources/target-size/pass-offset-default.html b/src/tests/end-to-end/test-resources/target-size/pass-offset-default.html new file mode 100644 index 00000000000..94a54f3aa10 --- /dev/null +++ b/src/tests/end-to-end/test-resources/target-size/pass-offset-default.html @@ -0,0 +1,28 @@ + + + + + + Target offset + + + +

Target Size Rule Example Controls

+ +

Passing - Offset - Default

+ + b + + a + + + + + diff --git a/src/tests/end-to-end/test-resources/target-size/pass-offset-large.html b/src/tests/end-to-end/test-resources/target-size/pass-offset-large.html new file mode 100644 index 00000000000..0b44aea274e --- /dev/null +++ b/src/tests/end-to-end/test-resources/target-size/pass-offset-large.html @@ -0,0 +1,24 @@ + + + + + + Target offset + + + +

Target Size Rule Example Controls

+ +

Passing - Offset - Large

+ + b + + a + + + + + diff --git a/src/tests/end-to-end/test-resources/target-size/pass-size-default.html b/src/tests/end-to-end/test-resources/target-size/pass-size-default.html new file mode 100644 index 00000000000..528979267b5 --- /dev/null +++ b/src/tests/end-to-end/test-resources/target-size/pass-size-default.html @@ -0,0 +1,23 @@ + + + + + + Target size + + + +

Target Size Rule Example Controls

+ +

Passing - Size - Default

+ + + a + + + + + diff --git a/src/tests/end-to-end/test-resources/target-size/pass-size-large.html b/src/tests/end-to-end/test-resources/target-size/pass-size-large.html new file mode 100644 index 00000000000..55c5aae59e9 --- /dev/null +++ b/src/tests/end-to-end/test-resources/target-size/pass-size-large.html @@ -0,0 +1,23 @@ + + + + + + Target size + + + +

Target Size Rule Example Controls

+ +

Passing - Size - Large

+ + + + + + diff --git a/src/tests/end-to-end/test-resources/target-size/pass-size-obscured.html b/src/tests/end-to-end/test-resources/target-size/pass-size-obscured.html new file mode 100644 index 00000000000..05bc03158a9 --- /dev/null +++ b/src/tests/end-to-end/test-resources/target-size/pass-size-obscured.html @@ -0,0 +1,27 @@ + + + + + + Target size + + + +

Target Size Rule Example Controls

+ +

Passing - Size - Obscured

+ + a + b + + + + From 1a37065099e4134e6a53c6b9c5f1b62518297cf7 Mon Sep 17 00:00:00 2001 From: madalynrose Date: Thu, 9 May 2024 15:21:27 -0400 Subject: [PATCH 4/9] create new column renderer for target size --- .../property-bag-column-renderer-factory.tsx | 48 ----- .../target-size-column-renderer-factory.tsx | 16 ++ .../target-size-column-renderer.tsx | 192 ++++++++++++++++++ .../pointer-motion/test-steps/target-size.tsx | 73 ++++--- 4 files changed, 243 insertions(+), 86 deletions(-) create mode 100644 src/assessments/pointer-motion/target-size-column-renderer-factory.tsx create mode 100644 src/assessments/pointer-motion/target-size-column-renderer.tsx diff --git a/src/assessments/common/property-bag-column-renderer-factory.tsx b/src/assessments/common/property-bag-column-renderer-factory.tsx index d03ba37ed1f..1d0d8ce48d4 100644 --- a/src/assessments/common/property-bag-column-renderer-factory.tsx +++ b/src/assessments/common/property-bag-column-renderer-factory.tsx @@ -4,7 +4,6 @@ import { InstanceTableRow } from 'assessments/types/instance-table-data'; import { ColumnValueBag } from 'common/types/property-bag/column-value-bag'; import { PropertyBagColumnRendererConfig } from 'common/types/property-bag/property-bag-column-renderer-config'; import { propertyBagColumnRenderer } from './property-bag-column-renderer'; -import * as React from 'react'; export class PropertyBagColumnRendererFactory { public static getRenderer( @@ -15,50 +14,3 @@ export class PropertyBagColumnRendererFactory { }; } } - -export class PropertyBagColumnRendererWithComputationFactory { - public static getRenderer( - configs: PropertyBagColumnRendererConfig[], - ): (item: InstanceTableRow) => JSX.Element { - return item => { - return propertyBagColumnRendererWithComputation(item, configs); - }; - } -} - -export function propertyBagColumnRendererWithComputation( - item: InstanceTableRow, - configs: PropertyBagColumnRendererConfig[], -): JSX.Element { - const mapper = (config: PropertyBagColumnRendererConfig, index: number) => { - if (item.instance.propertyBag == null) { - return null; - } - if (config.compute == null) { - return null; - } - if ( - config.neededPropertyBagValues.some( - key => item.instance.propertyBag.hasOwnProperty(key) === false, - ) && - config.defaultValue == null - ) { - return null; - } - return render(config, index); - }; - - const render = (config: PropertyBagColumnRendererConfig, index: number) => { - console.log(item.instance); - const ValueComponent = config.compute(item.instance.propertyBag); - console.log('get by calling compute', ValueComponent); - return ( -
- {`${config.displayName}: `} - -
- ); - }; - - return
{configs.map(mapper)}
; -} diff --git a/src/assessments/pointer-motion/target-size-column-renderer-factory.tsx b/src/assessments/pointer-motion/target-size-column-renderer-factory.tsx new file mode 100644 index 00000000000..12056a5d614 --- /dev/null +++ b/src/assessments/pointer-motion/target-size-column-renderer-factory.tsx @@ -0,0 +1,16 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +import { targetSizeColumnRenderer } from 'assessments/pointer-motion/target-size-column-renderer'; +import { InstanceTableRow } from 'assessments/types/instance-table-data'; +import { ColumnValueBag } from 'common/types/property-bag/column-value-bag'; +import { PropertyBagColumnRendererConfig } from 'common/types/property-bag/property-bag-column-renderer-config'; + +export class TargetSizeColumnRendererFactory { + public static getColumnComponent( + configs: PropertyBagColumnRendererConfig[], + ): (item: InstanceTableRow) => JSX.Element { + return item => { + return targetSizeColumnRenderer(item, configs); + }; + } +} diff --git a/src/assessments/pointer-motion/target-size-column-renderer.tsx b/src/assessments/pointer-motion/target-size-column-renderer.tsx new file mode 100644 index 00000000000..4e8b10a3878 --- /dev/null +++ b/src/assessments/pointer-motion/target-size-column-renderer.tsx @@ -0,0 +1,192 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +import { InstanceTableRow } from 'assessments/types/instance-table-data'; +import * as Markup from 'assessments/markup'; +import { ColumnValueBag } from 'common/types/property-bag/column-value-bag'; +import { PropertyBagColumnRendererConfig } from 'common/types/property-bag/property-bag-column-renderer-config'; +import * as React from 'react'; +import { DictionaryStringTo } from 'types/common-types'; +import { PropertyBagColumnRendererFactory } from '../common/property-bag-column-renderer-factory'; +import { TargetSizePropertyBag } from 'common/types/property-bag/target-size-property-bag'; + +export function targetSizeColumnRenderer( + item: InstanceTableRow, + configs: PropertyBagColumnRendererConfig[], +): JSX.Element { + const propertyBag = item.instance.propertyBag; + const renderColumnComponent = ( + propertyBag: TargetSizePropertyBag, + columnComponent: (props: TargetSizePropertyBag) => JSX.Element, + ) => { + return columnComponent(propertyBag); + }; + + if ( + propertyBag.sizeStatus && + hasNeededProperties(['height', 'width', 'minSize'], propertyBag) + ) { + propertyBag.sizeMessageKey = propertyBag.sizeMessageKey || 'default'; + + propertyBag.sizeComponent = ( + + {renderColumnComponent( + propertyBag, + getTargetSizeMessageComponentFromPropertyBag(propertyBag), + )} + + ); + } + if ( + propertyBag.offsetStatus && + hasNeededProperties(['closestOffset', 'minOffset'], propertyBag) + ) { + propertyBag.offsetMessageKey = propertyBag.offsetMessageKey || 'default'; + propertyBag.offsetComponent = ( + + {renderColumnComponent( + propertyBag, + getTargetOffsetMessageComponentFromPropertyBag(propertyBag), + )} + + ); + } + + const propertyBagRenderer = PropertyBagColumnRendererFactory.getRenderer(configs); + + return propertyBagRenderer(item); +} + +function hasNeededProperties( + neededProperties: string[], + propertyBag: TargetSizePropertyBag, +): boolean { + return !neededProperties.some(key => propertyBag.hasOwnProperty(key) === false); +} + +export function getTargetSizeMessageComponentFromPropertyBag( + propertyBag: TargetSizePropertyBag, +): (props: TargetSizePropertyBag) => JSX.Element { + return statusWithMessageKeyToMessageComponentMapping.size[propertyBag.sizeStatus][ + propertyBag.sizeMessageKey + ]; +} + +export function getTargetOffsetMessageComponentFromPropertyBag( + propertyBag: TargetSizePropertyBag, +): (props: TargetSizePropertyBag) => JSX.Element { + return statusWithMessageKeyToMessageComponentMapping.offset[propertyBag.offsetStatus][ + propertyBag.offsetMessageKey + ]; +} + +const statusWithMessageKeyToMessageComponentMapping: DictionaryStringTo< + DictionaryStringTo JSX.Element>> +> = { + size: { + pass: { + default: props => ( + <> + Element has sufficient touch target size ({props.height}px by {props.width} + px). + + ), + obscured: props => ( + <>Element was ignored because it is fully obscured and not clickable. + ), + large: props => <>Element has sufficient touch target size., + }, + fail: { + default: props => ( + <> + Element has insufficient touch target size ( + {props.height}px by {props.width} + px, should be at least {props.minSize}px by {props.minSize}px){' '} + + ), + partiallyObscured: props => ( + <> + Element has insufficient touch target size ( + {props.height}px by {props.width} + px, should be at least {props.minSize}px by {props.minSize}px) because it is + partially obscured. + + ), + }, + incomplete: { + default: props => ( + <> + Element has negative tabindex with insufficient touch + target size ({props.height}px by {props.width}px, should be at least{' '} + {props.minSize}px by + {props.minSize}px). This may be OK if the element is + not a touch target. + + ), + contentOverflow: props => ( + <> + Element touch target size{' '} + could not be accurately determined due to overflow + content. + + ), + partiallyObscured: props => ( + <> + Element with negative tabindex has + insufficient touch target size because it is + partially obscured. This may be OK if the element is + not a touch target. + + ), + partiallyObscuredNonTabbable: props => ( + <> + Element has insufficient + touch target size because it is partially obscured by a neighbor with negative + tabindex. This may be OK if the neighbor is not a + touch target. + + ), + tooManyRects: props => ( + <> + Could not determine element target size + because there are too many overlapping elements. + + ), + }, + }, + offset: { + pass: { + default: props => ( + <> + Element has sufficient offset from its closest neighbor ({props.closestOffset} + px) + + ), + large: props => <>Element has sufficient offset from its closest neighbor., + }, + fail: { + default: props => ( + <> + Element has insufficient offset to its closest + neighbor ({props.closestOffset} + px in diameter, should be at least {props.minOffset}px in diameter) + + ), + }, + incomplete: { + default: props => ( + <> + Element with negative tabindex has + insufficient offset to its closest neighbor. This{' '} + may be OK if the element is not a touch target. + + ), + nonTabbableNeighbor: props => ( + <> + Element has sufficient offset from its closest + neighbor and the closest neighbor has a negative tabindex. This{' '} + may be OK if the neighbor is not a touch target. + + ), + }, + }, +}; diff --git a/src/assessments/pointer-motion/test-steps/target-size.tsx b/src/assessments/pointer-motion/test-steps/target-size.tsx index 9a96bc1793f..d0dcf03ff2c 100644 --- a/src/assessments/pointer-motion/test-steps/target-size.tsx +++ b/src/assessments/pointer-motion/test-steps/target-size.tsx @@ -9,7 +9,6 @@ import * as Markup from '../../markup'; import { Requirement } from '../../types/requirement'; import { PointerMotionTestStep } from './test-steps'; import { TargetSizePropertyBag } from 'common/types/property-bag/target-size-property-bag'; -import { PropertyBagColumnRendererWithComputationFactory } from 'assessments/common/property-bag-column-renderer-factory'; import { ReportInstanceField } from 'assessments/types/report-instance-field'; import { AnalyzerProvider } from 'injected/analyzers/analyzer-provider'; import { AnalyzerConfigurationFactory } from 'assessments/common/analyzer-configuration-factory'; @@ -19,7 +18,11 @@ import { AssessmentVisualizationEnabledToggle } from 'DetailsView/components/ass import { DecoratedAxeNodeResult } from 'common/types/store-data/visualization-scan-result-data'; import { isEmpty } from 'lodash'; import { ChecksType } from 'background/assessment-data-converter'; -import { getTargetSizeColumnComponents } from 'scanner/target-size-utils'; +import { TargetSizeColumnRendererFactory } from 'assessments/pointer-motion/target-size-column-renderer-factory'; +import { + getTargetOffsetMessageComponentFromPropertyBag, + getTargetSizeMessageComponentFromPropertyBag, +} from 'assessments/pointer-motion/target-size-column-renderer'; const description: JSX.Element = ( @@ -30,27 +33,30 @@ const description: JSX.Element = ( const howToTest: JSX.Element = (
+

+ For this requirement, Accessibility Insights for Web highlights non-inline focusable + elements on the target page and checks the touch target size. +

+

+ + Note: If no matching/failing instances are found, this requirement will + automatically be marked as pass. + +

  1. - Examine the target page to identify interactive elements which have been created - by authors (non-native browser controls). + In the Instances list below, examine each element, + and verify the element is a sufficient size and{' '} + sufficient offset from its neighbor.

  2. - Verify these elements are a minimum size of 24x24 css pixels. The following - exceptions apply: + If an element does not have sufficient size and/or sufficient offset from its + neighbor, verify the following exceptions do not apply:

      -
    • -

      - Spacing: These elements may be - smaller than 24x24 css pixels so long as it is within a 24x24 css pixel - target spacing circle that doesn’t overlap with other targets or their - 24x24 target spacing circle. -

      -
    • Equivalent: If an alternative control @@ -58,13 +64,6 @@ const howToTest: JSX.Element = ( criteria.

    • -
    • -

      - Inline: The target is in a sentence, - or its size is otherwise constrained by the line-height of non-target - text. -

      -
    • User agent control: The size of the @@ -85,30 +84,17 @@ const howToTest: JSX.Element = (

); -const computePropertyBagValue: ( - ruleType: 'size' | 'offset', -) => (propertyBag: TargetSizePropertyBag) => typeof React.Component = - (ruleType: 'size' | 'offset') => (propertyBag: TargetSizePropertyBag) => { - const targetSizeColumnComponentGetter = - getTargetSizeColumnComponents(ruleType)(propertyBag); - console.log('get using property bag values', targetSizeColumnComponentGetter); - return targetSizeColumnComponentGetter; - }; const displayPropertyBagConfig: PropertyBagColumnRendererConfig[] = [ { - propertyName: 'sizeMessage', + propertyName: 'sizeComponent', displayName: 'Size', defaultValue: null, - neededPropertyBagValues: ['height', 'width', 'minSize'], - compute: computePropertyBagValue('size'), }, { - propertyName: 'offsetMessage', + propertyName: 'offsetComponent', displayName: 'Offset', defaultValue: null, - neededPropertyBagValues: ['closestOffset', 'minOffset'], - compute: computePropertyBagValue('offset'), }, ]; @@ -154,12 +140,23 @@ export const TargetSize: Requirement = { key: 'touch-target-info', name: 'Touch target info', onRender: - PropertyBagColumnRendererWithComputationFactory.getRenderer( + TargetSizeColumnRendererFactory.getColumnComponent( displayPropertyBagConfig, ), }, ], - reportInstanceFields: ReportInstanceField.fromColumns(displayPropertyBagConfig), + reportInstanceFields: [ + ReportInstanceField.fromPropertyBagFunction( + 'Size', + 'sizeComponent', + pb => getTargetSizeMessageComponentFromPropertyBag(pb).toString(), + ), + ReportInstanceField.fromPropertyBagFunction( + 'Offset', + 'offsetComponent', + pb => getTargetOffsetMessageComponentFromPropertyBag(pb).toString(), + ), + ], getAnalyzer: (provider: AnalyzerProvider, analyzerConfig: AnalyzerConfiguration) => provider.createRuleAnalyzer( AnalyzerConfigurationFactory.forScanner({ From 763bbe19c384e550710b6ac73e8026d9a45454c8 Mon Sep 17 00:00:00 2001 From: madalynrose Date: Thu, 9 May 2024 15:21:57 -0400 Subject: [PATCH 5/9] write some tests --- .../target-size-column-renderer.test.tsx.snap | 145 ++++++++++++++ ...rget-size-column-renderer-factory.test.tsx | 78 ++++++++ .../target-size-column-renderer.test.tsx | 177 ++++++++++++++++++ 3 files changed, 400 insertions(+) create mode 100644 src/tests/unit/tests/assessments/pointer-motion/__snapshots__/target-size-column-renderer.test.tsx.snap create mode 100644 src/tests/unit/tests/assessments/pointer-motion/target-size-column-renderer-factory.test.tsx create mode 100644 src/tests/unit/tests/assessments/pointer-motion/target-size-column-renderer.test.tsx diff --git a/src/tests/unit/tests/assessments/pointer-motion/__snapshots__/target-size-column-renderer.test.tsx.snap b/src/tests/unit/tests/assessments/pointer-motion/__snapshots__/target-size-column-renderer.test.tsx.snap new file mode 100644 index 00000000000..c7473161858 --- /dev/null +++ b/src/tests/unit/tests/assessments/pointer-motion/__snapshots__/target-size-column-renderer.test.tsx.snap @@ -0,0 +1,145 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`TargetSizeColumnRenderer when offsetStatus is set should render offsetComponent when the necessary properties are available 1`] = ` + +
+
+ + display a: + + value for a +
+
+ + offset: + + + Element has sufficient offset from its closest neighbor (25px) + +
+
+
+`; + +exports[`TargetSizeColumnRenderer when offsetStatus is set should return null when the necessary properties are not available 1`] = ` + +
+
+ + display a: + + value for a +
+
+
+`; + +exports[`TargetSizeColumnRenderer when sizeStatus is set should render expected message with bolded words when sizeStatus is fail 1`] = ` + +
+
+ + display a: + + value for a +
+
+ + size: + + + Element has + + insufficient + + touch target size (5px by 5px, should be at least 28px by 28px) + +
+
+
+`; + +exports[`TargetSizeColumnRenderer when sizeStatus is set should render sizeComponent when the necessary properties are available 1`] = ` + +
+
+ + display a: + + value for a +
+
+ + size: + + + Element has sufficient touch target size (40px by 40px). + +
+
+
+`; + +exports[`TargetSizeColumnRenderer when sizeStatus is set should return null when the necessary properties are not available 1`] = ` + +
+
+ + display a: + + value for a +
+
+
+`; diff --git a/src/tests/unit/tests/assessments/pointer-motion/target-size-column-renderer-factory.test.tsx b/src/tests/unit/tests/assessments/pointer-motion/target-size-column-renderer-factory.test.tsx new file mode 100644 index 00000000000..08f19d50693 --- /dev/null +++ b/src/tests/unit/tests/assessments/pointer-motion/target-size-column-renderer-factory.test.tsx @@ -0,0 +1,78 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +import { render } from '@testing-library/react'; +import { InstanceTableRow } from 'assessments/types/instance-table-data'; +import { PropertyBagColumnRendererConfig } from 'common/types/property-bag/property-bag-column-renderer-config'; +import * as React from 'react'; + +import { ColumnValueBag } from '../../../../../common/types/property-bag/column-value-bag'; +import { RendererWrapper } from '../common/renderer-wrapper'; +import { TargetSizeColumnRendererFactory } from 'assessments/pointer-motion/target-size-column-renderer-factory'; + +describe('TargetSizeColumnRendererFactory', () => { + let configs: PropertyBagColumnRendererConfig[]; + let item: InstanceTableRow; + + beforeEach(() => { + configs = [ + { + propertyName: 'a', + displayName: 'display a', + }, + { + propertyName: 'sizeComponent', + displayName: 'size', + }, + { + propertyName: 'offsetComponent', + displayName: 'offset', + }, + ]; + + item = { + key: 'stub-key', + instance: { + html: null, + target: null, + testStepResults: null, + propertyBag: { + a: 'value for a', + sizeStatus: 'pass', + height: 40, + width: 40, + minSize: 30, + }, + }, + statusChoiceGroup: null, + visualizationButton: null, + }; + }); + + it('getColumnComponent should render component', () => { + const result = TargetSizeColumnRendererFactory.getColumnComponent(configs); + + checkPropertyBagAndTag(result, 'SPAN'); + }); + + function checkPropertyBagAndTag(result: Function, tag: string): void { + const renderer = () => result(item); + + const renderResult = render(); + renderResult.debug(); + const div = renderResult.container.querySelector('.property-bag-container'); + expect(div).not.toBeNull(); + expect(div.children).toHaveLength(2); + + const targetSizeSpan = div.children[1]; + expect(targetSizeSpan).not.toBeUndefined(); + expect(targetSizeSpan.classList.contains('property-bag-div')).toBeTruthy(); + expect(targetSizeSpan.children).toHaveLength(2); + const contentElement = targetSizeSpan.children[1]; + expect(contentElement).not.toBeNull(); + expect(contentElement.tagName).toBe(tag); + } +}); + +interface TestPropertyBag extends ColumnValueBag { + a: string; +} diff --git a/src/tests/unit/tests/assessments/pointer-motion/target-size-column-renderer.test.tsx b/src/tests/unit/tests/assessments/pointer-motion/target-size-column-renderer.test.tsx new file mode 100644 index 00000000000..41361973ca7 --- /dev/null +++ b/src/tests/unit/tests/assessments/pointer-motion/target-size-column-renderer.test.tsx @@ -0,0 +1,177 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +import { render } from '@testing-library/react'; +import { InstanceTableRow } from 'assessments/types/instance-table-data'; +import { PropertyBagColumnRendererConfig } from 'common/types/property-bag/property-bag-column-renderer-config'; +import * as React from 'react'; + +import { ColumnValueBag } from '../../../../../common/types/property-bag/column-value-bag'; +import { RendererWrapper } from '../common/renderer-wrapper'; +import { targetSizeColumnRenderer } from 'assessments/pointer-motion/target-size-column-renderer'; + +describe('TargetSizeColumnRenderer', () => { + let configs: PropertyBagColumnRendererConfig[]; + let item: InstanceTableRow; + + beforeEach(() => { + configs = [ + { + propertyName: 'a', + displayName: 'display a', + }, + { + propertyName: 'sizeComponent', + displayName: 'size', + }, + { + propertyName: 'offsetComponent', + displayName: 'offset', + }, + ]; + + item = { + key: 'stub-key', + instance: { + html: null, + target: null, + testStepResults: null, + propertyBag: { + a: 'value for a', + }, + }, + statusChoiceGroup: null, + visualizationButton: null, + }; + }); + + it('should render unrelated properties', () => { + const renderer = () => targetSizeColumnRenderer(item, configs); + + const renderResult = render(); + + const div = renderResult.container.querySelector('.property-bag-container'); + expect(div).not.toBeNull(); + expect(div.children).toHaveLength(1); + + const designPatternSpan = div.children[0]; + expect(designPatternSpan).not.toBeUndefined(); + expect(designPatternSpan.classList.contains('property-bag-div')).toBeTruthy(); + expect(designPatternSpan.children).toHaveLength(1); + expect(designPatternSpan.textContent).toEqual( + `${configs[0].displayName}: ${item.instance.propertyBag.a}`, + ); + }); + + describe('when sizeStatus is set', () => { + beforeEach(() => { + item.instance.propertyBag.sizeStatus = 'pass'; + }); + it('should render sizeComponent when the necessary properties are available', () => { + item.instance.propertyBag.height = 40; + item.instance.propertyBag.width = 40; + item.instance.propertyBag.minSize = 28; + const renderer = () => targetSizeColumnRenderer(item, configs); + + const renderResult = render(); + const div = renderResult.container.querySelector('.property-bag-container'); + expect(div).not.toBeNull(); + expect(div.children).toHaveLength(2); + + const propertyBagDiv = div.children[1]; + expect(propertyBagDiv).not.toBeUndefined(); + expect(propertyBagDiv.classList.contains('property-bag-div')).toBeTruthy(); + expect(propertyBagDiv.children).toHaveLength(2); + + const targetSizeSpan = propertyBagDiv.children[1]; + expect(targetSizeSpan.id).toEqual('target-size'); + expect(targetSizeSpan.children.length).toEqual(0); + expect(targetSizeSpan.textContent).toContain( + `${item.instance.propertyBag.height}px by ${item.instance.propertyBag.width}px`, + ); + expect(renderResult.asFragment()).toMatchSnapshot(); + }); + + it('should return null when the necessary properties are not available', () => { + item.instance.propertyBag.a = 'value for a'; + const renderer = () => targetSizeColumnRenderer(item, configs); + + const renderResult = render(); + + const div = renderResult.container.querySelector('.property-bag-container'); + expect(div).not.toBeNull(); + expect(div.children).toHaveLength(1); + expect(renderResult.asFragment()).toMatchSnapshot(); + }); + + it('should render expected message with bolded words when sizeStatus is fail', () => { + item.instance.propertyBag.sizeStatus = 'fail'; + item.instance.propertyBag.height = 5; + item.instance.propertyBag.width = 5; + item.instance.propertyBag.minSize = 28; + const renderer = () => targetSizeColumnRenderer(item, configs); + + const renderResult = render(); + const div = renderResult.container.querySelector('.property-bag-container'); + expect(div).not.toBeNull(); + expect(div.children).toHaveLength(2); + + const propertyBagDiv = div.children[1]; + expect(propertyBagDiv).not.toBeUndefined(); + expect(propertyBagDiv.classList.contains('property-bag-div')).toBeTruthy(); + expect(propertyBagDiv.children).toHaveLength(2); + + const targetSizeSpan = propertyBagDiv.children[1]; + expect(targetSizeSpan.id).toEqual('target-size'); + expect(targetSizeSpan.children.length).toEqual(1); + expect(targetSizeSpan.textContent).toContain( + `${item.instance.propertyBag.height}px by ${item.instance.propertyBag.width}px`, + ); + expect(renderResult.asFragment()).toMatchSnapshot(); + }); + }); + + describe('when offsetStatus is set', () => { + beforeEach(() => { + item.instance.propertyBag.offsetStatus = 'pass'; + }); + it('should render offsetComponent when the necessary properties are available', () => { + item.instance.propertyBag.closestOffset = 25; + item.instance.propertyBag.minOffset = 25; + const renderer = () => targetSizeColumnRenderer(item, configs); + + const renderResult = render(); + const div = renderResult.container.querySelector('.property-bag-container'); + expect(div).not.toBeNull(); + expect(div.children).toHaveLength(2); + + const propertyBagDiv = div.children[1]; + expect(propertyBagDiv).not.toBeUndefined(); + expect(propertyBagDiv.classList.contains('property-bag-div')).toBeTruthy(); + expect(propertyBagDiv.children).toHaveLength(2); + + const targetOffsetSpan = propertyBagDiv.children[1]; + expect(targetOffsetSpan.id).toEqual('target-offset'); + expect(targetOffsetSpan.children.length).toEqual(0); + expect(targetOffsetSpan.textContent).toContain( + `${item.instance.propertyBag.closestOffset}px`, + ); + expect(renderResult.asFragment()).toMatchSnapshot(); + }); + + it('should return null when the necessary properties are not available', () => { + item.instance.propertyBag.a = 'value for a'; + const renderer = () => targetSizeColumnRenderer(item, configs); + + const renderResult = render(); + + const div = renderResult.container.querySelector('.property-bag-container'); + expect(div).not.toBeNull(); + expect(div.children).toHaveLength(1); + expect(renderResult.asFragment()).toMatchSnapshot(); + }); + }); +}); + +interface TestPropertyBag extends ColumnValueBag { + a: string; +} From a63bf6617720d9876fe9e4b6f9f5eed111aecd00 Mon Sep 17 00:00:00 2001 From: madalynrose Date: Thu, 9 May 2024 16:42:28 -0400 Subject: [PATCH 6/9] undo unnecessary changes --- ...property-bag-component-column-renderer.tsx | 96 ------------------- .../property-bag-column-renderer-config.ts | 5 - 2 files changed, 101 deletions(-) delete mode 100644 src/assessments/common/property-bag-component-column-renderer.tsx diff --git a/src/assessments/common/property-bag-component-column-renderer.tsx b/src/assessments/common/property-bag-component-column-renderer.tsx deleted file mode 100644 index 2290637a5c4..00000000000 --- a/src/assessments/common/property-bag-component-column-renderer.tsx +++ /dev/null @@ -1,96 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. -import { InstanceTableRow } from 'assessments/types/instance-table-data'; -import { NamedFC } from 'common/react/named-fc'; -import { ColumnValueBag } from 'common/types/property-bag/column-value-bag'; -import { PropertyBagColumnRendererConfig } from 'common/types/property-bag/property-bag-column-renderer-config'; -import { isEmpty } from 'lodash'; -import * as React from 'react'; - -import { DictionaryStringTo } from 'types/common-types'; -import { Term } from 'assessments/markup'; -import { PropertyBagColumnRenderer } from 'assessments/common/property-bag-column-renderer'; - -export class PropertyBagComponentColumnRenderer< - TPropertyBag extends ColumnValueBag, -> extends PropertyBagColumnRenderer { - private render = ( - config: PropertyBagColumnRendererConfig, - value: any, - index: number, - ) => { - return ( -
- {`${config.displayName}: `} - {this.renderValue(config, value)} -
- ); - }; - - private mapper = (config: PropertyBagColumnRendererConfig, index: number) => { - if (this.item.instance.propertyBag == null) { - return null; - } - const value = this.item.instance.propertyBag[config.propertyName]; - if (value == null && config.defaultValue == null) { - return null; - } - - return this.render(config, value, index); - }; - - protected renderValue = (config: PropertyBagColumnRendererConfig, value: any) => { - if (config.expand) { - return this.renderProperties(config, value); - } - return {() => value}; - // const valueToRender = value || config.defaultValue; - // if (typeof valueToRender === 'string') { - // return {valueToRender}; - // } - - // const ValueComponent = valueToRender; - // console.log(ValueComponent, ); - // return ( - // - // Hello, this is a test - // - // ); - }; - - private renderProperties = ( - config: PropertyBagColumnRendererConfig, - propertyMap: DictionaryStringTo, - ) => { - if (isEmpty(propertyMap)) { - return {config.defaultValue}; - } - - return Object.keys(propertyMap).map(key => { - return ( -
- {this.renderInnerKeyValue(key, propertyMap[key])} -
- ); - }); - }; - - private renderInnerKeyValue = (key: string, value: string) => { - if (value === null) { - return {`${key}`}; - } else { - return ( - - {`${key}: `} - {value} - - ); - } - }; - - public getRenderer = (): JSX.Element => { - return
{this.configs.map(this.mapper)}
; - }; -} diff --git a/src/common/types/property-bag/property-bag-column-renderer-config.ts b/src/common/types/property-bag/property-bag-column-renderer-config.ts index fac2a840832..581e3029cfa 100644 --- a/src/common/types/property-bag/property-bag-column-renderer-config.ts +++ b/src/common/types/property-bag/property-bag-column-renderer-config.ts @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -import { TargetSizePropertyBag } from 'common/types/property-bag/target-size-property-bag'; import { ColumnValue, ColumnValueBag } from './column-value-bag'; -import * as React from 'react'; export const NoValue = '(no value)'; export interface PropertyBagColumnRendererConfig { @@ -10,7 +8,4 @@ export interface PropertyBagColumnRendererConfig typeof React.Component; - render?: (element: JSX.Element) => JSX.Element; } From 7b9329464763bba405838e8e296dbc8a32c5aee6 Mon Sep 17 00:00:00 2001 From: madalynrose Date: Thu, 9 May 2024 16:43:24 -0400 Subject: [PATCH 7/9] remove old file --- src/scanner/target-size-utils.tsx | 129 ------------------------------ 1 file changed, 129 deletions(-) delete mode 100644 src/scanner/target-size-utils.tsx diff --git a/src/scanner/target-size-utils.tsx b/src/scanner/target-size-utils.tsx deleted file mode 100644 index d3f63291822..00000000000 --- a/src/scanner/target-size-utils.tsx +++ /dev/null @@ -1,129 +0,0 @@ -/* - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. - */ -import { DictionaryStringTo } from 'types/common-types'; -import { TargetSizePropertyBag } from 'common/types/property-bag/target-size-property-bag'; -import * as React from 'react'; -import * as Markup from 'assessments/markup'; - -const targetSizeMessageMap: DictionaryStringTo = { - size: { - pass: { - default: props => ( - - Element has sufficient touch target size ({props.height}px by {props.width} - px). - - ), - obscured: props => <>OBSCURED, - large: props => <>LARGE, //4.9 - }, - fail: { - default: props => ( - - Element has insufficient touch target size ( - {props.height}px by {props.width} - px, should be at least {props.minSize}px by {props.minSize}px){' '} - - ), - partiallyObscured: props => ( - - Element has insufficient touch target size ( - {props.height}px by {props.width} - px, should be at least {props.minSize}px by {props.minSize}px) because it is - partially obscured. - - ), - }, - incomplete: { - default: props => ( - - Element has negative tabindex with insufficient touch - target size ({props.height}px by {props.width}px, should be at least{' '} - {props.minSize}px by - {props.minSize}px). This may be OK if the element is not a touch target. - - ), - contentOverflow: props => ( - - Unknown: Element touch target size could not be - accurately determined due to overflow content. - - ), - partiallyObscured: props => ( - - Unknown: Element with negative tabindex has - insufficient touch target size because it is partially obscured. This may be OK - if the element is not a touch target. - - ), - partiallyObscuredNonTabbable: props => ( - - Unknown: Element has insufficient touch target size - because it is partially obscured by a neighbor with negative tabindex. This may - be OK if the neighbor is not a touch target. - - ), - tooManyRects: props => ( - - Unknown: Could not determine element target size - because there are too many overlapping elements. - - ), - }, - }, - offset: { - pass: { - default: props => ( - - Element has sufficient offset from its closest neighbor ({props.closestOffset} - px) - - ), - large: props => <>LARGE, - }, - fail: { - default: props => ( - - Element has insufficient offset to its closest neighbor ({props.closestOffset} - px in diameter, should be at least {props.minOffset}px in diameter) - - ), - }, - incomplete: { - default: props => ( - - Element with negative tabindex has insufficient offset to its closest neighbor. - This may be OK if the element is not a touch target. - - ), - nonTabbableNeighbor: props => ( - - Element has sufficient offset from its closest neighbor and the closest neighbor - has a negative tabindex. This may be OK if the neighbor is not a touch target. - - ), - }, - }, -}; - -const getTargetSizeMessageComponentForRule: ( - ruleType: 'size' | 'offset', - propertyBag: TargetSizePropertyBag, -) => typeof React.Component = (ruleType: 'size' | 'offset', propertyBag: TargetSizePropertyBag) => { - const status = propertyBag[`${ruleType}Status`] as string; - if (status == null) { - return null; - } - const messageKey = (propertyBag[`${ruleType}MessageKey`] as string) || 'default'; - console.log(ruleType, messageKey, status); - return targetSizeMessageMap[ruleType][status][messageKey]; -}; - -export const getTargetSizeColumnComponents: ( - ruleType: 'size' | 'offset', -) => (propertyBag: TargetSizePropertyBag) => typeof React.Component = - (ruleType: 'size' | 'offset') => (propertyBag: TargetSizePropertyBag) => { - return getTargetSizeMessageComponentForRule(ruleType, propertyBag); - }; From 9aecad655b5ee821b8bb19fa2c38c68f6bf15e2a Mon Sep 17 00:00:00 2001 From: madalynrose Date: Thu, 9 May 2024 16:59:49 -0400 Subject: [PATCH 8/9] lint:fix --- .../target-size-column-renderer.tsx | 4 +-- .../pointer-motion/test-steps/target-size.tsx | 27 +++++++++---------- src/assessments/types/requirement.ts | 2 +- .../property-bag/target-size-property-bag.ts | 6 ++--- ...rget-size-column-renderer-factory.test.tsx | 2 +- .../target-size-column-renderer.test.tsx | 2 +- 6 files changed, 20 insertions(+), 23 deletions(-) diff --git a/src/assessments/pointer-motion/target-size-column-renderer.tsx b/src/assessments/pointer-motion/target-size-column-renderer.tsx index 4e8b10a3878..40683a7ebf2 100644 --- a/src/assessments/pointer-motion/target-size-column-renderer.tsx +++ b/src/assessments/pointer-motion/target-size-column-renderer.tsx @@ -1,13 +1,13 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -import { InstanceTableRow } from 'assessments/types/instance-table-data'; import * as Markup from 'assessments/markup'; +import { InstanceTableRow } from 'assessments/types/instance-table-data'; import { ColumnValueBag } from 'common/types/property-bag/column-value-bag'; import { PropertyBagColumnRendererConfig } from 'common/types/property-bag/property-bag-column-renderer-config'; +import { TargetSizePropertyBag } from 'common/types/property-bag/target-size-property-bag'; import * as React from 'react'; import { DictionaryStringTo } from 'types/common-types'; import { PropertyBagColumnRendererFactory } from '../common/property-bag-column-renderer-factory'; -import { TargetSizePropertyBag } from 'common/types/property-bag/target-size-property-bag'; export function targetSizeColumnRenderer( item: InstanceTableRow, diff --git a/src/assessments/pointer-motion/test-steps/target-size.tsx b/src/assessments/pointer-motion/test-steps/target-size.tsx index d0dcf03ff2c..45f598f6ef9 100644 --- a/src/assessments/pointer-motion/test-steps/target-size.tsx +++ b/src/assessments/pointer-motion/test-steps/target-size.tsx @@ -1,28 +1,28 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. +import { AnalyzerConfigurationFactory } from 'assessments/common/analyzer-configuration-factory'; +import { + getTargetOffsetMessageComponentFromPropertyBag, + getTargetSizeMessageComponentFromPropertyBag, +} from 'assessments/pointer-motion/target-size-column-renderer'; +import { TargetSizeColumnRendererFactory } from 'assessments/pointer-motion/target-size-column-renderer-factory'; +import { ReportInstanceField } from 'assessments/types/report-instance-field'; +import { ChecksType } from 'background/assessment-data-converter'; +import { TargetSizePropertyBag } from 'common/types/property-bag/target-size-property-bag'; +import { DecoratedAxeNodeResult } from 'common/types/store-data/visualization-scan-result-data'; import { link } from 'content/link'; import * as content from 'content/test/pointer-motion/target-size'; +import { AssessmentVisualizationEnabledToggle } from 'DetailsView/components/assessment-visualization-enabled-toggle'; +import { AnalyzerConfiguration } from 'injected/analyzers/analyzer'; +import { AnalyzerProvider } from 'injected/analyzers/analyzer-provider'; import * as React from 'react'; import { PropertyBagColumnRendererConfig } from '../../../common/types/property-bag/property-bag-column-renderer-config'; import { ManualTestRecordYourResults } from '../../common/manual-test-record-your-results'; import * as Markup from '../../markup'; import { Requirement } from '../../types/requirement'; import { PointerMotionTestStep } from './test-steps'; -import { TargetSizePropertyBag } from 'common/types/property-bag/target-size-property-bag'; -import { ReportInstanceField } from 'assessments/types/report-instance-field'; -import { AnalyzerProvider } from 'injected/analyzers/analyzer-provider'; -import { AnalyzerConfigurationFactory } from 'assessments/common/analyzer-configuration-factory'; -import { AnalyzerConfiguration } from 'injected/analyzers/analyzer'; import { ScannerUtils } from 'injected/scanner-utils'; -import { AssessmentVisualizationEnabledToggle } from 'DetailsView/components/assessment-visualization-enabled-toggle'; -import { DecoratedAxeNodeResult } from 'common/types/store-data/visualization-scan-result-data'; import { isEmpty } from 'lodash'; -import { ChecksType } from 'background/assessment-data-converter'; -import { TargetSizeColumnRendererFactory } from 'assessments/pointer-motion/target-size-column-renderer-factory'; -import { - getTargetOffsetMessageComponentFromPropertyBag, - getTargetSizeMessageComponentFromPropertyBag, -} from 'assessments/pointer-motion/target-size-column-renderer'; const description: JSX.Element = ( @@ -123,7 +123,6 @@ const generateTargetSizePropertyBagFrom = ( }; }), ); - console.log(data); return data; } return null; diff --git a/src/assessments/types/requirement.ts b/src/assessments/types/requirement.ts index 61f3b5c1a1b..304e71cc882 100644 --- a/src/assessments/types/requirement.ts +++ b/src/assessments/types/requirement.ts @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. import { IColumn } from '@fluentui/react'; +import { ChecksType } from 'background/assessment-data-converter'; import { UniquelyIdentifiableInstances } from 'background/instance-identifier-generator'; import { HyperlinkDefinition } from 'common/types/hyperlink-definition'; import { @@ -23,7 +24,6 @@ import { ContentPageComponent } from 'views/content/content-page'; import { IGetMessageGenerator } from '../assessment-default-message-generator'; import { InstanceTableColumn, InstanceTableHeaderType } from './instance-table-data'; import { ReportInstanceFields } from './report-instance-field'; -import { ChecksType } from 'background/assessment-data-converter'; export interface Requirement { key: string; diff --git a/src/common/types/property-bag/target-size-property-bag.ts b/src/common/types/property-bag/target-size-property-bag.ts index 82a1386616b..36be2fc4f08 100644 --- a/src/common/types/property-bag/target-size-property-bag.ts +++ b/src/common/types/property-bag/target-size-property-bag.ts @@ -1,7 +1,5 @@ -/* - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. - */ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. import { ColumnValueBag } from './column-value-bag'; export interface TargetSizePropertyBag extends ColumnValueBag { diff --git a/src/tests/unit/tests/assessments/pointer-motion/target-size-column-renderer-factory.test.tsx b/src/tests/unit/tests/assessments/pointer-motion/target-size-column-renderer-factory.test.tsx index 08f19d50693..aec2f07c7c3 100644 --- a/src/tests/unit/tests/assessments/pointer-motion/target-size-column-renderer-factory.test.tsx +++ b/src/tests/unit/tests/assessments/pointer-motion/target-size-column-renderer-factory.test.tsx @@ -1,13 +1,13 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. import { render } from '@testing-library/react'; +import { TargetSizeColumnRendererFactory } from 'assessments/pointer-motion/target-size-column-renderer-factory'; import { InstanceTableRow } from 'assessments/types/instance-table-data'; import { PropertyBagColumnRendererConfig } from 'common/types/property-bag/property-bag-column-renderer-config'; import * as React from 'react'; import { ColumnValueBag } from '../../../../../common/types/property-bag/column-value-bag'; import { RendererWrapper } from '../common/renderer-wrapper'; -import { TargetSizeColumnRendererFactory } from 'assessments/pointer-motion/target-size-column-renderer-factory'; describe('TargetSizeColumnRendererFactory', () => { let configs: PropertyBagColumnRendererConfig[]; diff --git a/src/tests/unit/tests/assessments/pointer-motion/target-size-column-renderer.test.tsx b/src/tests/unit/tests/assessments/pointer-motion/target-size-column-renderer.test.tsx index 41361973ca7..2b6455454b3 100644 --- a/src/tests/unit/tests/assessments/pointer-motion/target-size-column-renderer.test.tsx +++ b/src/tests/unit/tests/assessments/pointer-motion/target-size-column-renderer.test.tsx @@ -1,13 +1,13 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. import { render } from '@testing-library/react'; +import { targetSizeColumnRenderer } from 'assessments/pointer-motion/target-size-column-renderer'; import { InstanceTableRow } from 'assessments/types/instance-table-data'; import { PropertyBagColumnRendererConfig } from 'common/types/property-bag/property-bag-column-renderer-config'; import * as React from 'react'; import { ColumnValueBag } from '../../../../../common/types/property-bag/column-value-bag'; import { RendererWrapper } from '../common/renderer-wrapper'; -import { targetSizeColumnRenderer } from 'assessments/pointer-motion/target-size-column-renderer'; describe('TargetSizeColumnRenderer', () => { let configs: PropertyBagColumnRendererConfig[]; From 16151f228b0ee186aa55b427db00100298845ba7 Mon Sep 17 00:00:00 2001 From: madalynrose Date: Thu, 9 May 2024 17:01:20 -0400 Subject: [PATCH 9/9] lint:fix again --- src/assessments/pointer-motion/test-steps/target-size.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/assessments/pointer-motion/test-steps/target-size.tsx b/src/assessments/pointer-motion/test-steps/target-size.tsx index 45f598f6ef9..883b4716d71 100644 --- a/src/assessments/pointer-motion/test-steps/target-size.tsx +++ b/src/assessments/pointer-motion/test-steps/target-size.tsx @@ -15,14 +15,14 @@ import * as content from 'content/test/pointer-motion/target-size'; import { AssessmentVisualizationEnabledToggle } from 'DetailsView/components/assessment-visualization-enabled-toggle'; import { AnalyzerConfiguration } from 'injected/analyzers/analyzer'; import { AnalyzerProvider } from 'injected/analyzers/analyzer-provider'; +import { ScannerUtils } from 'injected/scanner-utils'; +import { isEmpty } from 'lodash'; import * as React from 'react'; import { PropertyBagColumnRendererConfig } from '../../../common/types/property-bag/property-bag-column-renderer-config'; import { ManualTestRecordYourResults } from '../../common/manual-test-record-your-results'; import * as Markup from '../../markup'; import { Requirement } from '../../types/requirement'; import { PointerMotionTestStep } from './test-steps'; -import { ScannerUtils } from 'injected/scanner-utils'; -import { isEmpty } from 'lodash'; const description: JSX.Element = (