Skip to content

Commit e0d87d3

Browse files
authored
feat(typography): add new Kbd component (#2041)
1 parent 6b6c5e7 commit e0d87d3

File tree

8 files changed

+195
-0
lines changed

8 files changed

+195
-0
lines changed
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import { Meta, ArgsTable, Canvas, Story, Markdown } from '@storybook/addon-docs';
2+
import { Kbd } from '@zendeskgarden/react-typography';
3+
import { KbdStory } from './stories/KbdStory';
4+
import README from '../README.md';
5+
6+
<Meta title="Packages/Typography/Kbd" component={Kbd} />
7+
8+
# API
9+
10+
<ArgsTable />
11+
12+
# Demo
13+
14+
<Canvas>
15+
<Story name="Kbd" args={{ children: '⌃ ⌥ /', size: 'inherit' }}>
16+
{args => <KbdStory {...args} />}
17+
</Story>
18+
</Canvas>
19+
20+
<Markdown>{README}</Markdown>
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
/**
2+
* Copyright Zendesk, Inc.
3+
*
4+
* Use of this source code is governed under the Apache License, Version 2.0
5+
* found at http://www.apache.org/licenses/LICENSE-2.0.
6+
*/
7+
8+
import React from 'react';
9+
import { StoryFn } from '@storybook/react';
10+
import { IKbdProps, Kbd } from '@zendeskgarden/react-typography';
11+
12+
interface IArgs extends IKbdProps {
13+
children: string;
14+
}
15+
16+
export const KbdStory: StoryFn<IArgs> = ({ children, ...args }) => (
17+
<>
18+
{children.split(' ').map((child, index) => (
19+
<>
20+
{index > 0 ? ' ' : ''}
21+
<Kbd key={index} {...args}>
22+
{child}
23+
</Kbd>
24+
</>
25+
))}
26+
</>
27+
);
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
/**
2+
* Copyright Zendesk, Inc.
3+
*
4+
* Use of this source code is governed under the Apache License, Version 2.0
5+
* found at http://www.apache.org/licenses/LICENSE-2.0.
6+
*/
7+
8+
import React from 'react';
9+
import { render, renderRtl } from 'garden-test-utils';
10+
import { Kbd } from './Kbd';
11+
12+
describe('Kbd', () => {
13+
it('renders the expected element', () => {
14+
const { container } = render(<Kbd />);
15+
16+
expect(container.firstChild!.nodeName).toBe('KBD');
17+
});
18+
19+
it('forces left-to-right text direction', () => {
20+
const { container } = renderRtl(<Kbd />);
21+
22+
expect(container.firstChild).toHaveStyleRule('direction', 'ltr');
23+
});
24+
25+
describe('size', () => {
26+
it('renders inherited size', () => {
27+
const { container } = render(<Kbd />);
28+
29+
expect(container.firstChild).not.toHaveStyleRule('font-size', 'inherit');
30+
});
31+
32+
it('renders small styling if provided', () => {
33+
const { container } = render(<Kbd size="small" />);
34+
35+
expect(container.firstChild).toHaveStyleRule('font-size', '11px');
36+
});
37+
38+
it('renders medium styling if provided', () => {
39+
const { container } = render(<Kbd size="medium" />);
40+
41+
expect(container.firstChild).toHaveStyleRule('font-size', '13px');
42+
});
43+
44+
it('renders large styling if provided', () => {
45+
const { container } = render(<Kbd size="large" />);
46+
47+
expect(container.firstChild).toHaveStyleRule('font-size', '17px');
48+
});
49+
});
50+
});
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
/**
2+
* Copyright Zendesk, Inc.
3+
*
4+
* Use of this source code is governed under the Apache License, Version 2.0
5+
* found at http://www.apache.org/licenses/LICENSE-2.0.
6+
*/
7+
8+
import React, { forwardRef } from 'react';
9+
import PropTypes from 'prop-types';
10+
import { StyledKbd } from '../styled';
11+
import { IKbdProps, INHERIT_SIZE } from '../types';
12+
13+
/**
14+
* @extends HTMLAttributes<HTMLElement>
15+
*/
16+
export const Kbd = forwardRef<HTMLElement, IKbdProps>(({ size = 'inherit', ...other }, ref) => (
17+
<StyledKbd $size={size} {...other} ref={ref} />
18+
));
19+
20+
Kbd.displayName = 'Kbd';
21+
22+
Kbd.propTypes = {
23+
size: PropTypes.oneOf(INHERIT_SIZE)
24+
};

packages/typography/src/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ export { Blockquote } from './elements/Blockquote';
1515
export { Code } from './elements/Code';
1616
export { CodeBlock } from './elements/CodeBlock';
1717
export { Ellipsis } from './elements/Ellipsis';
18+
export { Kbd } from './elements/Kbd';
1819
export { Paragraph } from './elements/Paragraph';
1920
export { OrderedList } from './elements/lists/OrderedList';
2021
export { UnorderedList } from './elements/lists/UnorderedList';
@@ -31,6 +32,7 @@ export type {
3132
ICodeProps,
3233
ICodeBlockProps,
3334
IEllipsisProps,
35+
IKbdProps,
3436
IParagraphProps,
3537
IOrderedListProps,
3638
IUnorderedListProps,
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
/**
2+
* Copyright Zendesk, Inc.
3+
*
4+
* Use of this source code is governed under the Apache License, Version 2.0
5+
* found at http://www.apache.org/licenses/LICENSE-2.0.
6+
*/
7+
8+
import styled, { css, DefaultTheme, ThemeProps } from 'styled-components';
9+
import { math, stripUnit } from 'polished';
10+
import { componentStyles } from '@zendeskgarden/react-theming';
11+
import { StyledCode } from './StyledCode';
12+
import { IKbdProps } from '../types';
13+
14+
const COMPONENT_ID = 'typography.code';
15+
16+
interface IStyledKbdProps extends ThemeProps<DefaultTheme> {
17+
$size?: IKbdProps['size'];
18+
}
19+
20+
const sizeStyles = ({ theme, $size }: IStyledKbdProps) => {
21+
let paddingHorizontal;
22+
let paddingVertical = '0';
23+
24+
switch ($size) {
25+
case 'small':
26+
paddingHorizontal = `${theme.space.base}px`;
27+
break;
28+
29+
case 'medium':
30+
paddingHorizontal = `${theme.space.base * 1.5}px`;
31+
break;
32+
33+
case 'large':
34+
paddingHorizontal = `${theme.space.base * 1.75}px`;
35+
break;
36+
37+
default:
38+
paddingHorizontal = `${stripUnit(math(`${theme.space.base * 1.5} / (${theme.fontSizes.md} - 1px)`))}em`;
39+
paddingVertical = '1.5px';
40+
break;
41+
}
42+
43+
const padding = `${paddingVertical} ${paddingHorizontal}`;
44+
45+
return css`
46+
&& {
47+
padding: ${padding};
48+
}
49+
`;
50+
};
51+
52+
/*
53+
* 1. Force left-to-right text direction
54+
*/
55+
export const StyledKbd = styled(StyledCode as 'kbd').attrs({
56+
'data-garden-id': COMPONENT_ID,
57+
'data-garden-version': PACKAGE_VERSION,
58+
as: 'kbd'
59+
})<IStyledKbdProps>`
60+
display: inline-block; /* [1] */
61+
direction: ltr; /* [1] */
62+
63+
${sizeStyles};
64+
65+
${componentStyles};
66+
`;

packages/typography/src/styled/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ export * from './StyledCodeBlockToken';
1414
export * from './StyledEllipsis';
1515
export * from './StyledFont';
1616
export * from './StyledIcon';
17+
export * from './StyledKbd';
1718
export * from './StyledList';
1819
export * from './StyledListItem';
1920
export * from './StyledParagraph';

packages/typography/src/types/index.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,11 @@ export interface IEllipsisProps extends HTMLAttributes<HTMLDivElement> {
9191
tag?: any;
9292
}
9393

94+
export interface IKbdProps extends HTMLAttributes<HTMLElement> {
95+
/** Adjusts the font size. By default font size is inherited from the surrounding text. */
96+
size?: (typeof INHERIT_SIZE)[number];
97+
}
98+
9499
export interface IParagraphProps extends HTMLAttributes<HTMLParagraphElement> {
95100
/** Controls the spacing between sibling paragraphs */
96101
size?: Size;

0 commit comments

Comments
 (0)