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

[BD-46] feat: create chip carousel #2378

Merged

Conversation

monteri
Copy link
Contributor

@monteri monteri commented Jun 16, 2023

Description

Create chip carousel component

Deploy Preview

https://deploy-preview-2378--paragon-openedx.netlify.app

Merge Checklist

  • If your update includes visual changes, have they been reviewed by a designer? Send them a link to the Netlify deploy preview, if applicable.
  • Does your change adhere to the documented style conventions?
  • Do any prop types have missing descriptions in the Props API tables in the documentation site (check deploy preview)?
  • Were your changes tested using all available themes (see theme switcher in the header of the deploy preview, under the "Settings" icon)?
  • Were your changes tested in the example app?
  • Is there adequate test coverage for your changes?
  • Consider whether this change needs to reviewed/QA'ed for accessibility (a11y). If so, please add wittjeff and adamstankiewicz as reviewers on this PR.

Post-merge Checklist

  • Verify your changes were released to NPM at the expected version.
  • If you'd like, share your contribution in #show-and-tell.
  • 🎉 🙌 Celebrate! Thanks for your contribution.

@openedx-webhooks
Copy link

openedx-webhooks commented Jun 16, 2023

Thanks for the pull request, @monteri!

When this pull request is ready, tag your edX technical lead.

@openedx-webhooks openedx-webhooks added the blended PR is managed through 2U's blended developmnt program label Jun 16, 2023
@netlify
Copy link

netlify bot commented Jun 16, 2023

Deploy Preview for paragon-openedx ready!

Built without sensitive environment variables

Name Link
🔨 Latest commit acaff78
🔍 Latest deploy log https://app.netlify.com/sites/paragon-openedx/deploys/64c26a875685ce0008cb75ad
😎 Deploy Preview https://deploy-preview-2378--paragon-openedx.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify site configuration.

@monteri monteri self-assigned this Jun 16, 2023
@monteri monteri marked this pull request as draft June 16, 2023 11:48
@monteri monteri marked this pull request as ready for review June 19, 2023 14:58
}
}

.pgn__chip-carousel__right-control,
Copy link
Contributor

Choose a reason for hiding this comment

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

These selectors are children .pgn__chip-carousel?

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, but if we don't need nesting, it is appropriate to avoid it to have more readable styles.

Copy link
Member

Choose a reason for hiding this comment

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

Yeah, I'd say the nesting isn't a hard requirement given we have the convention of BEM class names (mostly) prefixed with pgn__{componentname}. Unlikely for collisions to happen, though still possible.

import React from 'react';
import { render, fireEvent } from '@testing-library/react';
import { IntlProvider } from 'react-intl';
import ChipCarousel from './index';
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
import ChipCarousel from './index';
import ChipCarousel from '.';

notes: |
---

``ChipCarousel`` component creates a scrollable horizontal block of chips with buttons for navigating to the previous and next set of chips.
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
``ChipCarousel`` component creates a scrollable horizontal block of chips with buttons for navigating to the previous and next set of chips.
The ``ChipCarousel`` component creates a scrollable horizontal block of chips with buttons for navigating to the previous and next set of chips.

