Skip to content

Commit

Permalink
Merge main into dev (#615)
Browse files Browse the repository at this point in the history
* fix: pagination: Issues with VisiblePagerCountSize (#610)

* chore: systemuibutton: pass transparent down via props (#611)

* chore(release): 2.36.0

* fix: select: ensure aria attributes target the intended element (#612)

* fix: select: ensure aria attributes target the intended element

* chore: dropdown: add ariaref ut and refine role null check

* chore: dropdown: update role null check to handle all elements

* chore(release): 2.36.1

* fix: stepper: handle scrollIntoView for both layouts on mount (#613)

* chore(release): 2.36.2

* fix: button: pass disruptive down from props to button from defaultbutton (#614)

---------

Co-authored-by: Ranjith Nori <106535945+rnori-eightfold@users.noreply.github.com>
Co-authored-by: Yash raj chhabra <95337653+ychhabra-eightfold@users.noreply.github.com>
Co-authored-by: Yash Raj Chhabra <ychhabra@eightfold.ai>
  • Loading branch information
4 people authored May 10, 2023
1 parent 6db0dc9 commit 6337c16
Show file tree
Hide file tree
Showing 14 changed files with 217 additions and 99 deletions.
24 changes: 24 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,30 @@

All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.

### [2.36.2](https://github.com/EightfoldAI/octuple/compare/v2.36.1...v2.36.2) (2023-05-09)

### Bug Fixes

- stepper: handle scrollIntoView for both layouts on mount ([#613](https://github.com/EightfoldAI/octuple/issues/613)) ([209d5c3](https://github.com/EightfoldAI/octuple/commits/209d5c3cd492991e67cd854632cdf00193b32b6c))

### [2.36.1](https://github.com/EightfoldAI/octuple/compare/v2.36.0...v2.36.1) (2023-05-04)

### Bug Fixes

- select: ensure aria attributes target the intended element ([#612](https://github.com/EightfoldAI/octuple/issues/612)) ([387158d](https://github.com/EightfoldAI/octuple/commits/387158d892270c8317eb7a4dab4dfc5c0473398e))

## [2.36.0](https://github.com/EightfoldAI/octuple/compare/v2.35.3...v2.36.0) (2023-05-03)

### Features

- adding style option for Empty description ([#606](https://github.com/EightfoldAI/octuple/issues/606)) ([0539a61](https://github.com/EightfoldAI/octuple/commits/0539a61868990ba4e636a559b2ad919af72424a1))
- linkbutton: add link button component ([#607](https://github.com/EightfoldAI/octuple/issues/607)) ([8b3d782](https://github.com/EightfoldAI/octuple/commits/8b3d782e20146413b623159ad280aaeb3360aed4))

### Bug Fixes

- dropdown: use id from clone element props if present ([#608](https://github.com/EightfoldAI/octuple/issues/608)) ([6db0dc9](https://github.com/EightfoldAI/octuple/commits/6db0dc90eb628091c45e03cfa7c7d624b06a312c))
- pagination: Issues with VisiblePagerCountSize ([#610](https://github.com/EightfoldAI/octuple/issues/610)) ([919c7c5](https://github.com/EightfoldAI/octuple/commits/919c7c5248a700a8e866ce2eeeac83ae3e9107b1))

### [2.35.3](https://github.com/EightfoldAI/octuple/compare/v2.35.2...v2.35.3) (2023-04-24)

### Bug Fixes
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@eightfold.ai/octuple",
"version": "2.35.3",
"version": "2.36.2",
"license": "MIT",
"main": "lib/octuple.js",
"types": "lib/octuple.d.ts",
Expand Down
1 change: 1 addition & 0 deletions src/components/Button/DefaultButton/DefaultButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ export const DefaultButton: FC<ButtonProps> = React.forwardRef(
classNames={classNames}
counter={counter}
disabled={disabled}
disruptive={disruptive}
dropShadow={dropShadow}
floatingButtonProps={floatingButtonProps}
htmlType={htmlType}
Expand Down
1 change: 1 addition & 0 deletions src/components/Button/SystemUIButton/SystemUIButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ export const SystemUIButton: FC<ButtonProps> = React.forwardRef(
style={style}
text={text}
toggle={toggle}
transparent={transparent}
variant={ButtonVariant.SystemUI}
/>
);
Expand Down
87 changes: 68 additions & 19 deletions src/components/Dropdown/Dropdown.test.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useState } from 'react';
import React, { useRef, useState } from 'react';
import Enzyme from 'enzyme';
import Adapter from '@wojtekmaj/enzyme-adapter-react-17';
import MatchMediaMock from 'jest-matchmedia-mock';
Expand All @@ -9,8 +9,10 @@ import {
ButtonWidth,
DefaultButton,
} from '../Button';
import { IconName } from '../Icon';
import { Icon, IconName } from '../Icon';
import { List } from '../List';
import { Stack } from '../Stack';
import { TextInput } from '../Inputs';
import { render, screen, waitFor } from '@testing-library/react';

Enzyme.configure({ adapter: new Adapter() });
Expand Down Expand Up @@ -46,26 +48,26 @@ const Overlay = () => (
/>
);

const dropdownProps: object = {
trigger: 'click',
classNames: 'my-dropdown-class',
style: {},
dropdownClassNames: 'my-dropdown-class',
dropdownStyle: {
color: 'red',
},
placement: 'bottom-start',
overlay: Overlay(),
offset: 0,
positionStrategy: 'absolute',
disabled: false,
closeOnDropdownClick: true,
portal: false,
};

const DropdownComponent = (): JSX.Element => {
const [visible, setVisibility] = useState(false);

const dropdownProps: object = {
trigger: 'click',
classNames: 'my-dropdown-class',
style: {},
dropdownClassNames: 'my-dropdown-class',
dropdownStyle: {
color: 'red',
},
placement: 'bottom-start',
overlay: Overlay(),
offset: 0,
positionStrategy: 'absolute',
disabled: false,
closeOnDropdownClick: true,
portal: false,
};

return (
<Dropdown
{...dropdownProps}
Expand All @@ -84,6 +86,36 @@ const DropdownComponent = (): JSX.Element => {
);
};

const ComplexDropdownComponent = (): JSX.Element => {
const inputRef: React.MutableRefObject<HTMLInputElement> =
useRef<HTMLInputElement>(null);
const [visible, setVisibility] = useState(false);

return (
<Dropdown
{...dropdownProps}
ariaRef={inputRef}
onVisibleChange={(isVisible) => setVisibility(isVisible)}
>
<div id="wrapper">
<Stack direction="horizontal" flexGap="xl">
<Icon path={IconName.mdiAccount} />
<TextInput
ref={inputRef}
placeholder={'Select'}
iconProps={{
path: IconName.mdiChevronDown,
rotate: visible ? 180 : 0,
}}
role="combobox"
data-testid="test-input-id"
/>
</Stack>
</div>
</Dropdown>
);
};

describe('Dropdown', () => {
beforeAll(() => {
matchMedia = new MatchMediaMock();
Expand Down Expand Up @@ -132,4 +164,21 @@ describe('Dropdown', () => {
const dropdownButton = screen.getByRole('button');
expect(dropdownButton.id).toBe('test-button-id');
});

test('Should support ariaRef prop for complex dropdown references', async () => {
const { container } = render(<ComplexDropdownComponent />);
const dropdownAriaRef = screen.getByTestId('test-input-id');
expect(dropdownAriaRef.getAttribute('aria-controls')).toBeTruthy();
expect(dropdownAriaRef.getAttribute('aria-expanded')).toBe('false');
expect(dropdownAriaRef.getAttribute('aria-haspopup')).toBe('true');
expect(dropdownAriaRef.getAttribute('role')).toBe('combobox');
dropdownAriaRef.click();
await waitFor(() => screen.getByText('User profile 1'));
const option1 = screen.getByText('User profile 1');
expect(option1).toBeTruthy();
expect(container.querySelector('.dropdown-wrapper')?.classList).toContain(
'my-dropdown-class'
);
expect(dropdownAriaRef.getAttribute('aria-expanded')).toBe('true');
});
});
22 changes: 22 additions & 0 deletions src/components/Dropdown/Dropdown.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ export const Dropdown: FC<DropdownProps> = React.memo(
React.forwardRef<DropdownRef, DropdownProps>(
(
{
ariaRef,
children,
classNames,
closeOnDropdownClick = true,
Expand Down Expand Up @@ -243,6 +244,27 @@ export const Dropdown: FC<DropdownProps> = React.memo(
if (child.props.id && dropdownReferenceId !== child.props.id) {
setReferenceElementId(child.props.id);
}
// If there's an ariaRef, apply the a11y attributes to it, rather than the immediate child.
if (ariaRef && ariaRef.current) {
ariaRef.current.setAttribute('aria-controls', dropdownId);
ariaRef.current.setAttribute('aria-expanded', `${mergedVisible}`);
ariaRef.current.setAttribute('aria-haspopup', 'true');

if (!ariaRef.current.hasAttribute('role')) {
ariaRef.current.setAttribute('role', 'button');
}

return cloneElement(child, {
...{
[TRIGGER_TO_HANDLER_MAP_ON_ENTER[trigger]]: toggle(true),
},
id: dropdownReferenceId,
onClick: handleReferenceClick,
onKeyDown: handleReferenceKeyDown,
className: referenceWrapperClassNames,
});
}

return cloneElement(child, {
...{
[TRIGGER_TO_HANDLER_MAP_ON_ENTER[trigger]]: toggle(true),
Expand Down
6 changes: 6 additions & 0 deletions src/components/Dropdown/Dropdown.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,12 @@ export const TRIGGER_TO_HANDLER_MAP_ON_LEAVE = {
};

export interface DropdownProps {
/**
* The ref of element that should implement the following props:
* 'aria-controls', 'aria-expanded', 'aria-haspopup', 'role'
* @default child
*/
ariaRef?: React.MutableRefObject<HTMLElement>;
/**
* Class names of the main wrapper
*/
Expand Down
47 changes: 31 additions & 16 deletions src/components/Pagination/Pager.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import React, { FC, useEffect, useCallback, useState, Ref } from 'react';
import {
PagerProps,
PagerSizeOptions,
PaginationVisiblePagerCountSizeOptions,
} from './Pagination.types';
import { ButtonShape, ButtonSize, NeutralButton } from '../Button';
Expand All @@ -9,19 +10,18 @@ import { mergeClasses } from '../../shared/utilities';

import styles from './pagination.module.scss';

/** Represents the number of pages from each edge of the list before we show quick buttons. */
const EDGE_BUFFER_THRESHOLD: number = 5;

/** Represents a list too short to display meaningful quick buttons. */
const SHORT_LIST_THRESHOLD: number = 10;

/** Represents the number of list items (pages) visible at any given time. */
const VISIBLE_PAGER_COUNT = {
[PaginationVisiblePagerCountSizeOptions.Small]: 3,
[PaginationVisiblePagerCountSizeOptions.Medium]: 5,
[PaginationVisiblePagerCountSizeOptions.Large]: 7,
};

const PAGER_SIZE_COUNT = {
[PagerSizeOptions.Small]: 5,
[PagerSizeOptions.Medium]: 7,
};

export const Pager: FC<PagerProps> = React.forwardRef(
(
{
Expand All @@ -33,17 +33,32 @@ export const Pager: FC<PagerProps> = React.forwardRef(
quickPreviousIconButtonAriaLabel,
simplified = false,
showLast = true,
pagerSize,
visiblePagerCountSize = PaginationVisiblePagerCountSizeOptions.Large,
...rest
},
ref: Ref<HTMLUListElement>
) => {
/** Represents the number of pages from each edge of the list before we show quick buttons. */
let edgeBufferThreshold: number = 5;
/** Represents a list too short to display meaningful quick buttons. */
let shortListThreshold: number = 10;

if (pagerSize) {
edgeBufferThreshold = PAGER_SIZE_COUNT[pagerSize] - 2;
shortListThreshold = PAGER_SIZE_COUNT[pagerSize] + 3;
}
const [_pagers, setPagers] = useState<number[]>([0]);
const [_quickNextActive, setQuickNextActive] = useState<boolean>(false);
const [_quickPreviousActive, setQuickPreviousActive] =
useState<boolean>(false);

const visiblePagerCount = VISIBLE_PAGER_COUNT?.[visiblePagerCountSize] || 7;
// TODO: Remove in Octuple v3.0.0 in favor of pagerSize prop
let visiblePagerCount = VISIBLE_PAGER_COUNT?.[visiblePagerCountSize] || 7;

if (pagerSize) {
visiblePagerCount = PAGER_SIZE_COUNT?.[pagerSize] || 7;
}

/**
* Updates the visible range of pages in the UL based upon list
Expand All @@ -58,7 +73,7 @@ export const Pager: FC<PagerProps> = React.forwardRef(
* The pageCount is greater than 10.
* Keeping the quick buttons meaningful.
*/
if (pageCount > SHORT_LIST_THRESHOLD) {
if (pageCount > shortListThreshold) {
/**
* The visible items in the array use a basic threshold.
* e.g. [1 << 4 5 [6] 7 8 >> 100] where 6 is the currentPage
Expand All @@ -67,9 +82,9 @@ export const Pager: FC<PagerProps> = React.forwardRef(
* the 5 items between the two quick buttons they must represent
* the correct position in the array.
*/
const afterQuickPrevious: boolean = currentPage > EDGE_BUFFER_THRESHOLD;
const afterQuickPrevious: boolean = currentPage > edgeBufferThreshold;
const beforeQuickNext: boolean =
currentPage < pageCount - EDGE_BUFFER_THRESHOLD;
currentPage < pageCount - edgeBufferThreshold;

/**
* The currentPage number is greater than the quick previous
Expand Down Expand Up @@ -164,8 +179,8 @@ export const Pager: FC<PagerProps> = React.forwardRef(
</li>
)}
{!simplified &&
currentPage > EDGE_BUFFER_THRESHOLD &&
pageCount > SHORT_LIST_THRESHOLD && (
currentPage > edgeBufferThreshold &&
pageCount > shortListThreshold && (
<li>
<NeutralButton
ariaLabel={quickPreviousIconButtonAriaLabel}
Expand All @@ -185,7 +200,7 @@ export const Pager: FC<PagerProps> = React.forwardRef(
onMouseEnter={() => setQuickPreviousActive(true)}
onMouseLeave={() => setQuickPreviousActive(false)}
onClick={() =>
onCurrentChange(currentPage - EDGE_BUFFER_THRESHOLD)
onCurrentChange(currentPage - edgeBufferThreshold)
}
size={ButtonSize.Medium}
/>
Expand All @@ -212,8 +227,8 @@ export const Pager: FC<PagerProps> = React.forwardRef(
);
})}
{!simplified &&
currentPage < pageCount - EDGE_BUFFER_THRESHOLD &&
pageCount > SHORT_LIST_THRESHOLD && (
currentPage < pageCount - edgeBufferThreshold &&
pageCount > shortListThreshold && (
<li>
<NeutralButton
ariaLabel={quickNextIconButtonAriaLabel}
Expand All @@ -233,7 +248,7 @@ export const Pager: FC<PagerProps> = React.forwardRef(
onMouseEnter={() => setQuickNextActive(true)}
onMouseLeave={() => setQuickNextActive(false)}
onClick={() =>
onCurrentChange(currentPage + EDGE_BUFFER_THRESHOLD)
onCurrentChange(currentPage + edgeBufferThreshold)
}
size={ButtonSize.Medium}
/>
Expand Down
8 changes: 2 additions & 6 deletions src/components/Pagination/Pagination.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,7 @@
import React from 'react';
import { Stories } from '@storybook/addon-docs';
import { ComponentStory, ComponentMeta } from '@storybook/react';
import {
Pagination,
PaginationLayoutOptions,
PaginationVisiblePagerCountSizeOptions,
} from './index';
import { PagerSizeOptions, Pagination, PaginationLayoutOptions } from './index';

export default {
title: 'Pagination',
Expand Down Expand Up @@ -116,7 +112,7 @@ const paginationArgs: Object = {
restrictPageSizesPropToSizesLayout: false,
hideWhenSinglePage: false,
total: 50,
visiblePagerCountSize: PaginationVisiblePagerCountSizeOptions.Large,
pagerSize: PagerSizeOptions.Medium,
'data-test-id': 'myPaginationTestId',
};

Expand Down
4 changes: 4 additions & 0 deletions src/components/Pagination/Pagination.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ export const Pagination: FC<PaginationProps> = React.forwardRef(
onCurrentChange,
onSizeChange,
pageCount,
// TODO: Set a default once visiblePagerCountSize is removed
pagerSize,
pageSize = 10,
pageSizeButtonAriaLabel: defaultPageSizeButtonAriaLabel,
pageSizes = [10, 20, 30, 40, 50, 100],
Expand Down Expand Up @@ -393,6 +395,7 @@ export const Pagination: FC<PaginationProps> = React.forwardRef(
locale={locale}
onCurrentChange={handleCurrentChange}
pageCount={getPageCount()}
pagerSize={pagerSize}
quickNextIconButtonAriaLabel={
quickNextIconButtonAriaLabel
}
Expand All @@ -412,6 +415,7 @@ export const Pagination: FC<PaginationProps> = React.forwardRef(
locale={locale}
onCurrentChange={handleCurrentChange}
pageCount={getPageCount()}
pagerSize={pagerSize}
quickNextIconButtonAriaLabel={
quickNextIconButtonAriaLabel
}
Expand Down
Loading

0 comments on commit 6337c16

Please sign in to comment.