Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Improve AI assistant accessibility #248

Merged
merged 7 commits into from
Jun 17, 2024
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
4 changes: 3 additions & 1 deletion src/components/assistant-thinking.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { __ } from '@wordpress/i18n';

export function MessageThinking() {
return (
<div className="flex justify-center items-center gap-1 p-0.5">
<div aria-label={ __( 'Thinking…' ) } className="flex justify-center items-center gap-1 p-0.5">
<div
className="animate-pulse h-1.5 w-1.5 bg-a8c-blueberry rounded-full"
style={ { animationDelay: '0.2s' } }
Expand Down
17 changes: 13 additions & 4 deletions src/components/content-tab-assistant.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ interface ContentTabAssistantProps {
}

interface MessageProps {
id: string;
children: React.ReactNode;
isUser: boolean;
className?: string;
Expand Down Expand Up @@ -85,7 +86,7 @@ const ActionButton = ( {
);
};

export const Message = ( { children, isUser, className }: MessageProps ) => {
export const Message = ( { children, id, isUser, className }: MessageProps ) => {
const [ cliOutput, setCliOutput ] = useState< string | null >( null );
const [ cliStatus, setCliStatus ] = useState< 'success' | 'error' | null >( null );
const [ cliTime, setCliTime ] = useState< string | null >( null );
Expand Down Expand Up @@ -158,11 +159,19 @@ export const Message = ( { children, isUser, className }: MessageProps ) => {
) }
>
<div
id={ id }
role="group"
aria-labelledby={ id }
className={ cx(
'inline-block p-3 rounded border border-gray-300 lg:max-w-[70%] select-text',
! isUser ? 'bg-white' : 'bg-white/45'
) }
>
<div className="relative">
<span className="sr-only">
Comment on lines +170 to +171
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wrapping an element leveraging Tailwind's sr-only class is necessary to avoid unexpected overflow scrolling for the top level document.

{ isUser ? __( 'Your message' ) : __( 'Studio Assistant' ) },
fluiddot marked this conversation as resolved.
Show resolved Hide resolved
</span>
</div>
{ typeof children === 'string' ? (
<div className="assistant-markdown">
<Markdown components={ { code: CodeBlock } }>{ children }</Markdown>
Expand All @@ -185,12 +194,12 @@ const AuthenticatedView = memo(
} ) => (
<>
{ messages.map( ( message, index ) => (
<Message key={ index } isUser={ message.role === 'user' }>
<Message key={ index } id={ `message-${ index }` } isUser={ message.role === 'user' }>
{ message.content }
</Message>
) ) }
{ isAssistantThinking && (
<Message isUser={ false }>
<Message isUser={ false } id="message-thinking">
<MessageThinking />
</Message>
) }
Expand All @@ -199,7 +208,7 @@ const AuthenticatedView = memo(
);

const UnauthenticatedView = ( { onAuthenticate }: { onAuthenticate: () => void } ) => (
<Message className="w-full" isUser={ false }>
<Message id="message-unauthenticated" className="w-full" isUser={ false }>
<div className="mb-3 a8c-label-semibold">{ __( 'Hold up!' ) }</div>
<div className="mb-1">
{ __( 'You need to log in to your WordPress.com account to use the assistant.' ) }
Expand Down
8 changes: 7 additions & 1 deletion src/components/icons/assistant.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
import { ReactSVG, SVGProps } from 'react';

interface AssistantIconProps {
size?: number;
}

export function AssistantIcon( { size = 14 }: AssistantIconProps ) {
export function AssistantIcon( {
size = 14,
'aria-hidden': ariaHidden,
}: AssistantIconProps & SVGProps< ReactSVG > ) {
return (
<svg
aria-hidden={ ariaHidden }
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Previously, the existing aria-hidden value introduced in #202 was not passed down to the child svg element.

width={ size }
height={ size }
viewBox={ `0 0 14 14` }
Expand Down
Loading