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
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: 2 additions & 2 deletions component-generator/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ exports.COMPONENT_FILES = [
templatePath: path.resolve(__dirname, './templates/README.md'),
},
{
targetPath: path.resolve(__dirname, '../src/componentName/componentName.scss'),
templatePath: path.resolve(__dirname, './templates/styles.scss'),
targetPath: path.resolve(__dirname, '../src/componentName/index.scss'),
templatePath: path.resolve(__dirname, './templates/index.scss'),
},
{
targetPath: path.resolve(__dirname, '../src/componentName/componentName.test.jsx'),
Expand Down
2 changes: 1 addition & 1 deletion component-generator/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ function addComponentToExports(componentName) {
);
fs.appendFileSync(
path.resolve(__dirname, '../src/index.scss'),
`@import './${componentName}/${componentName}.scss';\n`,
`@import "./${componentName}";\n`,
);
}

Expand Down
60 changes: 0 additions & 60 deletions src/Chip/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,63 +47,3 @@ notes: |
</Chip>
</div>
```

## `Chip` Carousel

```jsx live
<OverflowScroll ariaLabel="example chip carousel" hasInteractiveChildren>
<OverflowScrollContext.Consumer>
{({
setOverflowRef,
isScrolledToStart,
isScrolledToEnd,
scrollToPrevious,
scrollToNext,
}) => (
<>
<div className="mb-3">
<Button
onClick={scrollToPrevious}
className="mr-2"
size="sm"
disabled={isScrolledToStart}
>
Previous
</Button>
<Button
onClick={scrollToNext}
size="sm"
disabled={isScrolledToEnd}
>
Next
</Button>
</div>
<div ref={setOverflowRef} className="d-flex">
<OverflowScroll.Items>
<Chip iconAfter={Close}>New</Chip>
<Chip iconAfter={Close}>New</Chip>
<Chip iconAfter={Close}>New</Chip>
<Chip iconAfter={Close}>New</Chip>
<Chip iconAfter={Close}>New</Chip>
<Chip iconAfter={Close}>New</Chip>
<Chip iconAfter={Close}>New</Chip>
<Chip iconAfter={Close}>New</Chip>
<Chip iconAfter={Close}>New</Chip>
<Chip iconAfter={Close}>New</Chip>
<Chip iconAfter={Close}>New</Chip>
<Chip iconAfter={Close}>New</Chip>
<Chip iconAfter={Close}>New</Chip>
<Chip iconAfter={Close}>New</Chip>
<Chip iconAfter={Close}>New</Chip>
<Chip iconAfter={Close}>New</Chip>
<Chip iconAfter={Close}>New</Chip>
<Chip iconAfter={Close}>New</Chip>
<Chip iconAfter={Close}>New</Chip>
<Chip iconAfter={Close}>New</Chip>
</OverflowScroll.Items>
</div>
</>
)}
</OverflowScrollContext.Consumer>
</OverflowScroll>
```
71 changes: 71 additions & 0 deletions src/ChipCarousel/ChipCarousel.test.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import React from 'react';
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { IntlProvider } from 'react-intl';
import ChipCarousel from '.';

const items = [
{
props: {
onClick: jest.fn(),
children: 'Item 1',
'data-testid': 'chip',
},
},
{
props: {
onClick: jest.fn(),
children: 'Item 2',
'data-testid': 'chip',
},
},
{
props: {
onClick: jest.fn(),
children: 'Item 3',
'data-testid': 'chip',
},
},
{
props: {
onClick: jest.fn(),
'data-testid': 'chip',
},
},
'Test string',
];

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

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

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

const chipItems = screen.queryAllByTestId('chip');
expect(chipItems).toHaveLength(items.length - 2);
for (let i = 0; i < chipItems.length - 2; i++) {
expect(chipItems[i].textContent).toBe(items[i].props.children);
}
});

it('should call onClick when a chip item is clicked', async () => {
render(<TestingChipCarousel />);

const chipItems = screen.getByTestId('chip-carousel');
for (let i = 0; i < chipItems.length; i++) {
// eslint-disable-next-line no-await-in-loop
await userEvent.click(chipItems[i]);
expect(items[i].props.onClick).toHaveBeenCalledTimes(1);
}
});
});
84 changes: 84 additions & 0 deletions src/ChipCarousel/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
---
title: 'ChipCarousel'
type: 'component'
components:
- ChipCarousel
categories:
- Content
status: 'New'
designStatus: 'Done'
devStatus: 'Done'
notes: |
---

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

## Basic Usage

```jsx live
() => {
const MAX_PERCENTAGE = 100;
const MAX_FIXED = 1000;
const [offset, setOffset] = useState(50);
const [offsetType, setOffsetType] = useState('fixed');
const [gap, setGap] = useState(3)

const handleChangeOffsetType = (value) => {
const currentMax = offsetType === 'percentage' ? MAX_PERCENTAGE : MAX_FIXED
const newMax = value === 'percentage' ? MAX_PERCENTAGE : MAX_FIXED
const ration = offset / currentMax
const newOffset = Math.floor(newMax * ration)
setOffset(newOffset);
setOffsetType(value);
}

return (
<>
{/* start example form block */}
<ExamplePropsForm
inputs={[
{
value: offset,
setValue: setOffset,
range: {
min: 0,
max: offsetType === 'percentage' ? MAX_PERCENTAGE : MAX_FIXED,
step: offsetType === 'percentage' ? 1 : 50,
},
name: 'offset'
},
{
value: offsetType,
setValue: handleChangeOffsetType,
options: ['percentage', 'fixed'],
name: 'offsetType'
},
{
value: gap,
setValue: setGap,
range: { min: 0, max: 6, step: 0.5 },
name: 'gap'
},
]}
/>
{/* end example form block */}
<ChipCarousel
offset={offset}
offsetType={offsetType}
ariaLabel="example chip carousel"
gap={gap}
items={Array.from({ length: 40 },
(_, index) => (
<Chip
key={`Chip-${index}`}
onClick={() => console.log(`Chip #${index + 1} clicked`)}
>
Chip #{index + 1}
</Chip>
)
)}
/>
</>
)
}
```
1 change: 1 addition & 0 deletions src/ChipCarousel/_variables.scss
Original file line number Diff line number Diff line change
@@ -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 :)

32 changes: 32 additions & 0 deletions src/ChipCarousel/index.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
@import "variables";

.pgn__chip-carousel {
position: relative;

.pgn__overflow-scroll-overflow-container {
--pgn-overflow-scroll-opacity-mask-transparent: rgba(0, 0, 0, 0);
}

@each $level, $space in $spacers {
&.pgn__chip-carousel-gap__#{$level} {
.pgn__overflow-scroll-overflow-container {
column-gap: $space;
}
}
}

.pgn__chip-carousel__right-control,
.pgn__chip-carousel__left-control {
position: absolute;
z-index: 2;
top: $chip-carousel-controls-top-offset;
}

.pgn__chip-carousel__right-control {
right: 0;
}

.pgn__chip-carousel__left-control {
left: 0;
}
}
Loading
Loading