Skip to content

Commit 65bb39b

Browse files
authored
feat: migrates Container to TypeScript; Container without max-width on docs site [FC-0062] (#3216)
1 parent 41933fe commit 65bb39b

File tree

6 files changed

+86
-61
lines changed

6 files changed

+86
-61
lines changed

src/Container/Container.test.jsx src/Container/Container.test.tsx

+14-8
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import React from 'react';
22
import { render } from '@testing-library/react';
3-
import Container from '.';
3+
import Container, { type ContainerSize } from '.';
44

5-
const getClassNames = (container) => container.className.split(' ');
5+
const getClassNames = (container: HTMLElement) => container.className.split(' ');
66

77
describe('<Container />', () => {
88
it('displays children', () => {
@@ -12,32 +12,38 @@ describe('<Container />', () => {
1212

1313
it('adds the .container-fluid class', () => {
1414
const { container } = render(<Container>Content</Container>);
15-
const containerElement = container.firstChild;
15+
const containerElement = container.firstChild as HTMLElement;
1616
expect(getClassNames(containerElement)).toContain('container-fluid');
1717
});
1818

1919
it('adds the .container class', () => {
2020
const { container } = render(<Container fluid={false}>Content</Container>);
21-
const containerElement = container.firstChild;
22-
expect(getClassNames(containerElement)).toContain('container');
21+
const containerElement = container.firstChild as HTMLElement;
22+
expect(getClassNames(containerElement!)).toContain('container');
2323
expect(getClassNames(containerElement)).not.toContain('container-fluid');
2424
});
2525

26-
['xs', 'sm', 'md', 'lg', 'xl'].forEach((size) => {
26+
['xs', 'sm', 'md', 'lg', 'xl'].forEach((size: ContainerSize) => {
2727
it(`adds the .container-mw-${size} class`, () => {
2828
const { container } = render(<Container size={size}>Content</Container>);
29-
const containerElement = container.firstChild;
29+
const containerElement = container.firstChild as HTMLElement;
3030
expect(getClassNames(containerElement)).toContain(`container-mw-${size}`);
3131
});
3232
});
3333

34+
it('does not add a size class when size is not specified', () => {
35+
const { container } = render(<Container>Content</Container>);
36+
const containerElement = container.firstChild as HTMLElement;
37+
expect(getClassNames(containerElement)).toEqual(['container-fluid']);
38+
});
39+
3440
it('preserves custom class names', () => {
3541
const { container } = render(
3642
<Container className="custom-class" size="md">
3743
Content
3844
</Container>,
3945
);
40-
const containerElement = container.firstChild;
46+
const containerElement = container.firstChild as HTMLElement;
4147
expect(getClassNames(containerElement)).toContain('container-mw-md');
4248
expect(getClassNames(containerElement)).toContain('container-fluid');
4349
expect(getClassNames(containerElement)).toContain('custom-class');

src/Container/README.md

+4
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,10 @@ The base container to contain, pad, and center content in the viewport. This com
1919
```jsx live
2020
<div style={{ overflowX: 'auto' }}>
2121
<div style={{ width: '1500px', border: 'solid 3px red' }}>
22+
<Container className="bg-danger-300 my-4">
23+
The content in this container doesn't have a max width
24+
</Container>
25+
2226
<Container size="xl" className="bg-danger-300 my-4">
2327
The content in this container won't exceed the extra large width.
2428
</Container>

src/Container/index.jsx

-49
This file was deleted.

src/Container/index.tsx

+64
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
/* eslint-disable react/require-default-props */
2+
import React from 'react';
3+
import classNames from 'classnames';
4+
import PropTypes from 'prop-types';
5+
import RBContainer, { type ContainerProps as RBContainerProps } from 'react-bootstrap/Container';
6+
7+
import type { ComponentWithAsProp } from '../utils/types/bootstrap';
8+
9+
enum ContainerSizeClass {
10+
xs = 'container-mw-xs',
11+
sm = 'container-mw-sm',
12+
md = 'container-mw-md',
13+
lg = 'container-mw-lg',
14+
xl = 'container-mw-xl',
15+
}
16+
17+
export type ContainerSize = keyof typeof ContainerSizeClass;
18+
19+
interface ContainerProps extends RBContainerProps {
20+
size?: ContainerSize;
21+
}
22+
23+
type ContainerType = ComponentWithAsProp<'div', ContainerProps>;
24+
25+
const Container: ContainerType = React.forwardRef<Element, ContainerProps>(({
26+
size,
27+
children,
28+
...props
29+
}, ref) => (
30+
<RBContainer
31+
{...props}
32+
ref={ref}
33+
className={classNames(
34+
props.className,
35+
size && ContainerSizeClass[size],
36+
)}
37+
>
38+
{children}
39+
</RBContainer>
40+
));
41+
42+
Container.propTypes = {
43+
...RBContainer.propTypes,
44+
/** Override the base element */
45+
as: PropTypes.elementType,
46+
/** Specifies the contents of the container */
47+
children: PropTypes.node,
48+
/** Fill all available space at any breakpoint */
49+
fluid: PropTypes.bool,
50+
/** Set the maximum width for the container. Omiting the prop will remove the max-width */
51+
size: PropTypes.oneOf(['xs', 'sm', 'md', 'lg', 'xl']),
52+
/** Overrides underlying component base CSS class name */
53+
bsPrefix: PropTypes.string,
54+
};
55+
56+
Container.defaultProps = {
57+
as: 'div',
58+
children: undefined,
59+
fluid: true,
60+
size: undefined,
61+
bsPrefix: 'container',
62+
};
63+
64+
export default Container;

src/index.d.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ export { default as Bubble } from './Bubble';
88
export { default as Button, ButtonGroup, ButtonToolbar } from './Button';
99
export { default as Chip, CHIP_PGN_CLASS } from './Chip';
1010
export { default as ChipCarousel } from './ChipCarousel';
11+
export { default as Container, ContainerSize } from './Container';
1112
export { default as Hyperlink, HYPER_LINK_EXTERNAL_LINK_ALT_TEXT, HYPER_LINK_EXTERNAL_LINK_TITLE } from './Hyperlink';
1213
export { default as Icon } from './Icon';
1314
export { default as IconButton, IconButtonWithTooltip } from './IconButton';
@@ -41,7 +42,6 @@ export const
4142
export const CheckBox: any; // from './CheckBox';
4243
export const CheckBoxGroup: any; // from './CheckBoxGroup';
4344
export const CloseButton: any; // from './CloseButton';
44-
export const Container: any; // from './Container';
4545
export const Layout: any, Col: any, Row: any; // from './Layout';
4646
export const Collapse: any; // from './Collapse';
4747
export const Collapsible: any; // from './Collapsible';

www/src/context/SettingsContext.tsx

+3-3
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import React, { createContext, useState, useEffect } from 'react';
22
import PropTypes from 'prop-types';
33
import { Helmet } from 'react-helmet';
44
import { IntlProvider } from 'react-intl';
5-
import { messages } from '~paragon-react';
5+
import { messages, type ContainerSize } from '~paragon-react';
66

77
import { THEMES, DEFAULT_THEME } from '../../theme-config';
88
import { SETTINGS_EVENTS, sendUserAnalyticsEvent } from '../../segment-events';
@@ -12,7 +12,7 @@ export interface IDefaultValue {
1212
theme?: string,
1313
direction?: string,
1414
language?: string,
15-
containerWidth?: string,
15+
containerWidth?: ContainerSize,
1616
},
1717
theme?: string,
1818
handleSettingsChange: Function,
@@ -35,7 +35,7 @@ function SettingsContextProvider({ children }) {
3535
theme: DEFAULT_THEME,
3636
direction: 'ltr',
3737
language: 'en',
38-
containerWidth: 'md',
38+
containerWidth: 'md' as ContainerSize,
3939
});
4040
const [showSettings, setShowSettings] = useState(false);
4141

0 commit comments

Comments
 (0)