From b65533c1dc439ff6f45223e153b7ae90fb8e73c8 Mon Sep 17 00:00:00 2001 From: Eric Olkowski Date: Wed, 15 Feb 2023 15:17:42 -0500 Subject: [PATCH 1/7] fix(lists): added role to list elements within components --- .../src/components/AlertGroup/AlertGroup.tsx | 14 ++++++++- .../AlertGroup/AlertGroupInline.tsx | 1 + .../src/components/Breadcrumb/Breadcrumb.tsx | 2 +- .../src/components/DataList/DataList.tsx | 1 + .../Divider/examples/DividerUsingLi.tsx | 2 +- .../src/components/HelperText/HelperText.tsx | 4 +++ .../src/components/JumpLinks/JumpLinks.tsx | 11 +++---- .../components/JumpLinks/JumpLinksList.tsx | 2 +- .../react-core/src/components/List/List.tsx | 10 ++++++- .../components/LoginPage/LoginMainFooter.tsx | 11 +++++-- .../src/components/LoginPage/LoginPage.tsx | 4 +++ .../LoginPage/examples/LoginPageBasic.tsx | 1 + .../examples/LoginPageLanguageSelect.tsx | 1 + .../examples/LoginPageShowHidePassword.tsx | 1 + .../MultipleFileUploadStatus.tsx | 7 ++++- .../examples/MultipleFileUploadBasic.tsx | 29 ++++++++++--------- .../react-core/src/components/Nav/NavList.tsx | 1 + .../NotificationDrawerList.tsx | 11 ++++++- .../examples/NotificationDrawerBasic.tsx | 2 +- .../examples/NotificationDrawerGroups.tsx | 6 ++-- .../NotificationDrawerLightweight.tsx | 15 ++++++++-- .../ProgressStepper/ProgressStepper.tsx | 5 ++++ .../examples/ProgressStepperBasic.tsx | 2 +- .../examples/ProgressStepperBasicFailure.tsx | 2 +- .../examples/ProgressStepperBasicIssue.tsx | 2 +- .../ProgressStepperBasicWithAlignment.tsx | 6 +++- .../ProgressStepperBasicWithDescription.tsx | 2 +- .../examples/ProgressStepperCompact.tsx | 2 +- .../examples/ProgressStepperCustomIcons.tsx | 2 +- .../examples/ProgressStepperHelpPopover.tsx | 2 +- .../src/components/SimpleList/SimpleList.tsx | 6 +++- .../components/SimpleList/SimpleListGroup.tsx | 2 +- .../src/components/Wizard/WizardNav.tsx | 4 ++- .../src/next/components/Wizard/WizardNav.tsx | 6 +++- 34 files changed, 130 insertions(+), 49 deletions(-) diff --git a/packages/react-core/src/components/AlertGroup/AlertGroup.tsx b/packages/react-core/src/components/AlertGroup/AlertGroup.tsx index 0ce85babc71..1e42fe6f549 100644 --- a/packages/react-core/src/components/AlertGroup/AlertGroup.tsx +++ b/packages/react-core/src/components/AlertGroup/AlertGroup.tsx @@ -18,6 +18,8 @@ export interface AlertGroupProps extends Omit, onOverflowClick?: () => void; /** Custom text to show for the overflow message */ overflowMessage?: string; + /** Adds an accessible label to the alert group. */ + 'aria-label'?: string; } interface AlertGroupState { @@ -53,7 +55,16 @@ export class AlertGroup extends React.Component {children} diff --git a/packages/react-core/src/components/AlertGroup/AlertGroupInline.tsx b/packages/react-core/src/components/AlertGroup/AlertGroupInline.tsx index 8a828cf416f..f47dbaf8f98 100644 --- a/packages/react-core/src/components/AlertGroup/AlertGroupInline.tsx +++ b/packages/react-core/src/components/AlertGroup/AlertGroupInline.tsx @@ -14,6 +14,7 @@ export const AlertGroupInline: React.FunctionComponent = ({ ...rest }: AlertGroupProps) => (
    = ({ const ouiaProps = useOUIAProps(Breadcrumb.displayName, ouiaId, ouiaSafe); return ( ); diff --git a/packages/react-core/src/components/JumpLinks/JumpLinksList.tsx b/packages/react-core/src/components/JumpLinks/JumpLinksList.tsx index ab101ccf55c..68f6342af4c 100644 --- a/packages/react-core/src/components/JumpLinks/JumpLinksList.tsx +++ b/packages/react-core/src/components/JumpLinks/JumpLinksList.tsx @@ -14,7 +14,7 @@ export const JumpLinksList: React.FunctionComponent = ({ className, ...props }: JumpLinksListProps) => ( -
      +
        {children}
      ); diff --git a/packages/react-core/src/components/List/List.tsx b/packages/react-core/src/components/List/List.tsx index ed72be36ebc..8518c75de27 100644 --- a/packages/react-core/src/components/List/List.tsx +++ b/packages/react-core/src/components/List/List.tsx @@ -32,9 +32,12 @@ export interface ListProps extends Omit = ({ @@ -47,12 +50,15 @@ export const List: React.FunctionComponent = ({ type = OrderType.number, ref = null, component = ListComponent.ul, + 'aria-label': ariaLabel, ...props }: ListProps) => component === ListComponent.ol ? (
        } type={type} + {...(isPlain && { role: 'list' })} + aria-label={ariaLabel} {...props} className={css( styles.list, @@ -68,6 +74,8 @@ export const List: React.FunctionComponent = ({ ) : (
          } + {...(isPlain && { role: 'list' })} + {...(ariaLabel && { 'aria-label': ariaLabel })} {...props} className={css( styles.list, diff --git a/packages/react-core/src/components/LoginPage/LoginMainFooter.tsx b/packages/react-core/src/components/LoginPage/LoginMainFooter.tsx index c5583f224a4..3768b4a77d3 100644 --- a/packages/react-core/src/components/LoginPage/LoginMainFooter.tsx +++ b/packages/react-core/src/components/LoginPage/LoginMainFooter.tsx @@ -7,8 +7,10 @@ export interface LoginMainFooterProps extends React.HTMLProps { className?: string; /** Content rendered inside the login main footer */ children?: React.ReactNode; - /** Content rendered inside the login main footer as social media links* */ + /** Content rendered inside the login main footer as social media links */ socialMediaLoginContent?: React.ReactNode; + /** Adds an accessible name to the social media login list. */ + socialMediaLoginAriaLabel?: string; /** Content rendered inside of login main footer band to display a sign up for account message */ signUpForAccountMessage?: React.ReactNode; /** Content rendered inside of login main footer band do display a forgot credentials link* */ @@ -21,11 +23,16 @@ export const LoginMainFooter: React.FunctionComponent = ({ signUpForAccountMessage = null, forgotCredentials = null, className = '', + socialMediaLoginAriaLabel, ...props }: LoginMainFooterProps) => (
          {children} - {socialMediaLoginContent &&
            {socialMediaLoginContent}
          } + {socialMediaLoginContent && ( +
            + {socialMediaLoginContent} +
          + )} {(signUpForAccountMessage || forgotCredentials) && (
          {signUpForAccountMessage} diff --git a/packages/react-core/src/components/LoginPage/LoginPage.tsx b/packages/react-core/src/components/LoginPage/LoginPage.tsx index 2e6d89d0447..0c2afd2e1bf 100644 --- a/packages/react-core/src/components/LoginPage/LoginPage.tsx +++ b/packages/react-core/src/components/LoginPage/LoginPage.tsx @@ -43,6 +43,8 @@ export interface LoginPageProps extends React.HTMLProps { forgotCredentials?: React.ReactNode; /** Content rendered inside of social media login footer section */ socialMediaLoginContent?: React.ReactNode; + /** Adds an accessible name to the social media login list. */ + socialMediaLoginAriaLabel?: string; } export const LoginPage: React.FunctionComponent = ({ @@ -61,6 +63,7 @@ export const LoginPage: React.FunctionComponent = ({ signUpForAccountMessage = null, forgotCredentials = null, socialMediaLoginContent = null, + socialMediaLoginAriaLabel, ...props }: LoginPageProps) => { const HeaderBrand = ( @@ -85,6 +88,7 @@ export const LoginPage: React.FunctionComponent = ({ {(socialMediaLoginContent || forgotCredentials || signUpForAccountMessage) && ( diff --git a/packages/react-core/src/components/LoginPage/examples/LoginPageBasic.tsx b/packages/react-core/src/components/LoginPage/examples/LoginPageBasic.tsx index e3a809a2ca3..e9b97b1718f 100644 --- a/packages/react-core/src/components/LoginPage/examples/LoginPageBasic.tsx +++ b/packages/react-core/src/components/LoginPage/examples/LoginPageBasic.tsx @@ -134,6 +134,7 @@ export const SimpleLoginPage: React.FunctionComponent = () => { loginTitle="Log in to your account" loginSubtitle="Enter your single sign-on LDAP credentials." socialMediaLoginContent={socialMediaLoginContent} + socialMediaLoginAriaLabel="Log in with social media" signUpForAccountMessage={signUpForAccountMessage} forgotCredentials={forgotCredentials} > diff --git a/packages/react-core/src/components/LoginPage/examples/LoginPageLanguageSelect.tsx b/packages/react-core/src/components/LoginPage/examples/LoginPageLanguageSelect.tsx index a47f378ad55..849b6880ef3 100644 --- a/packages/react-core/src/components/LoginPage/examples/LoginPageLanguageSelect.tsx +++ b/packages/react-core/src/components/LoginPage/examples/LoginPageLanguageSelect.tsx @@ -185,6 +185,7 @@ export const LoginPageLanguageSelect: React.FunctionComponent = () => { loginSubtitle="Enter your single sign-on LDAP credentials." headerUtilities={headerUtils} socialMediaLoginContent={socialMediaLoginContent} + socialMediaLoginAriaLabel="Log in with social media" signUpForAccountMessage={signUpForAccountMessage} forgotCredentials={forgotCredentials} > diff --git a/packages/react-core/src/components/LoginPage/examples/LoginPageShowHidePassword.tsx b/packages/react-core/src/components/LoginPage/examples/LoginPageShowHidePassword.tsx index b20dec0be4f..91c408f3efa 100644 --- a/packages/react-core/src/components/LoginPage/examples/LoginPageShowHidePassword.tsx +++ b/packages/react-core/src/components/LoginPage/examples/LoginPageShowHidePassword.tsx @@ -135,6 +135,7 @@ export const LoginPageHideShowPassword: React.FunctionComponent = () => { loginTitle="Log in to your account" loginSubtitle="Enter your single sign-on LDAP credentials." socialMediaLoginContent={socialMediaLoginContent} + socialMediaLoginAriaLabel="Log in with social media" signUpForAccountMessage={signUpForAccountMessage} forgotCredentials={forgotCredentials} > diff --git a/packages/react-core/src/components/MultipleFileUpload/MultipleFileUploadStatus.tsx b/packages/react-core/src/components/MultipleFileUpload/MultipleFileUploadStatus.tsx index 36056ae4c1b..587d167c50f 100644 --- a/packages/react-core/src/components/MultipleFileUpload/MultipleFileUploadStatus.tsx +++ b/packages/react-core/src/components/MultipleFileUpload/MultipleFileUploadStatus.tsx @@ -21,6 +21,8 @@ export interface MultipleFileUploadStatusProps extends React.HTMLProps = ({ @@ -28,6 +30,7 @@ export const MultipleFileUploadStatus: React.FunctionComponent { const [icon, setIcon] = React.useState(); @@ -63,7 +66,9 @@ export const MultipleFileUploadStatus: React.FunctionComponent -
            {children}
          +
            + {children} +
          ); diff --git a/packages/react-core/src/components/MultipleFileUpload/examples/MultipleFileUploadBasic.tsx b/packages/react-core/src/components/MultipleFileUpload/examples/MultipleFileUploadBasic.tsx index 689e067a6fa..c05b8992ef7 100644 --- a/packages/react-core/src/components/MultipleFileUpload/examples/MultipleFileUploadBasic.tsx +++ b/packages/react-core/src/components/MultipleFileUpload/examples/MultipleFileUploadBasic.tsx @@ -34,7 +34,7 @@ export const MultipleFileUploadBasic: React.FunctionComponent = () => { React.useEffect(() => { if (readFileData.length < currentFiles.length) { setStatusIcon('inProgress'); - } else if (readFileData.every(file => file.loadResult === 'success')) { + } else if (readFileData.every((file) => file.loadResult === 'success')) { setStatusIcon('success'); } else { setStatusIcon('danger'); @@ -44,13 +44,13 @@ export const MultipleFileUploadBasic: React.FunctionComponent = () => { // remove files from both state arrays based on their name const removeFiles = (namesOfFilesToRemove: string[]) => { const newCurrentFiles = currentFiles.filter( - currentFile => !namesOfFilesToRemove.some(fileName => fileName === currentFile.name) + (currentFile) => !namesOfFilesToRemove.some((fileName) => fileName === currentFile.name) ); setCurrentFiles(newCurrentFiles); const newReadFiles = readFileData.filter( - readFile => !namesOfFilesToRemove.some(fileName => fileName === readFile.fileName) + (readFile) => !namesOfFilesToRemove.some((fileName) => fileName === readFile.fileName) ); setReadFileData(newReadFiles); @@ -60,34 +60,34 @@ export const MultipleFileUploadBasic: React.FunctionComponent = () => { * only used in this example for demonstration purposes */ const updateCurrentFiles = (files: File[]) => { if (fileUploadShouldFail) { - const corruptedFiles = files.map(file => ({ ...file, lastModified: ('foo' as unknown) as number })); - setCurrentFiles(prevFiles => [...prevFiles, ...corruptedFiles]); + const corruptedFiles = files.map((file) => ({ ...file, lastModified: 'foo' as unknown as number })); + setCurrentFiles((prevFiles) => [...prevFiles, ...corruptedFiles]); } else { - setCurrentFiles(prevFiles => [...prevFiles, ...files]); + setCurrentFiles((prevFiles) => [...prevFiles, ...files]); } }; // callback that will be called by the react dropzone with the newly dropped file objects const handleFileDrop = (droppedFiles: File[]) => { // identify what, if any, files are re-uploads of already uploaded files - const currentFileNames = currentFiles.map(file => file.name); - const reUploads = droppedFiles.filter(droppedFile => currentFileNames.includes(droppedFile.name)); + const currentFileNames = currentFiles.map((file) => file.name); + const reUploads = droppedFiles.filter((droppedFile) => currentFileNames.includes(droppedFile.name)); /** this promise chain is needed because if the file removal is done at the same time as the file adding react * won't realize that the status items for the re-uploaded files needs to be re-rendered */ Promise.resolve() - .then(() => removeFiles(reUploads.map(file => file.name))) + .then(() => removeFiles(reUploads.map((file) => file.name))) .then(() => updateCurrentFiles(droppedFiles)); }; // callback called by the status item when a file is successfully read with the built-in file reader const handleReadSuccess = (data: string, file: File) => { - setReadFileData(prevReadFiles => [...prevReadFiles, { data, fileName: file.name, loadResult: 'success' }]); + setReadFileData((prevReadFiles) => [...prevReadFiles, { data, fileName: file.name, loadResult: 'success' }]); }; // callback called by the status item when a file encounters an error while being read with the built-in file reader const handleReadFail = (error: DOMException, file: File) => { - setReadFileData(prevReadFiles => [ + setReadFileData((prevReadFiles) => [ ...prevReadFiles, { loadError: error, fileName: file.name, loadResult: 'danger' } ]); @@ -95,7 +95,7 @@ export const MultipleFileUploadBasic: React.FunctionComponent = () => { // add helper text to a status item showing any error encountered during the file reading process const createHelperText = (file: File) => { - const fileResult = readFileData.find(readFile => readFile.fileName === file.name); + const fileResult = readFileData.find((readFile) => readFile.fileName === file.name); if (fileResult?.loadError) { return ( @@ -105,7 +105,7 @@ export const MultipleFileUploadBasic: React.FunctionComponent = () => { } }; - const successfullyReadFileCount = readFileData.filter(fileData => fileData.loadResult === 'success').length; + const successfullyReadFileCount = readFileData.filter((fileData) => fileData.loadResult === 'success').length; return ( <> @@ -131,8 +131,9 @@ export const MultipleFileUploadBasic: React.FunctionComponent = () => { - {currentFiles.map(file => ( + {currentFiles.map((file) => ( { ref={this.navList} className={css(styles.navList, className)} onScroll={this.handleScrollButtons} + role="list" {...props} > {children} diff --git a/packages/react-core/src/components/NotificationDrawer/NotificationDrawerList.tsx b/packages/react-core/src/components/NotificationDrawer/NotificationDrawerList.tsx index 7e35f8a5ad1..ee1deb764e1 100644 --- a/packages/react-core/src/components/NotificationDrawer/NotificationDrawerList.tsx +++ b/packages/react-core/src/components/NotificationDrawer/NotificationDrawerList.tsx @@ -9,15 +9,24 @@ export interface NotificationDrawerListProps extends React.HTMLProps = ({ children, className = '', isHidden = false, + 'aria-label': ariaLabel, ...props }: NotificationDrawerListProps) => ( -