/>
```

## `Chip` Carousel
Copy link
Contributor

Choose a reason for hiding this comment

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

Interactive Сhip Сarousel maybe

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I just forgot to delete this example

</div>
<div ref={setOverflowRef} className="d-flex">
<OverflowScroll.Items>
<Chip iconAfter={Close}>New</Chip>
Copy link
Contributor

Choose a reason for hiding this comment

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

I propose to simulate an array in this way. This will help us keep the example compact.

{[...Array(12).keys()].map(item => (
  <Chip key={item + 1} iconAfter={Close}>New</Chip>
))}

Copy link
Contributor Author

@monteri monteri Jun 21, 2023

Choose a reason for hiding this comment

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

Already removed code, thank you for noticing

import { useIntl } from 'react-intl';
import classNames from 'classnames';
// @ts-ignore
import { OverflowScroll, OverflowScrollContext } from '../OverflowScroll';
Copy link
Contributor

Choose a reason for hiding this comment

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

Let's try to ignore the necessary import rule for the whole file, this will help to avoid numerous @ts-ignore

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I would do it as a separate PR

Copy link
Member

Choose a reason for hiding this comment

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

[clarification] Once the Paragon types PR merges in, I'm assuming these @ts-ignore comment would become unnecessary?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I checked by adding d.ts file for Icon component. After that I could remove ts-ignore for import Icon ....


const messages = defineMessages({
scrollToPrevious: {
id: 'pgn.ChipCarousel.scrollToPrevious',
Copy link
Contributor

Choose a reason for hiding this comment

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

We do not use camelСase in the declaration of transfer identifiers, let's use exclusively lowercase identifiers

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I just stick to the other id in the Paragon, do you think we need to change to lowercase all of them?

Copy link
Contributor

@PKulkoRaccoonGang PKulkoRaccoonGang Jun 21, 2023

Choose a reason for hiding this comment

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

id: 'pgn.chip-carousel.scroll-to-previous', maybe

Copy link
Member

Choose a reason for hiding this comment

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

We do not use camelСase in the declaration of transfer identifiers, let's use exclusively lowercase identifiers

@PKulkoRaccoonGang [clarification] Can you expand on this comment a bit more?

Copy link
Member

Choose a reason for hiding this comment

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

@PKulkoRaccoonGang @monteri After discussing, we decided to stick with the camelCase message ID format (it's used through frontend-app-learning and some other MFEs. Up to the owning team of repo to decide the standard).

src/index.scss Outdated
@@ -11,6 +11,7 @@
@import "./Collapsible/Collapsible";
@import "./CloseButton/CloseButton";
@import "./Chip/Chip";
@import "./ChipCarousel/ChipCarousel";
Copy link
Contributor

Choose a reason for hiding this comment

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

Curious moment. It seems to me that if the style files are called index, we can remove the duplication of the name from the path here

For example: @import "./ChipCarousel"

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah, I think it can be considered as separate issue

@@ -0,0 +1 @@
$chip-carousel-controls-top-offset: -3px !default;
Copy link
Contributor

Choose a reason for hiding this comment

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

It will be necessary to add a new token :)

@codecov
Copy link

codecov bot commented Jun 23, 2023

Codecov Report

Patch coverage: 85.29% and project coverage change: +0.26% 🎉

Comparison is base (319b694) 91.39% compared to head (acaff78) 91.65%.

Additional details and impacted files
@@            Coverage Diff             @@
##           master    #2378      +/-   ##
==========================================
+ Coverage   91.39%   91.65%   +0.26%     
==========================================
  Files         234      236       +2     
  Lines        4161     4195      +34     
  Branches     1001     1012      +11     
==========================================
+ Hits         3803     3845      +42     
+ Misses        351      346       -5     
+ Partials        7        4       -3     
Files Changed Coverage Δ
src/OverflowScroll/OverflowScroll.jsx 100.00% <ø> (ø)
src/ChipCarousel/index.tsx 84.84% <84.84%> (ø)
src/ChipCarousel/messages.js 100.00% <100.00%> (ø)

... and 3 files with indirect coverage changes

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

Comment on lines 34 to 46
max: offsetType === 'percentage' ? 100 : 1000,
step: offsetType === 'percentage' ? 1 : 50,
Copy link
Member

Choose a reason for hiding this comment

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

On page load, offsetType is defaulted to "fixed" at a value of 120. However, if the user switches to "percentage", 120 > the max of 100, so the percentage offset changes to 100%, which doesn't quite demonstrate the percentage increment option effectively.

I wonder if we should reset the offset value when switching between offsetTypes so the offset values make sense for each type upon switching?

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 function to be more user-friendly when offsetType is switched. It calculates ration and sets relatively equal value between fixed and percentage.

import { useIntl } from 'react-intl';
import classNames from 'classnames';
// @ts-ignore
import { OverflowScroll, OverflowScrollContext } from '../OverflowScroll';
Copy link
Member

Choose a reason for hiding this comment

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

[clarification] Once the Paragon types PR merges in, I'm assuming these @ts-ignore comment would become unnecessary?

value: gap,
setValue: setGap,
range: { min: 0, max: 6, step: 0.5 },
name: 'offset'
Copy link
Member

Choose a reason for hiding this comment

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

Should this be named gap instead?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

updated to gap

value: gap,
setValue: setGap,
range: { min: 0, max: 6, step: 0.5 },
name: 'offset'
Copy link
Member

Choose a reason for hiding this comment

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

[curious] It looks like there's no difference in the gap between 0 and 0.5; is this expected?

image 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, probably I have talked about this case that there is no .5 spacer value.

$spacers: map-merge(
  (
    0: 0,
    1: ($spacer * .25),
    1\.5: ($spacer * .375),
    2: ($spacer * .5),
    2\.5: ($spacer * .75),
    3: $spacer,
    3\.5: ($spacer * 1.25),
    4: ($spacer * 1.5),
    4\.5: ($spacer * 2),
    5: ($spacer * 3),
    5\.5: ($spacer * 4),
    6: ($spacer * 5),
  ),
  $spacers
);

Copy link
Member

Choose a reason for hiding this comment

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

Ah, yes. Thank you for the reminder. Noted :)


const messages = defineMessages({
scrollToPrevious: {
id: 'pgn.ChipCarousel.scrollToPrevious',
Copy link
Member

Choose a reason for hiding this comment

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

We do not use camelСase in the declaration of transfer identifiers, let's use exclusively lowercase identifiers

@PKulkoRaccoonGang [clarification] Can you expand on this comment a bit more?

@@ -84,7 +84,9 @@ OverflowScroll.propTypes = {
onScrollPrevious: PropTypes.func,
/** Callback function for when the user scrolls to the next element. */
onScrollNext: PropTypes.func,
offset: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
/** A value specifying the distance the scroll should move. */
Copy link
Member

Choose a reason for hiding this comment

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

🎉

it('should render the carousel correctly', () => {
render(<TestingChipCarousel />);

const carousel = document.getElementsByClassName('pgn__chip-carousel');
Copy link
Member

Choose a reason for hiding this comment

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

Is there a way around needing to rely on the implementation details of the component in these tests by relying more on the @testing-library/react (RTL) API?

This is kind of using RTL almost as if it were Enzyme by relying on implementation details like class names. Perhaps we rely more on data-testid attributes here for selections?

function TestingChipCarousel(props) {
  return (
    <IntlProvider locale="en">
      <ChipCarousel data-testid="chip-carousel" ariaLabel={ariaLabel} items={items} {...props} />
    </IntlProvider>
  );
}

render(<TestingChipCarousel />);

const carousel = screen.getByTestId('chip-carousel');

Similarly for the chipItems below, we could do something like add a data-testid="chip-component" on each child object passed to ChipCarousel such that we could query using RTL like: screen.queryByTestId('chip-component')

Reference: https://kentcdodds.com/blog/testing-implementation-details

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Agree on this point. Refactored tests to use data-testid


const chipItems = document.getElementsByClassName('pgn__chip');
for (let i = 0; i < chipItems.length; i++) {
fireEvent.click(chipItems[i]);
Copy link
Member

Choose a reason for hiding this comment

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

Typically, userEvent.click is preferred over using fireEvent directly. See https://stackoverflow.com/a/71031849.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

updated to use userEvent.click

@adamstankiewicz adamstankiewicz linked an issue Jul 21, 2023 that may be closed by this pull request
Copy link
Member

@adamstankiewicz adamstankiewicz left a comment

Choose a reason for hiding this comment

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

LGTM! Just looks like it might need a rebase with master, though.

}
}

.pgn__chip-carousel__right-control,
Copy link
Member

Choose a reason for hiding this comment

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

Yeah, I'd say the nesting isn't a hard requirement given we have the convention of BEM class names (mostly) prefixed with pgn__{componentname}. Unlikely for collisions to happen, though still possible.

@monteri
Copy link
Contributor Author

monteri commented Jul 27, 2023

Yeah, I'd say the nesting isn't a hard requirement given we have the convention of BEM class names (mostly) prefixed with pgn__{componentname}. Unlikely for collisions to happen, though still possible.

Updated to use nesting in this case. Also noticed that when we changed componentName.scss to index.scss we forgot to update component-generator. Made a quick fix as part of this PR

@adamstankiewicz
Copy link
Member

Updated to use nesting in this case. Also noticed that when we changed componentName.scss to index.scss we forgot to update component-generator. Made a quick fix as part of this PR

Good catch!

@adamstankiewicz adamstankiewicz merged commit c1ecb71 into openedx:master Aug 4, 2023
8 of 9 checks passed
@openedx-webhooks
Copy link

@monteri 🎉 Your pull request was merged! Please take a moment to answer a two question survey so we can improve your experience in the future.

@edx-semantic-release
Copy link
Contributor

🎉 This PR is included in version 20.46.0 🎉

The release is available on:

Your semantic-release bot 📦🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
blended PR is managed through 2U's blended developmnt program raccoon-gang released
Projects
No open projects
Status: Done
Development

Successfully merging this pull request may close these issues.

Add ChipCarousel component
5 participants