Skip to content

[Clov-406][BpkRating] Improve BpkRating component to allow its subtitle text truncation in edge cases #3872

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

Merged
merged 5 commits into from
Jul 8, 2025

Conversation

Faye-Xiao
Copy link
Contributor

@Faye-Xiao Faye-Xiao commented Jul 7, 2025

We have received feedback that in real production, BpkRating may not display all subtitle text content when the component is placed in a width-limited container.
Screenshot 2025-07-07 at 16 23 56

Ideally, BpkRating only has 1~2 words in the subtitle. But it could be text overflow in a long language.
This PR is to improve this question. The solution is to make its subtitle truncated when the container width is not enough to display all the content. One condition is not to limit the width of its own component.

before after note
default
large size

Remember to include the following changes:

  • Ensure the PR title includes the name of the component you are changing so it's clear in the release notes for consumers of the changes in the version e.g [Clover-123][BpkButton] Updating the colour
  • README.md (If you have created a new component)
  • Component README.md
  • Tests
  • Accessibility tests
    • The following checks were performed:
      • Ability to navigate using a keyboard only
      • Zoom functionality (Deque University explanation):
        • The page SHOULD be functional AND readable when only the text is magnified to 200% of its initial size
        • Pages must reflow as zoom increases up to 400% so that content continues to be presented in only one column i.e. Content MUST NOT require scrolling in two directions (both vertically and horizontally)
      • Ability to navigate using a screen reader only
  • Storybook examples created/updated
  • For breaking changes or deprecating components/properties, migration guides added to the description of the PR. If the guide has large changes, consider creating a new Markdown page inside the component's docs folder and link it here


expect(screen.getByText('Excellent')).toBeVisible()
expect(screen.getByText('672 reviews')).toBeVisible()
expect(screen.getByLabelText('4.6 Excellent 672 reviews')).toBeInTheDocument();
Copy link
Contributor Author

Choose a reason for hiding this comment

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

As we do not recommend using toMatchSnapshot, changed it to use jest expect element.

const defaultProps = {
ratingScale: RATING_SCALES.zeroToFive,
size: RATING_SIZES.base,
};
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Added default props for testing.

expect(asFragment()).toMatchSnapshot();

expect(screen.getByLabelText('6.7 Average might recommend')).toHaveClass('bpk-rating');
expect(screen.getByText('/5')).toHaveClass('bpk-rating__scale');
Copy link
Contributor Author

Choose a reason for hiding this comment

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

As the ratingScale: RATING_SCALES.zeroToFive, it displays the max value.

expect(screen.getByText('Low')).toBeVisible();
expect(screen.getByText('Bad option')).toBeVisible();
expect(screen.getByLabelText('-1.3 Low bad option')).toBeInTheDocument();
expect(screen.getByText('/5')).toHaveClass('bpk-rating__scale');
Copy link
Contributor Author

Choose a reason for hiding this comment

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

As the ratingScale: RATING_SCALES.zeroToFive, it displays the max value.

expect(screen.getByText('Smashing')).toBeVisible();
expect(screen.getByText('Doubleplusgood')).toBeVisible();
expect(screen.getByLabelText('10 Super, smashing, great')).toBeInTheDocument();
expect(screen.getByText('/5')).toHaveClass('bpk-rating__scale');
Copy link
Contributor Author

@Faye-Xiao Faye-Xiao Jul 7, 2025

Choose a reason for hiding this comment

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

As the ratingScale: RATING_SCALES.zeroToFive, it displays the max value 5.

@skyscanner-backpack-bot
Copy link

Visit https://backpack.github.io/storybook-prs/3872 to see this build running in a browser.

@@ -34,6 +34,7 @@

&__text-wrapper {
display: flex;
min-width: 0;
Copy link
Contributor Author

@Faye-Xiao Faye-Xiao Jul 7, 2025

Choose a reason for hiding this comment

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

The container of text-wrapper uses the flex layout. The min-width:0 is to make its min width 0. Allow flex-item to shrink to 0, thus giving child elements a real chance to "squeeze".

There is a nested flex layout in the BpkRating
Screenshot 2025-07-07 at 16 47 52

@@ -61,6 +62,12 @@
}

