-
Notifications
You must be signed in to change notification settings - Fork 66
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
[BD-46] feat: create chip carousel #2378
Conversation
Thanks for the pull request, @monteri! When this pull request is ready, tag your edX technical lead. |
✅ Deploy Preview for paragon-openedx ready!Built without sensitive environment variables
To edit notification comments on pull requests, go to your Netlify site configuration. |
src/ChipCarousel/ChipCarousel.scss
Outdated
} | ||
} | ||
|
||
.pgn__chip-carousel__right-control, |
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.
These selectors are children .pgn__chip-carousel
?
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, but if we don't need nesting, it is appropriate to avoid it to have more readable styles.
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.
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'; |
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.
import ChipCarousel from './index'; | |
import ChipCarousel from '.'; |
src/ChipCarousel/README.md
Outdated
notes: | | ||
--- | ||
|
||
``ChipCarousel`` component creates a scrollable horizontal block of chips with buttons for navigating to the previous and next set of chips. |
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.
``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. |
src/ChipCarousel/README.md
Outdated
/> | ||
``` | ||
|
||
## `Chip` Carousel |
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.
Interactive Сhip Сarousel maybe
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 just forgot to delete this example
src/ChipCarousel/README.md
Outdated
</div> | ||
<div ref={setOverflowRef} className="d-flex"> | ||
<OverflowScroll.Items> | ||
<Chip iconAfter={Close}>New</Chip> |
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 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>
))}
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.
Already removed code, thank you for noticing
import { useIntl } from 'react-intl'; | ||
import classNames from 'classnames'; | ||
// @ts-ignore | ||
import { OverflowScroll, OverflowScrollContext } from '../OverflowScroll'; |
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.
Let's try to ignore the necessary import rule for the whole file, this will help to avoid numerous @ts-ignore
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 do it as a separate PR
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.
[clarification] Once the Paragon types PR merges in, I'm assuming these @ts-ignore
comment would become unnecessary?
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 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', |
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.
We do not use camelСase in the declaration of transfer identifiers, let's use exclusively lowercase identifiers
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 just stick to the other id
in the Paragon, do you think we need to change to lowercase all of them?
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.
id: 'pgn.chip-carousel.scroll-to-previous',
maybe
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.
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?
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.
@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"; |
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.
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"
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.
Yeah, I think it can be considered as separate issue
@@ -0,0 +1 @@ | |||
$chip-carousel-controls-top-offset: -3px !default; |
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.
It will be necessary to add a new token :)
Codecov ReportPatch coverage:
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
☔ View full report in Codecov by Sentry. |
src/ChipCarousel/README.md
Outdated
max: offsetType === 'percentage' ? 100 : 1000, | ||
step: offsetType === 'percentage' ? 1 : 50, |
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.
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 offsetType
s so the offset values make sense for each type upon switching?
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.
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'; |
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.
[clarification] Once the Paragon types PR merges in, I'm assuming these @ts-ignore
comment would become unnecessary?
src/ChipCarousel/README.md
Outdated
value: gap, | ||
setValue: setGap, | ||
range: { min: 0, max: 6, step: 0.5 }, | ||
name: 'offset' |
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.
Should this be named gap
instead?
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.
updated to gap
src/ChipCarousel/README.md
Outdated
value: gap, | ||
setValue: setGap, | ||
range: { min: 0, max: 6, step: 0.5 }, | ||
name: 'offset' |
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.
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, 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
);
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.
Ah, yes. Thank you for the reminder. Noted :)
|
||
const messages = defineMessages({ | ||
scrollToPrevious: { | ||
id: 'pgn.ChipCarousel.scrollToPrevious', |
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.
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. */ |
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.
🎉
it('should render the carousel correctly', () => { | ||
render(<TestingChipCarousel />); | ||
|
||
const carousel = document.getElementsByClassName('pgn__chip-carousel'); |
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 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
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.
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]); |
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.
Typically, userEvent.click
is preferred over using fireEvent
directly. See https://stackoverflow.com/a/71031849.
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.
updated to use userEvent.click
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.
LGTM! Just looks like it might need a rebase with master
, though.
src/ChipCarousel/ChipCarousel.scss
Outdated
} | ||
} | ||
|
||
.pgn__chip-carousel__right-control, |
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.
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.
045ff08
to
acaff78
Compare
Updated to use nesting in this case. Also noticed that when we changed |
Good catch! |
@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. |
🎉 This PR is included in version 20.46.0 🎉 The release is available on: Your semantic-release bot 📦🚀 |
Description
Create chip carousel component
Deploy Preview
https://deploy-preview-2378--paragon-openedx.netlify.app
Merge Checklist
example
app?wittjeff
andadamstankiewicz
as reviewers on this PR.Post-merge Checklist