Skip to content

Commit

Permalink
feat: create chip carousel (#2378)
Browse files Browse the repository at this point in the history
---------

Co-authored-by: monteri <lansevermore>
  • Loading branch information
monteri authored Aug 4, 2023
1 parent 8ae2fa8 commit c1ecb71
Show file tree
Hide file tree
Showing 14 changed files with 389 additions and 64 deletions.
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
File renamed without changes.
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;
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

0 comments on commit c1ecb71

Please sign in to comment.