Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ describe('Access Control Access scopes', () => {
cy.get('th:contains("Origin")');
cy.get('th:contains("Description")');
cy.get('th:contains("Roles")');
cy.get('th[aria-label="Row actions"]');
cy.get(`th:has('span.pf-v5-screen-reader:contains("Row actions")')`);
});

it('list has default names', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ describe('Access Control Auth providers', () => {
cy.get('th:contains("Type")');
cy.get('th:contains("Minimum access role")');
cy.get('th:contains("Assigned rules")');
cy.get('th[aria-label="Row actions"]');
cy.get(`th:has('span.pf-v5-screen-reader:contains("Row actions")')`);
});

it('add Auth0', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ describe('Access Control Permission sets', () => {
cy.get('th:contains("Origin")');
cy.get('th:contains("Description")');
cy.get('th:contains("Roles")');
cy.get('th[aria-label="Row actions"]');
cy.get(`th:has('span.pf-v5-screen-reader:contains("Row actions")')`);
});

it('list has default names', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ describe('Access Control Roles', () => {
cy.get('th:contains("Description")');
cy.get('th:contains("Permission set")');
cy.get('th:contains("Access scope")');
cy.get('th[aria-label="Row actions"]');
cy.get(`th:has('span.pf-v5-screen-reader:contains("Row actions")')`);
});

it('list has default names', () => {
Expand Down
43 changes: 0 additions & 43 deletions ui/apps/platform/eslint-plugins/accessibilityPlugin.js

This file was deleted.

172 changes: 172 additions & 0 deletions ui/apps/platform/eslint-plugins/pluginAccessibility.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
/* globals module */

const rules = {
// ESLint naming convention for positive rules:
// If your rule is enforcing the inclusion of something, use a short name without a special prefix.

'Alert-component-prop': {
// Require alternative markup to prevent axe DevTools issue:
// Heading levels should only increase by one
// https://dequeuniversity.com/rules/axe/4.9/heading-order
meta: {
type: 'problem',
docs: {
description: 'Require that Alert element has component="p" prop',
},
schema: [],
},
create(context) {
return {
JSXOpeningElement(node) {
if (node.name?.name === 'Alert') {
if (
!node.attributes.some((nodeAttribute) => {
return (
nodeAttribute.name?.name === 'component' &&
nodeAttribute.value?.value === 'p'
);
})
) {
context.report({
node,
message: 'Alert element requires component="p" prop',
});
}
}
},
};
},
},
'Th-screenReaderText-prop': {
// Require prop to prevent axe DevTools issue:
// Table header text should not be empty
// https://dequeuniversity.com/rules/axe/4.9/empty-table-header

// Until upgrade to PatternFly 5.3 which has screenReaderText prop,
// temporary solution is to render child:
// <span className="pf-v5-screen-reader">{screenReaderText}</span>
meta: {
type: 'problem',
docs: {
description:
'Require that empty Th element has either expand, select, or screenReaderText prop',
},
schema: [],
},
create(context) {
return {
JSXElement(node) {
if (node.openingElement?.name?.name === 'Th') {
if (node.children?.length === 0) {
if (
!node.openingElement?.attributes?.some((nodeAttribute) => {
return ['expand', 'select', 'screenReaderText'].includes(
nodeAttribute.name?.name
);
})
) {
context.report({
node,
message:
'Require that empty Th element has either expand, select, or screenReaderText prop',
});
}
}
}
},
};
},
},

// ESLint naming convention for negative rules.
// If your rule only disallows something, prefix it with no.
// However, we can write forbid instead of disallow as the verb in description and message.

'no-Td-in-Thead': {
// Forbid work-around to prevent axe DevTools issue:
// Table header text should not be empty
// https://dequeuniversity.com/rules/axe/4.9/empty-table-header
meta: {
type: 'problem',
docs: {
description: 'Forbid Td as alternative to Th in Thead element',
},
schema: [],
},
create(context) {
return {
JSXElement(node) {
if (node.openingElement?.name?.name === 'Td') {
const ancestors = context.sourceCode.getAncestors(node);
if (
ancestors.length >= 2 &&
ancestors[ancestors.length - 1].openingElement?.name?.name === 'Tr' &&
ancestors[ancestors.length - 2].openingElement?.name?.name === 'Thead'
) {
context.report({
node,
message: 'Forbid Td as alternative to Th in Thead element',
});
}
}
},
};
},
},
'no-Th-aria-label-prop': {
// Forbid work-around to prevent axe DevTools issue:
// Table header text should not be empty
// https://dequeuniversity.com/rules/axe/4.9/empty-table-header

// Until upgrade to PatternFly 5.3 which has screenReaderText prop,
// temporary solution is to render child:
// <span className="pf-v5-screen-reader">{screenReaderText}</span>
meta: {
type: 'problem',
docs: {
description:
'Forbid aria-label as alternative to screenReaderText prop in Th element',
},
schema: [],
},
create(context) {
return {
JSXOpeningElement(node) {
if (node.name?.name === 'Th') {
if (
node.attributes.some((nodeAttribute) => {
return nodeAttribute.name?.name === 'aria-label';
})
) {
context.report({
node,
message:
'Forbid aria-label as alternative to screenReaderText prop in Th element',
});
}
}
},
};
},
},
};

const pluginKey = 'accessibility'; // key of pluginAccessibility in eslint.config.js file

const pluginAccessibility = {
meta: {
name: 'pluginAccessibility',
version: '0.0.1',
},
rules,
// ...pluginAccessibility.configs.recommended.rules means all rules in eslint.config.js file.
configs: {
recommended: {
rules: Object.fromEntries(
Object.keys(rules).map((ruleKey) => [`${pluginKey}/${ruleKey}`, 'error'])
),
},
},
};

module.exports = pluginAccessibility;
13 changes: 6 additions & 7 deletions ui/apps/platform/eslint.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ const path = require('node:path');

const parserTypeScriptESLint = require('@typescript-eslint/parser');

const pluginAccessibility = require('eslint-plugin-jsx-a11y');
const pluginAccessibilityJSX = require('eslint-plugin-jsx-a11y');
const pluginCypress = require('eslint-plugin-cypress');
const pluginESLint = require('@eslint/js'); // eslint-disable-line import/no-extraneous-dependencies
const pluginESLintComments = require('eslint-plugin-eslint-comments');
Expand All @@ -19,7 +19,7 @@ const pluginTypeScriptESLint = require('@typescript-eslint/eslint-plugin');

const { browser: browserGlobals, jest: jestGlobals, node: nodeGlobals } = require('globals');

const accessibilityPlugin = require('./eslint-plugins/accessibilityPlugin');
const pluginAccessibility = require('./eslint-plugins/pluginAccessibility');

const parserAndOptions = {
parser: parserTypeScriptESLint,
Expand All @@ -38,7 +38,6 @@ module.exports = [
'coverage/**',
'react-app-rewired/**',
'scripts/**',
'eslint-plugins/**',
'src/setupProxy.js',
'src/setupTests.js',
'cypress.d.ts',
Expand Down Expand Up @@ -532,14 +531,14 @@ module.exports = [

// Key of plugin is namespace of its rules.
plugins: {
accessibility: pluginAccessibility,
import: pluginImport,
'jsx-a11y': pluginAccessibility,
'jsx-a11y': pluginAccessibilityJSX,
react: pluginReact,
'react-hooks': pluginReactHooks,
accessibilityPlugin,
},
rules: {
'accessibilityPlugin/require-Alert-component': 'error',
...pluginAccessibility.configs.recommended.rules,

'no-restricted-imports': [
'error',
Expand Down Expand Up @@ -701,7 +700,7 @@ module.exports = [
},
},
{
files: ['*.js', 'eslint-plugins/*.js', 'tailwind-plugins/*.js'], // non-product files
files: ['*.js', 'tailwind-plugins/*.js'], // non-product files

languageOptions: {
...parserAndOptions,
Expand Down
5 changes: 3 additions & 2 deletions ui/apps/platform/src/Components/ExpandRowTh.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,13 @@ export default function ExpandRowTh(props: ThProps) {
return (
<Th
{...props}
aria-label={props['aria-label'] || 'Expand row'}
style={{
// Setting a defined width here prevents column shift when the table is in a loading state
width: `calc(${expandButtonWidth} + ${expandButtonPaddingX} + ${firstTableCellPadding})`,
...props.style,
}}
/>
>
<span className="pf-v5-screen-reader">Row expansion</span>
</Th>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,9 @@ function AccessScopesList({
<Th width={15}>Origin</Th>
<Th width={25}>Description</Th>
<Th width={35}>Roles</Th>
<Th width={10} aria-label="Row actions" />
<Th width={10}>
<span className="pf-v5-screen-reader">Row actions</span>
</Th>
</Tr>
</Thead>
<Tbody>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import {
Tooltip,
} from '@patternfly/react-core';
import { OutlinedQuestionCircleIcon, PlusCircleIcon } from '@patternfly/react-icons';
import { Table, Tbody, Td, Th, Thead, Tr } from '@patternfly/react-table';
import { Table, Tbody, Th, Thead, Tr } from '@patternfly/react-table';

import { LabelSelectorRequirement, LabelSelectorsKey } from 'services/AccessScopesService';

Expand Down Expand Up @@ -203,7 +203,9 @@ function LabelSelectorCard({
<Thead>
<Tr>
<Th width={40}>Key</Th>
<Td />
<Th>
<span className="pf-v5-screen-reader">Operator</span>
</Th>
<Th width={40}>Values</Th>
{isLabelSelectorActive && <Th modifier="fitContent">Action</Th>}
</Tr>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,9 @@ function AuthProvidersList({ entityId, authProviders }: AuthProvidersListProps):
<Th width={15}>Type</Th>
<Th width={20}>Minimum access role</Th>
<Th width={25}>Assigned rules</Th>
<Th width={10} aria-label="Row actions" />
<Th width={10}>
<span className="pf-v5-screen-reader">Row actions</span>
</Th>
</Tr>
</Thead>
<Tbody>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,9 @@ function PermissionSetsList({
<Th width={15}>Origin</Th>
<Th width={25}>Description</Th>
<Th width={35}>Roles</Th>
<Th width={10} aria-label="Row actions" />
<Th width={10}>
<span className="pf-v5-screen-reader">Row actions</span>
</Th>
</Tr>
</Thead>
<Tbody>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,9 @@ function AccessScopesTable({
<Table variant="compact" isStickyHeader>
<Thead>
<Tr>
<Td />
<Th>
<span className="pf-v5-screen-reader">Row selection</span>
</Th>
<Th width={20}>Name</Th>
<Th>Description</Th>
</Tr>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,9 @@ function PermissionSetsTable({
<Table variant="compact" isStickyHeader>
<Thead>
<Tr>
<Th key="radio" />
<Th>
<span className="pf-v5-screen-reader">Row selection</span>
</Th>
<Th width={20}>Name</Th>
<Th>Description</Th>
</Tr>
Expand Down
Loading