-
Notifications
You must be signed in to change notification settings - Fork 152
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(Card): improve the accessibility of the component #4585
base: master
Are you sure you want to change the base?
Conversation
Storybook staging is available at https://kiwicom-orbit-sarka-card-a11y-fix.surge.sh |
Size Change: +307 B (+0.07%) Total Size: 460 kB
ℹ️ View Unchanged
|
Deploying orbit with
|
Latest commit: |
7a934f5
|
Status: | ✅ Deploy successful! |
Preview URL: | https://69ae352e.orbit.pages.dev |
Branch Preview URL: | https://sarka-card-a11y-fix.orbit.pages.dev |
d847976
to
55b517f
Compare
Storybook staging is available at https://kiwicom-orbit-sarka-card-a11y-fix.surge.sh Playroom staging is available at https://kiwicom-orbit-sarka-card-a11y-fix.surge.sh/playroom |
9db2842
to
1afd500
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Key Issues
The PR review highlights a critical issue where the children
prop is used as a function without type checking, which can lead to runtime errors if children
is not a function.
1afd500
to
c0a5a49
Compare
<Stack inline grow={false} justify="end"> | ||
{actions} | ||
</Stack> | ||
const InteractiveWrapper = ({ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've created this component to avoid code duplication (rendered 3 times within this file).
c0a5a49
to
879c2b8
Compare
/> | ||
{actions && <Actions actions={actions} />} | ||
<div className="p-400 lm:p-600 "> | ||
<InteractiveWrapper onClick={!children ? onClick : undefined}> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I added this !children
condition, bcs in case that the CardSection
has both - header and children and onClick
, then the CardSection component is focusable twice.
879c2b8
to
79ce698
Compare
2c8b08f
to
850a2d5
Compare
850a2d5
to
2abcca2
Compare
@@ -339,12 +340,19 @@ export const CardWithMixedSections: Story = { | |||
<CardSection title={sectionTitle} description={sectionDescription}> | |||
Section Content | |||
</CardSection> | |||
<CardSection onClick={onClick} title={sectionTitle} description={sectionDescription}> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I added a variant with CardSection
with onClick
function, I haven't found this variant in SB.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would also add stories for onClick
together with actions, both non/expanded.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I added these to the story so I can check all cases, so all my comments in the review are based on also these
<Card>
<CardSection
actions={<ButtonLink onClick={handleClick}>Open</ButtonLink>}
onClick={onClick}
expandable={false}
title={sectionTitle}
>
Section Content with onClick
</CardSection>
<CardSection
actions={<ButtonLink onClick={handleClick}>Open</ButtonLink>}
onClick={onClick}
expandable
title={sectionTitle}
>
Section Content with onClick
</CardSection>
</Card>
@@ -339,12 +340,19 @@ export const CardWithMixedSections: Story = { | |||
<CardSection title={sectionTitle} description={sectionDescription}> | |||
Section Content | |||
</CardSection> | |||
<CardSection onClick={onClick} title={sectionTitle} description={sectionDescription}> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would also add stories for onClick
together with actions, both non/expanded.
import Stack from "../../Stack"; | ||
import handleKeyDown from "../../utils/handleKeyDown"; | ||
|
||
const ContentWrapper = ({ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If I understand correctly, you created this Wrapper to solve nested interactive elements when both actions
and onClick
are provided?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes, I created this ContentWrapper
to avoid duplication, otherwise there would be same div with the same attributes (role
, tabIndex
, onClick
, className
,... etc)..
onKeyDown={onClick ? handleKeyDown(onClick) : undefined} | ||
onClick={onClick || undefined} | ||
className={cx( | ||
"orbit-card-wrapper-button flex-1 focus:outline-none", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I find it odd that there is class "orbit-card-wrapper-button"
even if this is not a button & in case of title/header and children, it's present there twice 🤔
<div | ||
className={cx( | ||
"duration-fast lm:border-x border-b transition-all ease-in-out", | ||
"duration-fast lm:border-x relative border-b transition-all ease-in-out", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When the card is not expandable, the focus/outline animation is rather weird and slow compared to expandable card, probably caused by the transition duration? It also looks like there is black border/outline added right before the blue one. And when section is expandable, the grey border disappears and is replaced by the blue one, not happening when not expandable. Can you take a look at these?
onClick != null && "hover:bg-white-normal-hover cursor-pointer", | ||
onClick && !expandable && "hover:bg-white-normal-hover", | ||
onClick && | ||
"has-[.orbit-card-wrapper-button:focus]:focus-within:rounded-100 has-[.orbit-card-wrapper-button:focus]:focus-within:outline-blue-normal has-[.orbit-card-wrapper-button:focus]:focus-within:outline has-[.orbit-card-wrapper-button:focus]:focus-within:outline-2", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
{actions && <Actions actions={actions} />} | ||
</div> | ||
)} | ||
|
||
{children && expandable && ( | ||
<Expandable expanded={opened} slideID={slideID} labelID={slideID}> | ||
<div className="font-base text-normal text-primary-foreground px-400 lm:px-600 w-full leading-normal"> | ||
<div className="py-400 lm:py-600 border-elevation-flat-border-color border-t"> | ||
<ContentWrapper | ||
onClick={opened ? onClick : undefined} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this change on purpose? Because currently on production, when section is opened, onClick
event is not fired.
Shouldn't be there also grey hover effect on this section when expanded, same as on production? Currently it's only on the Header part.
// See comment above | ||
// eslint-disable-next-line jsx-a11y/no-noninteractive-tabindex | ||
tabIndex={onClick == null ? undefined : 0} | ||
onClick={onClick} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As you removed this, now when card is expandable, onClick
is not fired when clicked on the header, is that desired change?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Master:
master.mp4
Your branch:
staging.mp4
expanded={opened} | ||
isSection | ||
/> | ||
</ContentWrapper> | ||
{actions && <Actions actions={actions} />} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In this case, when actions
are set both with onClick
, it's not possible to click on the clickable element in the actions
😬
@@ -105,42 +121,45 @@ export default function CardSection({ | |||
{actions && <Actions actions={actions} />} | |||
</div> | |||
)} | |||
|
|||
{(title != null || header != null) && !expandable && ( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am a bit unsure about the focus order in this case (when both onClick
and actions
are provided), first the action button is being focused and then the card section is being focused, shouldn't it be the other way around? Same as when it's expandable? 🤔
onClick != null && "hover:bg-white-normal-hover cursor-pointer", | ||
onClick && !expandable && "hover:bg-white-normal-hover", | ||
onClick && | ||
"has-[.orbit-card-wrapper-button:focus]:focus-within:rounded-100 has-[.orbit-card-wrapper-button:focus]:focus-within:outline-blue-normal has-[.orbit-card-wrapper-button:focus]:focus-within:outline has-[.orbit-card-wrapper-button:focus]:focus-within:outline-2", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
d94278c
to
8623a66
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Key Issues
Replacing React.isValidElement
with a simple truthy check for actions
is unsafe and could lead to runtime errors if actions
is not a valid React element.
8623a66
to
7a934f5
Compare
@@ -307,7 +309,7 @@ export const CardWithMixedSections: Story = { | |||
expandable | |||
title={sectionTitle} | |||
actions={ | |||
<ButtonLink compact size="small" type="secondary"> | |||
<ButtonLink onClick={buttonClick} compact size="small" type="secondary"> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I added this action('buttonClick')
to be able to see that click action is working in SB.
Closes https://kiwicom.atlassian.net/browse/FEPLT-2214
This PR enhances the accessibility of the
CardSection
component which was done before - #4574 . The PR avoids the duplication ofactions
prop and setsonClick
function only tochildren
element.As agreed before, this PR doesn't cover the variant when
onClick !== null
&& children contains an interactive element (button, link), that should be discussed as a scope of the component's functionality - check documentation.UPDATE:
action
elements does not trigger the card's onClick.:before
hack as it's impossible to make a different implementation for this design of the CardSection component.Check Playroom with variants here.
✨
Description by Callstackai
This PR enhances the accessibility of the
CardSection
component by improving click handling and avoiding duplication of theactions
prop.Diagrams of code changes
Files Changed
onClick
andbuttonClick
props for better interaction handling.ContentWrapper
component for handling click events and improved accessibility features.onClick
prop.This PR includes files in programming languages that we currently do not support. We have not reviewed files with the extensions
.md
. See list of supported languages.