Skip to content

Commit 959af4f

Browse files
authored
compact accordion and card (#66)
* feat: add compact compact accordion variant * v0.5.5 * Consolidate colors * Move font to 5 * WIP * v0.5.6 * Fix styles * v0.5.7 * Add className to card body * v0.5.8 * fix padding * v0.5.9 * Make variant optional * v0.5.10 * Add compact card * Add card story * Compact card sizing * v0.5.11 * Change colors * v0.5.12 * Add datatag support * v0.5.13 * data-testid support * data-testid support * v0.5.14
1 parent 85645b6 commit 959af4f

File tree

15 files changed

+427
-172
lines changed

15 files changed

+427
-172
lines changed

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{
2-
"version": "0.5.4",
2+
"version": "0.5.14",
33
"license": "MIT",
44
"main": "dist/index.js",
55
"typings": "dist/index.d.ts",
@@ -122,6 +122,7 @@
122122
"@react-stately/utils": "^3.2.0",
123123
"@react-stately/virtualizer": "^3.1.5",
124124
"@react-types/shared": "^3.5.0",
125+
"@storybook/addon-measure": "^6.5.10",
125126
"@types/react-transition-group": "^4.4.5",
126127
"clsx": "^1.1.1",
127128
"react-transition-group": "^4.4.5",

src/accordion/Accordion.tsx

Lines changed: 26 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,40 @@
11
import React, { useState, ReactNode } from 'react';
22
import { css } from '@emotion/core';
3-
import { Heading, Text } from '../content';
3+
import { Heading } from '../content';
44
import { Icon, ArrowIosDownwardOutline } from '../icon';
55
import { classNames } from '../utils/classNames';
66
import theme from '../theme';
77

88
export interface AccordionProps {
99
children: ReactNode;
10+
/**
11+
* The variant of the accordion (e.g. sizing)
12+
* @default "default"
13+
*/
14+
variant?: 'default' | 'compact';
1015
}
1116

1217
/**
1318
* Accordion component for having collapsible sections
1419
* @see https://www.w3.org/TR/wai-aria-practices-1.1/#accordion
1520
*/
16-
export function Accordion({ children }: AccordionProps) {
21+
export function Accordion({ children, variant = 'default' }: AccordionProps) {
1722
return (
1823
<div
19-
className="ac-accordion"
24+
className={`ac-accordion ac-accordion--${variant}`}
2025
role="region"
2126
css={css`
22-
background-color: ${theme.components.accordion.backgroundColor};
27+
2328
--accordion-animation-duration: ${theme.animation.global.duration}ms;
29+
&.ac-accordion--default {
30+
--accordion-padding-top: ${theme.spacing.padding16}px;
31+
--accordion-padding-side: ${theme.spacing.padding16}px;
32+
--accordion-font-size: ${theme.typography.sizes.large.fontSize}px;
33+
}
34+
&.ac-accordion--compact {
35+
--accordion-padding-top: ${theme.spacing.padding8}px;
36+
--accordion-padding-side: ${theme.spacing.padding16}px;
37+
--accordion-font-size: ${theme.typography.sizes.medium.fontSize}px;
2438
`}
2539
>
2640
{children}
@@ -56,11 +70,11 @@ export function AccordionItem(props: AccordionItemProps) {
5670
flex-direction: row;
5771
`}
5872
>
59-
<Text textSize="large">{title}</Text>
73+
<span className="ac-accordion-item__title">{title}</span>
6074
{titleExtra}
6175
</div>
6276
) : (
63-
<Text textSize="large">{title}</Text>
77+
<span className="ac-accordion-item__title">{title}</span>
6478
);
6579

6680
return (
@@ -82,7 +96,7 @@ export function AccordionItem(props: AccordionItemProps) {
8296
id={headerId}
8397
css={css`
8498
cursor: pointer;
85-
padding: 16px 16px;
99+
padding: var(--accordion-padding-top) var(--accordion-padding-side);
86100
display: block;
87101
width: 100%;
88102
display: flex;
@@ -95,13 +109,17 @@ export function AccordionItem(props: AccordionItemProps) {
95109
border: 0;
96110
text-align: start;
97111
color: ${theme.textColors.white90};
98-
border-bottom: 1px solid ${theme.colors.dividerColor};
112+
border-bottom: 1px solid ${theme.components.accordion.borderColor};
99113
/* remove outline - TODO might need to give a visual cue that this area is in focus */
100114
outline: none;
115+
background-color: ${theme.components.accordion.backgroundColor};
101116
transition: background-color 0.2s ease-in-out;
102117
&:hover {
103118
background-color: ${theme.colors.hoverBgColor};
104119
}
120+
.ac-accordion-item__title {
121+
font-size: var(--accordion-font-size);
122+
}
105123
`}
106124
onClick={() => {
107125
setIsOpen(!isOpen);

src/button/ButtonGroup.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import React, {
55
cloneElement,
66
ReactNode,
77
HTMLAttributes,
8+
ReactElement,
89
} from 'react';
910
import theme from '../theme';
1011
import { ButtonProps } from './Button';
@@ -42,7 +43,7 @@ export function ButtonGroup({ children, size, ...divProps }: ButtonGroupProps) {
4243
>
4344
{Children.map(children, child => {
4445
if (isValidElement(child)) {
45-
return cloneElement(child, {
46+
return cloneElement(child as ReactElement<ButtonProps>, {
4647
size,
4748
});
4849
}

src/card/Card.tsx

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import theme from '../theme';
66
import { cardCSS, headerCSS, collapsibleCardCSS } from './styles';
77
import { classNames } from '../utils';
88
import { useId } from '@react-aria/utils';
9+
import { CardVariant } from './types';
910

1011
const headerTitleWrapCSS = css`
1112
flex: 1 1 auto;
@@ -28,24 +29,31 @@ const bodyCSS = css`
2829
padding: ${theme.spacing.padding16}px;
2930
`;
3031

31-
export type CardProps = {
32+
export interface CardBaseProps {
3233
title?: string;
3334
subTitle?: string;
35+
variant?: CardVariant;
3436
children: ReactNode;
3537
style?: CSSProperties;
3638
bodyStyle?: CSSProperties;
3739
extra?: ReactNode; // Extra controls on the header
3840
className?: string;
3941
titleExtra?: ReactNode;
42+
id?: string;
43+
}
44+
45+
interface CollapsibleCardProps {
4046
collapsible?: boolean;
4147
defaultOpen?: boolean;
4248
onToggle?: (open: boolean) => void;
43-
id?: string;
44-
};
49+
}
50+
51+
export interface CardProps extends CardBaseProps, CollapsibleCardProps {}
4552

4653
export function Card({
4754
title,
4855
subTitle,
56+
variant = 'default',
4957
children,
5058
style,
5159
bodyStyle,
@@ -60,8 +68,10 @@ export function Card({
6068
const idPrefix = useId(id);
6169
const contentId = `${idPrefix}-content`,
6270
headerId = `${idPrefix}-heading`;
71+
const titleSize = variant === 'default' ? 'xlarge' : 'large';
72+
const subTitleSize = variant === 'default' ? 'medium' : 'xsmall';
6373
const defaultTitle = (
64-
<Text textSize="xlarge" elementType="h3" weight="heavy">
74+
<Text textSize={titleSize} elementType="h3" weight="heavy">
6575
{title}
6676
</Text>
6777
);
@@ -76,14 +86,15 @@ export function Card({
7686
);
7787
const subTitleEl =
7888
subTitle != null ? (
79-
<Text textSize="medium" elementType="h4" color="white70">
89+
<Text textSize={subTitleSize} elementType="h4" color="white70">
8090
{subTitle}
8191
</Text>
8292
) : (
8393
undefined
8494
);
95+
const titleClassName = `ac-card__title-wrap ac-card__title-wrap--${variant}`;
8596
const titleComponent = collapsible ? (
86-
<div css={headerTitleWrapCSS}>
97+
<div css={headerTitleWrapCSS} className={titleClassName}>
8798
<CollapsibleCardTitle
8899
isOpen={isOpen}
89100
onOpen={() => setIsOpen(!isOpen)}
@@ -95,7 +106,7 @@ export function Card({
95106
/>
96107
</div>
97108
) : (
98-
<div css={headerTitleWrapCSS}>
109+
<div css={headerTitleWrapCSS} className={titleClassName}>
99110
{titleEl}
100111
{subTitleEl}
101112
</div>
@@ -104,7 +115,7 @@ export function Card({
104115
<section
105116
css={collapsible ? collapsibleCardCSS : cardCSS}
106117
style={style}
107-
className={classNames('ac-card', className, {
118+
className={classNames('ac-card', `ac-card--${variant}`, className, {
108119
'is-open': isOpen,
109120
})}
110121
>
@@ -116,9 +127,10 @@ export function Card({
116127
css={css(
117128
bodyCSS,
118129
css`
119-
${!isOpen && `display: none;`}
130+
${!isOpen && `display: none !important;`}
120131
`
121132
)}
133+
className="ac-card__body"
122134
style={bodyStyle}
123135
id={contentId}
124136
aria-labelledby={headerId}

src/card/TabbedCard.tsx

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
import React, { ReactNode, HTMLProps } from 'react';
1+
import React from 'react';
2+
import { CardBaseProps } from './Card';
23
import { css } from '@emotion/core';
34
import { Text } from '../content';
45
import { cardCSS, headerCSS } from './styles';
@@ -11,26 +12,20 @@ const tabbedCardCSS = css`
1112
}
1213
`;
1314

14-
export interface TabbedCardProps extends HTMLProps<HTMLElement> {
15-
title?: string;
16-
children: ReactNode;
17-
extra?: ReactNode; // Extra controls on the header
18-
}
15+
export interface TabbedCardProps extends CardBaseProps {}
1916

2017
export function TabbedCard(props: TabbedCardProps) {
21-
const { title, children, extra, ...restProps } = props;
18+
const { title, children, extra, variant = 'default', ...restProps } = props;
2219
const hasTitle = title != null;
2320
return (
2421
<section
2522
css={css(cardCSS, tabbedCardCSS)}
26-
className="ac-card ac-card--tabbed"
23+
className={`ac-card ac-card--${variant} ac-card--tabbed`}
2724
data-has-title={hasTitle}
2825
{...restProps}
2926
>
3027
{hasTitle ? (
31-
<header
32-
css={headerCSS({ bordered: false, height: 60, collapsible: false })}
33-
>
28+
<header css={headerCSS({ bordered: false, collapsible: false })}>
3429
<Text textSize="xlarge" elementType="h3" weight="heavy">
3530
{title}
3631
</Text>

src/card/styles.ts

Lines changed: 31 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,45 +1,51 @@
11
import { css } from '@emotion/core';
22
import theme from '../theme';
3-
const cardHeaderHeight = 68;
43

54
export const cardCSS = css`
65
display: flex;
76
flex-direction: column;
8-
background-color: ${theme.components.card.bgColor};
7+
background-color: ${theme.components.card.backgroundColor};
98
color: ${theme.textColors.white90};
109
border-radius: 8px;
11-
border: 1px solid ${theme.colors.dividerColor};
10+
border: 1px solid ${theme.components.card.borderColor};
1211
overflow: hidden;
12+
/* variant variables */
13+
&.ac-card--default {
14+
--card-header-height: 68px;
15+
}
16+
&.ac-card--compact {
17+
--card-header-height: 46px;
18+
}
1319
`;
1420

1521
const headerBorderCSS = css`
16-
border-bottom: 1px solid ${theme.colors.gray500};
22+
border-bottom: 1px solid ${theme.components.card.borderColor};
1723
`;
1824

1925
export const headerCSS = ({
2026
bordered,
2127
collapsible,
22-
height = cardHeaderHeight,
2328
}: {
2429
bordered: boolean;
2530
collapsible: boolean;
26-
height?: number;
27-
}) => css`
28-
display: flex;
29-
flex-direction: row;
30-
flex: none;
31-
justify-content: space-between;
32-
align-items: center;
33-
padding: 0 16px;
34-
height: ${height}px;
35-
transition: background-color 0.2s ease-in-out;
36-
&:hover {
37-
background-color: ${collapsible
38-
? theme.colors.hoverBgColor
39-
: 'transparent'};
40-
}
41-
${bordered ? headerBorderCSS : ''}
42-
`;
31+
}) => {
32+
return css`
33+
display: flex;
34+
flex-direction: row;
35+
flex: none;
36+
justify-content: space-between;
37+
align-items: center;
38+
padding: 0 16px;
39+
height: var(--card-header-height);
40+
transition: background-color 0.2s ease-in-out;
41+
&:hover {
42+
background-color: ${collapsible
43+
? theme.colors.hoverBgColor
44+
: 'transparent'};
45+
}
46+
${bordered ? headerBorderCSS : ''}
47+
`;
48+
};
4349

4450
export const collapsibleCardCSS = css`
4551
${cardCSS}
@@ -66,9 +72,10 @@ export const collapsibleCardCSS = css`
6672
transform: rotate(0deg);
6773
}
6874
}
69-
/* shrink the height to the card title so the body is hidden*/
75+
/* shrink the height to the card title so the body is hidden */
7076
&:not(.is-open) {
71-
height: ${cardHeaderHeight}px !important;
77+
height: var(--card-header-height) !important;
78+
overflow: hidden;
7279
}
7380
7481
--collapsible-card-animation-duration: ${theme.animation.global.duration}ms;

src/card/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export type CardVariant = 'default' | 'compact';

src/search/CompactSearchField.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ export function CompactSearchField(props: CompactSearchFieldProps) {
3737
border-radius: ${theme.rounding.rounding4}px;
3838
overflow: hidden;
3939
transition: all 0.2s ease-in-out;
40+
background-color: ${theme.components.textField.backgroundColor};
4041
& > .ac-button {
4142
flex: none;
4243
border: none;

0 commit comments

Comments
 (0)