Restructure accordion so list items are in their own component

Accordion styles to get more BEM
This commit is contained in:
Paul Clarke 2016-04-26 17:45:51 +12:00 committed by Ingo Schommer
parent a7f5ef7b95
commit 3be4e80711
10 changed files with 181 additions and 186 deletions

View File

@ -4,7 +4,10 @@ import SilverStripeComponent from 'lib/SilverStripeComponent';
class Accordion extends SilverStripeComponent {
render() {
return (
<div role="tablist" aria-multiselectable="true">{this.props.children}</div>
<div className="accordion"
role="tablist"
aria-multiselectable="true"
>{this.props.children}</div>
);
}
}

View File

@ -1,53 +1,48 @@
// Accordion styles
.accordion-group {
// Accordion based off Bootstrap collapse
.accordion__block {
margin-top: $spacer-y * 1.25;
}
&__title {
margin-bottom: 0;
.accordion__title {
margin-bottom: 0;
margin-left: -$spacer-y;
margin-right: -$spacer-y;
padding: $spacer-x*.75 $spacer-y;
position: relative;
display: block;
font-size: $font-size-sm;
line-height: $line-height-base;
font-weight: 400;
color: $body-color;
text-transform: uppercase;
text-decoration: none;
border-bottom: 1px solid $border-color;
a {
/* Todo: extend table header */
font-size: $font-size-sm;
line-height: $line-height-base;
font-weight: 400;
text-transform: uppercase;
padding: $spacer-x*.75 $spacer-y;
/* end table header */
&::before {
padding: $spacer-x*.75 $spacer-y;
position: absolute;
right: 0;
top: 0;
font-size: $font-size-lg;
line-height: $line-height-base;
color: lighten($body-color,30);
text-align: center;
content: "7";
font-family: silverstripe;
}
display: block;
margin-left: -$spacer-y;
margin-right: -$spacer-y;
text-decoration: none;
position: relative;
border-bottom: 1px solid $border-color;
&:hover,
&:active,
&:focus {
text-decoration: none;
&::before {
color: $body-color;
&::before {
padding: $spacer-x*.75 $spacer-y;
position: absolute;
right: 0;
top: 0;
font-size: $font-size-lg;
line-height: $line-height-base;
color: lighten($body-color,30);
text-align: center;
content: "7";
font-family: silverstripe;
}
&.collapsed::before {
content: "6";
}
&:hover,
&:active,
&:focus {
text-decoration: none;
&::before {
color: $body-color;
}
}
}
}
}
.accordion__title.collapsed::before {
content: "6";
}

View File

@ -1,8 +1,10 @@
// TODO move list-group to its own component
import React from 'react';
import SilverStripeComponent from 'lib/SilverStripeComponent';
import 'bootstrap-collapse';
class AccordionGroup extends SilverStripeComponent {
class AccordionBlock extends SilverStripeComponent {
render() {
const headerID = `${this.props.groupid}_Header`;
const listID = `${this.props.groupid}_Items`;
@ -15,12 +17,16 @@ class AccordionGroup extends SilverStripeComponent {
'aria-labelledby': headerID,
};
return (
<div className="accordion-group">
<h6 className="accordion-group__title" role="tab" id={headerID}>
<a data-toggle="collapse" href={href} aria-expanded="true" aria-controls={listID}>
{this.props.title}
</a>
</h6>
<div className="accordion__block">
<a className="accordion__title"
data-toggle="collapse"
href={href}
aria-expanded="true"
aria-controls={listID}
id={headerID}
role="tab"
>{this.props.title}
</a>
<div {...groupProps}>
{this.props.children}
</div>
@ -28,4 +34,4 @@ class AccordionGroup extends SilverStripeComponent {
);
}
}
export default AccordionGroup;
export default AccordionBlock;

View File

@ -0,0 +1,3 @@
# Accordion
The ability to have panel(s) which can collapse and expand

View File

@ -0,0 +1,96 @@
// List-group, based on Bootstraps list-group
// TODO split out campaign specific css from generic list-group
.list-group {
margin-left: -$spacer-y;
margin-right: -$spacer-y;
border-bottom: 1px solid $border-color-light;
margin-bottom: $spacer-y*.75;
}
.list-group-item {
padding-left: $spacer-y;
padding-right: $spacer-y;
min-height: 64px;
cursor: pointer;
text-decoration: none;
&:first-child {
border-top: none;
}
&:hover {
text-decoration: none;
.list-group-item--has-links {
display: block;
opacity: 1;
}
}
&.active {
background-color: $brand-primary;
color: #FFF;
opacity: 1;
.list-group-item--has-links,
.list-group-item__status {
color: #FFF;
opacity: 1;
}
}
}
.list-group-item__heading {
font-weight: 400;
font-size: 14px;
margin: 2px 0 5px;
color: $body-color;
}
.list-group-item__thumbnail {
width: 64px;
height: 64px;
display: block;
background: #ccc;
float: left;
margin: -12px 12px 0 -$spacer-y;
}
// Show linked items
.list-group-item--has-links,
.list-group-item--is-linked {
color: $brand-primary;
float: right;
font-size: $font-size-sm;
position: absolute;
right: $spacer-y;
top: 27px;
opacity: 0;
transition: opacity .2s ease-in-out;
.font-icon-link {
font-size: 16px;
position: relative;
top: 3px;
margin-right: 1px;
}
}
.list-group-item--published {
opacity: .6;
transition: opacity .2s ease-in-out;
.list-group-item__status {
opacity: 0;
transition: opacity .2s ease-in-out;
}
&:hover {
opacity: 1;
.list-group-item__status {
display: inline-block;
opacity: 1;
}
}
}

View File

@ -1,7 +1,9 @@
// TODO move to its own component list-group
import React from 'react';
import SilverStripeComponent from 'lib/SilverStripeComponent';
class AccordionItem extends SilverStripeComponent {
class ListGroupItem extends SilverStripeComponent {
constructor(props) {
super(props);
@ -24,9 +26,9 @@ class AccordionItem extends SilverStripeComponent {
}
}
AccordionItem.propTypes = {
ListGroupItem.propTypes = {
handleClickArg: React.PropTypes.any,
handleClick: React.PropTypes.func,
};
export default AccordionItem;
export default ListGroupItem;

View File

@ -1,118 +1 @@
.campaign-items {
.list-group {
margin-left: -$spacer-y;
margin-right: -$spacer-y;
border-bottom: 1px solid $border-color-light;
margin-bottom: 0;
}
.list-group-item {
padding-left: $spacer-y;
padding-right: $spacer-y;
min-height: 64px;
cursor: pointer;
text-decoration: none;
&:first-child {
border-top: none;
}
&:hover,
&:focus {
text-decoration: none;
.list-group-item--has-links {
display: block;
opacity: 1;
}
}
&:focus {
outline-width: 1px;
outline-offset: -1px;
outline-style: solid;
outline-color: $brand-primary;
z-index: 1;
}
&.active {
background-color: $brand-primary;
color: #FFF;
opacity: 1;
.list-group-item--has-links,
.list-group-item__status {
color: #FFF;
opacity: 1;
}
// Focus color clashes with selected colour
&:focus {
outline-color: darken($brand-primary, 10%);
}
}
}
.list-group-item-heading {
font-weight: 400;
font-size: 14px;
margin: 2px 0 5px;
color: $body-color;
}
.list-group-item__thumbnail {
width: 64px;
height: 64px;
display: block;
background: #ccc;
float: left;
// See negate padding on bootstrap .list-group-item in _list-group.scss
margin: -12px 12px 0 (0 - $spacer-y);
}
.label {
text-transform: uppercase;
font-size: 10px;
font-weight: 400;
letter-spacing: .4px;
}
// Show linked items
.list-group-item--has-links,
.list-group-item--is-linked {
color: $brand-primary;
float: right;
font-size: $font-size-sm;
position: absolute;
right: $spacer-y;
top: 27px;
opacity: 0;
transition: opacity .2s ease-in-out;
.font-icon-link {
font-size: 16px;
position: relative;
top: 3px;
margin-right: 1px;
}
}
.list-group-item--published {
opacity: .6;
transition: opacity .2s ease-in-out;
.list-group-item__status {
opacity: 0;
transition: opacity .2s ease-in-out;
}
&:hover {
opacity: 1;
.list-group-item__status {
display: inline-block;
opacity: 1;
}
}
}
}

View File

@ -5,8 +5,8 @@ import * as recordActions from 'state/records/RecordsActions';
import * as campaignActions from 'state/campaign/CampaignActions';
import SilverStripeComponent from 'lib/SilverStripeComponent';
import Accordion from 'components/Accordion/Accordion';
import AccordionGroup from 'components/Accordion/AccordionGroup';
import AccordionItem from 'components/Accordion/AccordionItem';
import AccordionBlock from 'components/Accordion/AccordionBlock';
import ListGroupItem from 'components/ListGroup/ListGroupItem';
import Toolbar from 'components/Toolbar/Toolbar';
import FormAction from 'components/FormAction/FormAction';
import CampaignAdminItem from './CampaignAdminItem';
@ -49,12 +49,13 @@ class CampaignAdminList extends SilverStripeComponent {
const itemGroups = this.groupItemsForSet();
// Get items in this set
let accordionGroups = [];
let accordionBlocks = [];
Object.keys(itemGroups).forEach(className => {
const group = itemGroups[className];
const groupCount = group.items.length;
let accordionItems = [];
let listGroupItems = [];
let title = `${groupCount} ${groupCount === 1 ? group.singular : group.plural}`;
let groupid = `Set_${campaignId}_Group_${className}`;
@ -86,18 +87,23 @@ class CampaignAdminList extends SilverStripeComponent {
itemClassNames.push('active');
}
accordionItems.push(
<AccordionItem key={item.ID} className={itemClassNames.join(' ')} handleClick={this.handleItemSelected} handleClickArg={item.ID} >
listGroupItems.push(
<ListGroupItem
key={item.ID}
className={itemClassNames.join(' ')}
handleClick={this.handleItemSelected}
handleClickArg={item.ID}
>
<CampaignAdminItem item={item} campaign={this.props.record} />
</AccordionItem>
</ListGroupItem>
);
});
// Merge into group
accordionGroups.push(
<AccordionGroup key={groupid} groupid={groupid} title={title}>
{accordionItems}
</AccordionGroup>
accordionBlocks.push(
<AccordionBlock key={groupid} groupid={groupid} title={title}>
{listGroupItems}
</AccordionBlock>
);
});
@ -114,7 +120,7 @@ class CampaignAdminList extends SilverStripeComponent {
</Toolbar>
<div className="container-fluid campaign-items panel-scrollable--double-toolbar">
<Accordion>
{accordionGroups}
{accordionBlocks}
</Accordion>
</div>
<div className="toolbar--south">

View File

@ -28,6 +28,7 @@
// Components
@import "../components/Accordion/Accordion";
@import "../components/ListGroup/ListGroup";
@import "../components/Breadcrumb/Breadcrumb";
@import "../components/FormAction/FormAction";
@import "../components/GridField/GridField";