&__subtitle {
min-width: 0;
max-width: 100%;
flex: 1 1 auto;
Copy link
Contributor Author

@Faye-Xiao Faye-Xiao Jul 7, 2025

Choose a reason for hiding this comment

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

The __subtitle is the second flex item in the flex container text-wrapper.

The flex: 1 1 auto; is to occupy the remaining space, can be reduced, with three sets of triggering ellipsis.

  • When flex-direction: row; in text-wrapper
    • min-width:0 -> to make its min-width 0, not use the default min-width: auto
  • When flex-direction: column; in text-wrapper
    • max-width: 100%; -> As flex direction is changed, they now take care of the height, not the width we want to truncate. So we can add a max-content to the container horizontally

See the related CSS spec https://www.w3.org/TR/css-flexbox-1/#min-size-auto

color: tokens.$bpk-text-secondary-day;
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
Copy link
Contributor Author

Choose a reason for hiding this comment

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

3 sets of triggering text ellipsis.

showScale: true,
title: null,
};

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Removed the propTypes and defaultProps and use TS type definition solution.

@Faye-Xiao Faye-Xiao added the patch Patch production bug label Jul 7, 2025
@skyscanner-backpack-bot
Copy link

Visit https://backpack.github.io/storybook-prs/3872 to see this build running in a browser.

@skyscanner-backpack-bot
Copy link

skyscanner-backpack-bot bot commented Jul 7, 2025

Warnings
⚠️

Package source files (e.g. packages/package-name/src/Component.js) were updated, but snapshots weren't. Have you checked that the tests still pass?

Browser support

If this is a visual change, make sure you've tested it in multiple browsers.

Generated by 🚫 dangerJS against 19933a5

@Faye-Xiao
Copy link
Contributor Author

The Percy failure https://percy.io/09e69e44/web/backpack/builds/41384044?utm_campaign=09e69e44&utm_content=backpack&utm_source=github_status_public came from the flaky test. A slight change that does not involve visuals.

@GCpigsic
Copy link
Contributor

GCpigsic commented Jul 7, 2025

A stupid question:
Could it be that the screenshots for the large size—before and after—have been mixed up?

@Faye-Xiao
Copy link
Contributor Author

Could it be that the screenshots for the large size—before and after—have been mixed up?

Yes. Good catch! I made a mistake. Corrected it.

@GCpigsic GCpigsic requested a review from Copilot July 8, 2025 03:11
Copy link
Contributor

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull Request Overview

This PR improves the BpkRating component by enabling subtitle text truncation in constrained containers and refactors the code and tests to modern patterns.

  • Adds CSS rules to truncate the subtitle with an ellipsis when there's insufficient width.
  • Refactors BpkRating to use inline default props, removes prop-types, and adds a catch-all type for extra props.
  • Updates tests to use @testing-library/react’s screen queries and removes legacy snapshots.

