Skip to content

Commit

Permalink
feat(accordion): add definition list use case
Browse files Browse the repository at this point in the history
Signed-off-by: Boaz Shuster <boaz.shuster.github@gmail.com>
  • Loading branch information
boaz0 committed Jul 25, 2019
1 parent 9363e37 commit 9b46347
Show file tree
Hide file tree
Showing 7 changed files with 224 additions and 71 deletions.
127 changes: 125 additions & 2 deletions packages/patternfly-4/react-core/src/components/Accordion/Accordion.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ class SimpleAccordion extends React.Component {
}
};
return (
<Accordion>
<Accordion asDefinitionList={false}>
<AccordionItem>
<AccordionToggle
onClick={() => {onToggle('ex-toggle1')}}
Expand Down Expand Up @@ -154,7 +154,7 @@ class FixedAccordion extends React.Component {
};

return (
<Accordion>
<Accordion asDefinitionList={false}>
<AccordionItem>
<AccordionToggle
onClick={() => toggle('ex-toggle1')}
Expand Down Expand Up @@ -254,3 +254,126 @@ class FixedAccordion extends React.Component {
}
}
```

# Accordion definition list example
```js
import React from 'react';
import { Accordion, AccordionItem, AccordionContent, AccordionToggle } from '@patternfly/react-core';

class SimpleAccordion extends React.Component {
constructor(props) {
super(props);
this.state = {
expanded: 'ex-toggle2'
};
}

render() {
const onToggle = id => {
if (id === this.state.expanded) {
this.setState({expanded: ''});
} else {
this.setState({expanded: id })
}
};
return (
<Accordion asDefinitionList>
<AccordionItem>
<AccordionToggle
onClick={() => {onToggle('ex-toggle1')}}
isExpanded={this.state.expanded==='ex-toggle1'}
id="ex-toggle1"
>
Item One
</AccordionToggle>
<AccordionContent
id="ex-expand1"
isHidden={this.state.expanded !== 'ex-toggle1'}
>
<p>
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et
dolore magna aliqua.
</p>
</AccordionContent>
</AccordionItem>

<AccordionItem>
<AccordionToggle
onClick={() => {onToggle('ex-toggle2')}}
isExpanded={this.state.expanded === 'ex-toggle2'}
id="ex-toggle2"
>
Item Two
</AccordionToggle>
<AccordionContent
id="ex-expand2"
isHidden={this.state.expanded !=='ex-toggle2'}
>
<p>
Vivamus et tortor sed arcu congue vehicula eget et diam. Praesent nec dictum lorem. Aliquam id diam
ultrices, faucibus erat id, maximus nunc.
</p>
</AccordionContent>
</AccordionItem>

<AccordionItem>
<AccordionToggle
onClick={() => {onToggle('ex-toggle3')}}
isExpanded={this.state.expanded === 'ex-toggle3'}
id="ex-toggle3"
>
Item Three
</AccordionToggle>
<AccordionContent
id="ex-expand3"
isHidden={this.state.expanded !== 'ex-toggle3'}
>
<p>Morbi vitae urna quis nunc convallis hendrerit. Aliquam congue orci quis ultricies tempus.</p>
</AccordionContent>
</AccordionItem>

<AccordionItem>
<AccordionToggle
onClick={() => {onToggle('ex-toggle4')}}
isExpanded={this.state.expanded === 'ex-toggle4'}
id="ex-toggle4"
>
Item Four
</AccordionToggle>
<AccordionContent
id="ex-expand4"
isHidden={this.state.expanded !== 'ex-toggle4'}
>
<p>
Donec vel posuere orci. Phasellus quis tortor a ex hendrerit efficitur. Aliquam lacinia ligula pharetra,
sagittis ex ut, pellentesque diam. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices
posuere cubilia Curae; Vestibulum ultricies nulla nibh. Etiam vel dui fermentum ligula ullamcorper
eleifend non quis tortor. Morbi tempus ornare tempus. Orci varius natoque penatibus et magnis dis
parturient montes, nascetur ridiculus mus. Mauris et velit neque. Donec ultricies condimentum mauris,
pellentesque imperdiet libero convallis convallis. Aliquam erat volutpat. Donec rutrum semper tempus.
Proin dictum imperdiet nibh, quis dapibus nulla. Integer sed tincidunt lectus, sit amet auctor eros.
</p>
</AccordionContent>
</AccordionItem>

<AccordionItem>
<AccordionToggle
onClick={() => {onToggle('ex-toggle5')}}
isExpanded={this.state.expanded === 'ex-toggle5'}
id="ex-toggle5"
>
Item Five
</AccordionToggle>
<AccordionContent
id="ex-expand5"
isHidden={this.state.expanded !== 'ex-toggle5'}

>
<p>Vivamus finibus dictum ex id ultrices. Mauris dictum neque a iaculis blandit.</p>
</AccordionContent>
</AccordionItem>
</Accordion>
);
}
}
```
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { Accordion } from './Accordion';
import { AccordionToggle } from './AccordionToggle';
import { AccordionContent } from './AccordionContent';
import { AccordionItem } from './AccordionItem';
import { AccordionContext } from './AccordionContext';

describe('Accordion', () => {
test('Accordion default', () => {
Expand All @@ -13,7 +14,7 @@ describe('Accordion', () => {

test('Accordion with non-default headingLevel', () => {
const view = shallow(
<Accordion headingLevel="h2">
<Accordion asDefinitionList={false} headingLevel="h2">
<AccordionItem>
<AccordionToggle id="item-1">Item One</AccordionToggle>
<AccordionContent>Item One Content</AccordionContent>
Expand All @@ -23,19 +24,26 @@ describe('Accordion', () => {
expect(view.render()).toMatchSnapshot();
});


test('It should pass optional aria props', () => {
const view = mount(
<AccordionToggle aria-label="Toggle details for" aria-labelledby="ex-toggle2 ex-item2" id="ex-toggle2" />
<AccordionContext.Provider value={{ asDefinitionList: true }}>
<AccordionToggle aria-label="Toggle details for" aria-labelledby="ex-toggle2 ex-item2" id="ex-toggle2" />
</AccordionContext.Provider>,
{}
);
console.log(view.debug());
const button = view.find('button[id="ex-toggle2"]').getElement();
expect(button.props['aria-label']).toBe('Toggle details for');
expect(button.props['aria-labelledby']).toBe('ex-toggle2 ex-item2');
expect(button.props['aria-expanded']).toBe(false);
});

test('Toggle expanded', () => {
const view = mount(<AccordionToggle aria-label="Toggle details for" id="ex-toggle2" isExpanded />);
const view = mount(
<AccordionContext.Provider value={{ asDefinitionList: true }}>
<AccordionToggle aria-label="Toggle details for" id="ex-toggle2" isExpanded />
</AccordionContext.Provider>
);
const button = view.find('button[id="ex-toggle2"]').getElement();
expect(button.props['aria-expanded']).toBe(true);
expect(button.props.className).toContain('pf-m-expanded');
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import * as React from 'react';
import { css } from '@patternfly/react-styles';
import styles from '@patternfly/react-styles/css/components/Accordion/accordion';

export const AccordionContext = React.createContext('h3');
import {AccordionContext} from './AccordionContext';

export interface AccordionProps extends React.HTMLProps<HTMLDListElement> {
/** Content rendered inside the Accordion */
Expand All @@ -11,18 +10,26 @@ export interface AccordionProps extends React.HTMLProps<HTMLDListElement> {
className?: string;
/** Adds accessible text to the Accordion */
'aria-label'?: string;
/** the heading level to use */
/** Heading level to use */
headingLevel?: 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6';
/** Flag to indicate whether use definition list or div */
asDefinitionList?: boolean;
}

export const Accordion: React.FunctionComponent<AccordionProps> = ({
children = null,
className = '',
'aria-label': ariaLabel = '',
headingLevel = 'h3',
asDefinitionList = true,
...props
}: AccordionProps) => (
<dl className={css(styles.accordion, className)} aria-label={ariaLabel} {...props}>
<AccordionContext.Provider value={headingLevel}>{children}</AccordionContext.Provider>
</dl>
);
}: AccordionProps) => {
const AccordionList: any = asDefinitionList ? 'dl' : 'div';
return (
<AccordionList className={css(styles.accordion, className)} aria-label={ariaLabel} {...props}>
<AccordionContext.Provider value={{ AccordionHeadingLevel: headingLevel, asDefinitionList }}>
{children}
</AccordionContext.Provider>
</AccordionList>
);
};
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import * as React from 'react';
import { css } from '@patternfly/react-styles';
import styles from '@patternfly/react-styles/css/components/Accordion/accordion';
import { AccordionContext } from './AccordionContext';

export interface AccordionContentProps extends React.HTMLProps<HTMLElement> {
export interface AccordionContentProps extends React.HTMLProps<HTMLDivElement> {
/** Content rendered inside the Accordion */
children?: React.ReactNode;
/** Additional classes added to the Accordion content */
Expand All @@ -26,18 +27,25 @@ export const AccordionContent: React.FunctionComponent<AccordionContentProps> =
'aria-label': ariaLabel = '',
...props
}: AccordionContentProps) => (
<dd
id={id}
className={css(
styles.accordionExpandedContent,
isFixed && styles.modifiers.fixed,
!isHidden && styles.modifiers.expanded,
className
)}
hidden={isHidden}
aria-label={ariaLabel}
{...props}
>
<div className={css(styles.accordionExpandedContentBody)}>{children}</div>
</dd>
<AccordionContext.Consumer>
{({ asDefinitionList }) => {
const AccordionContentContainer = asDefinitionList ? 'dd' : 'div';
return (
<AccordionContentContainer
id={id}
className={css(
styles.accordionExpandedContent,
isFixed && styles.modifiers.fixed,
!isHidden && styles.modifiers.expanded,
className
)}
hidden={isHidden}
aria-label={ariaLabel}
{...props}
>
<div className={css(styles.accordionExpandedContentBody)}>{children}</div>
</AccordionContentContainer>
);
}}
</AccordionContext.Consumer>
);
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import * as React from 'react';

interface AccordionContextProps {
AccordionHeadingLevel: 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6';
asDefinitionList: boolean;
}

export const AccordionContext = React.createContext<Partial<AccordionContextProps>>({});
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { css } from '@patternfly/react-styles';
import styles from '@patternfly/react-styles/css/components/Accordion/accordion';
import { AngleRightIcon } from '@patternfly/react-icons';
import { Omit } from '../../helpers/typeUtils';
import { AccordionContext } from './Accordion';
import { AccordionContext } from './AccordionContext';

export interface AccordionToggleProps extends Omit<React.HTMLProps<HTMLButtonElement>, 'type'> {
/** Content rendered inside the Accordion toggle */
Expand All @@ -23,10 +23,11 @@ export const AccordionToggle: React.FunctionComponent<AccordionToggleProps> = ({
children = null,
...props
}: AccordionToggleProps) => (
<dt>
<AccordionContext.Consumer>
{(AccordionHeadingLevel: any) => (
<AccordionHeadingLevel>
<AccordionContext.Consumer>
{({ asDefinitionList, AccordionHeadingLevel }) => {
const AccordionToggleContainer = asDefinitionList ? 'dt' : AccordionHeadingLevel;
return (
<AccordionToggleContainer>
<button
id={id}
className={css(styles.accordionToggle, isExpanded && styles.modifiers.expanded, className)}
Expand All @@ -36,8 +37,8 @@ export const AccordionToggle: React.FunctionComponent<AccordionToggleProps> = ({
<span className={css(styles.accordionToggleText)}>{children}</span>
<AngleRightIcon className={css(styles.accordionToggleIcon)} />
</button>
</AccordionHeadingLevel>
)}
</AccordionContext.Consumer>
</dt>
</AccordionToggleContainer>
);
}}
</AccordionContext.Consumer>
);
Loading

0 comments on commit 9b46347

Please sign in to comment.