Skip to content
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
9 changes: 9 additions & 0 deletions packages/patternfly-4/react-core/src/components/Card/Card.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ export interface CardProps extends React.HTMLProps<HTMLElement> {
isHoverable?: boolean;
/** Modifies the card to include compact styling */
isCompact?: boolean;
/** Modifies the card to include selectable styling */
isSelectable?: boolean;
/** Modifies the card to include selected styling */
isSelected?: boolean;
}

export const Card: React.FunctionComponent<CardProps> = ({
Expand All @@ -21,6 +25,8 @@ export const Card: React.FunctionComponent<CardProps> = ({
component = 'article',
isHoverable = false,
isCompact = false,
isSelectable = false,
isSelected = false,
...props
}: CardProps) => {
const Component = component as any;
Expand All @@ -30,8 +36,11 @@ export const Card: React.FunctionComponent<CardProps> = ({
styles.card,
isHoverable && styles.modifiers.hoverable,
isCompact && styles.modifiers.compact,
isSelectable && styles.modifiers.selectable,
isSelected && isSelectable && styles.modifiers.selected,
className
)}
tabIndex={isSelectable ? '0' : undefined}
{...props}
>
{children}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,22 @@ test('card with isCompact applied ', () => {
const view = shallow(<Card isCompact />);
expect(view).toMatchSnapshot();
});

test('card with isSelectable applied ', () => {
const view = shallow(<Card isSelectable />);
expect(view.prop('className')).toMatch(/selectable/);
expect(view.prop('tabIndex')).toBe('0');
});

test('card with isSelectable and isSelected applied ', () => {
const view = shallow(<Card isSelectable isSelected />);
expect(view.prop('className')).toMatch(/selectable/);
expect(view.prop('className')).toMatch(/selected/);
expect(view.prop('tabIndex')).toBe('0');
});

test('card with only isSelected applied - not change', () => {
const view = shallow(<Card isSelected />);
expect(view.prop('className')).not.toMatch(/selected/);
expect(view.prop('tabIndex')).toBe(undefined);
});
Original file line number Diff line number Diff line change
Expand Up @@ -366,3 +366,91 @@ HoverableCard = () => (
</Card>
);
```

```js title=Selectable-and-selected
import React from 'react';
import { Card, CardHead, CardActions, CardHeader, CardBody, Dropdown, DropdownToggle, DropdownItem, DropdownSeparator, DropdownPosition, DropdownDirection, KebabToggle, } from '@patternfly/react-core';

class SelectableCard extends React.Component {
constructor(props) {
super(props);
this.state = {
selected: null
};
this.onKeyDown = event => {
if (event.target !== event.currentTarget) {
return;
}
if ([13, 32].includes(event.keyCode)) {
const newSelected = event.currentTarget.id === this.state.selected ? null : event.currentTarget.id
this.setState({
selected: newSelected
})
}
}
this.onClick = event => {
const newSelected = event.currentTarget.id === this.state.selected ? null : event.currentTarget.id
this.setState({
selected: newSelected
})
};
this.onToggle = (isOpen, event) => {
event.stopPropagation()
this.setState({
isOpen
});
};
this.onSelect = event => {
event.stopPropagation()
this.setState({
isOpen: !this.state.isOpen
});
};
}
render() {
const { selected, isOpen} = this.state
const dropdownItems = [
<DropdownItem key="link">Link</DropdownItem>,
<DropdownItem key="action" component="button">
Action
</DropdownItem>,
<DropdownItem key="disabled link" isDisabled>
Disabled Link
</DropdownItem>,
<DropdownItem key="disabled action" isDisabled component="button">
Disabled Action
</DropdownItem>,
<DropdownSeparator key="separator" />,
<DropdownItem key="separated link">Separated Link</DropdownItem>,
<DropdownItem key="separated action" component="button">
Separated Action
</DropdownItem>
];
return (
<>
<Card id="first-card" onKeyDown={this.onKeyDown} onClick={this.onClick} isSelectable isSelected={selected === 'first-card'}>
<CardHead>
<CardActions>
<Dropdown
onSelect={this.onSelect}
toggle={<KebabToggle onToggle={this.onToggle} />}
isOpen={isOpen}
isPlain
dropdownItems={dropdownItems}
position={'right'}
/>
</CardActions>
</CardHead>
<CardHeader>First card</CardHeader>
<CardBody>This is a selectable card. Click me to select me. Click again to deselect me.</CardBody>
</Card>
<br/>
<Card id="second-card" onKeyDown={this.onKeyDown} onClick={this.onClick} isSelectable isSelected={selected === 'second-card'}>
<CardHeader>Second card</CardHeader>
<CardBody>This is a selectable card. Click me to select me. Click again to deselect me.</CardBody>
</Card>
</>
);
}
}
```
2 changes: 1 addition & 1 deletion packages/patternfly-4/react-core/src/helpers/constants.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export const KEY_CODES = { ARROW_UP: 38, ARROW_DOWN: 40, ESCAPE_KEY: 27, TAB: 9, ENTER: 13, SPACE: ' ' };
export const KEY_CODES = { ARROW_UP: 38, ARROW_DOWN: 40, ESCAPE_KEY: 27, TAB: 9, ENTER: 13, SPACE: 32 };

export const SIDE = { RIGHT: 'right', LEFT: 'left', BOTH: 'both', NONE: 'none' };

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,14 @@ describe('Card Demo Test', () => {

it('Verify card is compact', () => {
cy.get('article')
.last()
.eq(1)
.should('have.class', 'pf-m-compact');
});

it('Verify card is selectable and selected', () => {
cy.get('article')
.last()
.should('have.class', 'pf-m-selected')
.should('have.class', 'pf-m-selectable');
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,12 @@ export class CardDemo extends React.Component {
<CardBody>Body</CardBody>
<CardFooter>Footer</CardFooter>
</Card>
<br></br>
<Card isSelectable isSelected>
<CardHeader>Header</CardHeader>
<CardBody>Body</CardBody>
<CardFooter>Footer</CardFooter>
</Card>
</React.Fragment>
);
}
Expand Down