Reviewed Changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
packages/bpk-component-rating/src/accessibility-test.tsx Introduces defaultProps and updates accessibility test render
packages/bpk-component-rating/src/BpkRating.tsx Removes PropTypes, adds inline defaults and rest index signature
packages/bpk-component-rating/src/BpkRating.module.scss Adds min-width: 0 and ellipsis styles for subtitle truncation
packages/bpk-component-rating/src/BpkRating-test.tsx Replaces snapshots with screen-based assertions
packages/bpk-component-rating/src/snapshots/* Deletes outdated snapshot file

it('should not have programmatically-detectable accessibility issues', async () => {
const { container } = render(
<BpkRating
{...defaultProps}
Copy link
Preview

Copilot AI Jul 8, 2025

Choose a reason for hiding this comment

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

The accessibility test render is missing the required value prop on <BpkRating>, which may lead to runtime errors or unexpected output. Add a value prop (e.g., value={3.6}) to the rendered component.

Copilot uses AI. Check for mistakes.

ariaLabel="4.9 Awesome It is a fantanstic place"
title="Awesome"
subtitle="It is a fantanstic place"
value={4.9}
showScale={false}
/>,
);
expect(asFragment()).toMatchSnapshot();

expect(screen.queryByText('4.9')).not.toHaveClass('bpk-rating__scale');
Copy link
Preview

Copilot AI Jul 8, 2025

Choose a reason for hiding this comment

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

This assertion checks the class on the numeric value element rather than verifying the absence of the scale element. To confirm the scale is not rendered, use something like expect(screen.queryByText('/5')).toBeNull().

Suggested change
expect(screen.queryByText('4.9')).not.toHaveClass('bpk-rating__scale');
expect(screen.queryByText('/5')).toBeNull();

Copilot uses AI. Check for mistakes.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is a special case as it will display 4.9 because of the ratingScale: RATING_SCALES.zeroToFive. The bpk-rating__scale is for the case when showScale=true

@@ -55,20 +54,20 @@ type Props = {
subtitle?: string,
title?: string | ReactNode,
value: string | number,
[rest: string]: any
Copy link
Preview

Copilot AI Jul 8, 2025

Choose a reason for hiding this comment

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

[nitpick] The index signature [rest: string]: any is very permissive and can hide invalid props. Consider extending React.HTMLAttributes<HTMLDivElement> (or the appropriate element interface) to provide stronger typing for spread props.

Suggested change
[rest: string]: any

Copilot uses AI. Check for mistakes.

Copy link
Contributor

@GCpigsic GCpigsic Jul 8, 2025

Choose a reason for hiding this comment

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

I also tried to remove this type declaration, and the type check also passed. is there any reason that we add it?
image

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, it won't trigger TS check. The reason why I added is beacuse it is used in the below code.

<div className={classNames} aria-label={ariaLabel} role="figure" {...rest}>

And also referred to the similar code written.

Comment on lines +136 to +155
<span className={titleStyles}>
<BpkText
textStyle={titleTextSize}
tagName="span"
aria-hidden="true"
>
{title}
</BpkText>
</span>
)}

{subtitle && (
{subtitle && (<span className={subtitleStyles}>
<BpkText
textStyle={subtitleTextSize}
tagName="span"
aria-hidden="true"
>
<span className={subtitleStyles}>{subtitle}</span>
{subtitle}
</BpkText>
</span>
Copy link
Preview

Copilot AI Jul 8, 2025

Choose a reason for hiding this comment

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

[nitpick] The extra wrapper <span> around <BpkText> adds redundant markup. If BpkText supports a className or wrapperClassName prop, apply titleStyles directly to it to simplify the DOM structure.

Copilot uses AI. Check for mistakes.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

We do not expect to make obvious changes to the DOM structure at this time. I don't know why it's designed as a nested SPAN here.

Copy link
Contributor

@GCpigsic GCpigsic left a comment

Choose a reason for hiding this comment

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

Could we add storybooks for the truncated case?

@Faye-Xiao
Copy link
Contributor Author

https://backpack.github.io/storybook-prs/3872

Hi @GCpigsic No strong need to add new case for that. Because we can see that in the current storybook use case if you click this link https://backpack.github.io/storybook-prs/3872/?path=/story/bpk-component-rating--visual-test-with-zoom&globals=viewport:mobile1.

Screenshot 2025-07-08 at 14 59 04

@skyscanner-backpack-bot
Copy link

Visit https://backpack.github.io/storybook-prs/3872 to see this build running in a browser.

Copy link
Contributor

@GCpigsic GCpigsic left a comment

Choose a reason for hiding this comment

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

LGTM

@Faye-Xiao Faye-Xiao merged commit 44eb6aa into main Jul 8, 2025
10 checks passed
@Faye-Xiao Faye-Xiao deleted the CLOV-406 branch July 8, 2025 10:13
@Faye-Xiao Faye-Xiao added V38 Planned this change in V38 version and removed V38 Planned this change in V38 version labels Jul 9, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
patch Patch production bug
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants