Skip to content

Commit 25f0b35

Browse files
authored
feat(grid): add polymorphicProps in CSSGrid and FlexGrid (#18810)
* feat(grid): add polymorphicProps in CSSGrid and FlexGrid * test: add test story for FlexGrid and Grid * fix: remove test case * test: add new test cases for FlexGrid and Grid * fix: keep PropTypes for JavaScript users
1 parent 22cd6ea commit 25f0b35

File tree

6 files changed

+175
-109
lines changed

6 files changed

+175
-109
lines changed

packages/react/__tests__/__snapshots__/PublicAPI-test.js.snap

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3837,6 +3837,7 @@ Map {
38373837
"render": [Function],
38383838
},
38393839
"FlexGrid" => Object {
3840+
"$$typeof": Symbol(react.forward_ref),
38403841
"propTypes": Object {
38413842
"as": Object {
38423843
"args": Array [
@@ -3867,6 +3868,7 @@ Map {
38673868
"type": "bool",
38683869
},
38693870
},
3871+
"render": [Function],
38703872
},
38713873
"FluidForm" => Object {
38723874
"propTypes": Object {

packages/react/src/components/Grid/CSSGrid.tsx

Lines changed: 90 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -9,73 +9,82 @@ import cx from 'classnames';
99
import PropTypes from 'prop-types';
1010
import React from 'react';
1111
import { usePrefix } from '../../internal/usePrefix';
12-
import { PolymorphicProps } from '../../types/common';
1312
import { GridSettings, useGridSettings } from './GridContext';
1413
import { GridComponent, GridProps } from './GridTypes';
15-
16-
function CSSGrid<T extends React.ElementType>({
17-
align,
18-
as: BaseComponent = 'div' as T,
19-
children,
20-
className: customClassName,
21-
condensed = false,
22-
fullWidth = false,
23-
narrow = false,
24-
...rest
25-
}: GridProps<T>) {
26-
const prefix = usePrefix();
27-
const { subgrid } = useGridSettings();
28-
let mode: SubgridMode = 'wide';
29-
if (narrow) {
30-
mode = 'narrow';
31-
} else if (condensed) {
32-
mode = 'condensed';
33-
}
34-
35-
if (subgrid) {
14+
import {
15+
PolymorphicComponentPropWithRef,
16+
PolymorphicRef,
17+
} from '../../internal/PolymorphicProps';
18+
19+
const CSSGrid = React.forwardRef(
20+
<T extends React.ElementType = 'div'>(
21+
{
22+
align,
23+
as,
24+
children,
25+
className: customClassName,
26+
condensed = false,
27+
fullWidth = false,
28+
narrow = false,
29+
...rest
30+
}: GridProps<T>,
31+
ref?: PolymorphicRef<T>
32+
) => {
33+
const prefix = usePrefix();
34+
const { subgrid } = useGridSettings();
35+
let mode: SubgridMode = 'wide';
36+
if (narrow) {
37+
mode = 'narrow';
38+
} else if (condensed) {
39+
mode = 'condensed';
40+
}
41+
42+
if (subgrid) {
43+
return (
44+
<GridSettings mode="css-grid" subgrid>
45+
<Subgrid
46+
ref={ref}
47+
as={as}
48+
className={customClassName}
49+
mode={mode}
50+
{...rest}>
51+
{children}
52+
</Subgrid>
53+
</GridSettings>
54+
);
55+
}
56+
57+
const className = cx(customClassName, {
58+
[`${prefix}--css-grid`]: true,
59+
[`${prefix}--css-grid--condensed`]: mode === 'condensed',
60+
[`${prefix}--css-grid--narrow`]: mode === 'narrow',
61+
[`${prefix}--css-grid--full-width`]: fullWidth,
62+
[`${prefix}--css-grid--start`]: align === 'start',
63+
[`${prefix}--css-grid--end`]: align === 'end',
64+
});
65+
66+
// cast as any to let TypeScript allow passing in attributes to base component
67+
const BaseComponent = as || 'div';
3668
return (
3769
<GridSettings mode="css-grid" subgrid>
38-
<Subgrid
39-
as={BaseComponent}
40-
className={customClassName}
41-
mode={mode}
42-
{...rest}>
70+
<BaseComponent className={className} ref={ref} {...rest}>
4371
{children}
44-
</Subgrid>
72+
</BaseComponent>
4573
</GridSettings>
4674
);
4775
}
48-
49-
const className = cx(customClassName, {
50-
[`${prefix}--css-grid`]: true,
51-
[`${prefix}--css-grid--condensed`]: mode === 'condensed',
52-
[`${prefix}--css-grid--narrow`]: mode === 'narrow',
53-
[`${prefix}--css-grid--full-width`]: fullWidth,
54-
[`${prefix}--css-grid--start`]: align === 'start',
55-
[`${prefix}--css-grid--end`]: align === 'end',
56-
});
57-
58-
// cast as any to let TypeScript allow passing in attributes to base component
59-
const BaseComponentAsAny: any = BaseComponent;
60-
return (
61-
<GridSettings mode="css-grid" subgrid>
62-
<BaseComponentAsAny className={className} {...rest}>
63-
{children}
64-
</BaseComponentAsAny>
65-
</GridSettings>
66-
);
67-
}
76+
) as GridComponent;
6877

6978
CSSGrid.propTypes = {
7079
/**
71-
* Specify grid alignment. Default is center
80+
* Provide a custom element to render instead of the default <div>
7281
*/
73-
align: PropTypes.oneOf(['start', 'center', 'end']),
82+
as: PropTypes.oneOfType([PropTypes.string, PropTypes.elementType]),
7483

7584
/**
76-
* Provide a custom element to render instead of the default <div>
85+
* Specify grid alignment. Default is center
7786
*/
78-
as: PropTypes.oneOfType([PropTypes.string, PropTypes.elementType]),
87+
align: PropTypes.oneOf(['start', 'center', 'end']),
7988

8089
/**
8190
* Pass in content that will be rendered within the `Grid`
@@ -124,28 +133,35 @@ interface SubgridBaseProps {
124133
mode?: SubgridMode;
125134
}
126135

127-
type SubgridProps = PolymorphicProps<any, SubgridBaseProps>;
128-
129-
const Subgrid = ({
130-
as: BaseComponent = 'div',
131-
className: customClassName,
132-
children,
133-
mode,
134-
...rest
135-
}: SubgridProps) => {
136-
const prefix = usePrefix();
137-
const className = cx(customClassName, {
138-
[`${prefix}--subgrid`]: true,
139-
[`${prefix}--subgrid--condensed`]: mode === 'condensed',
140-
[`${prefix}--subgrid--narrow`]: mode === 'narrow',
141-
[`${prefix}--subgrid--wide`]: mode === 'wide',
142-
});
143-
return (
144-
<BaseComponent {...rest} className={className}>
145-
{children}
146-
</BaseComponent>
147-
);
148-
};
136+
type SubgridProps<T extends React.ElementType = 'div'> =
137+
PolymorphicComponentPropWithRef<T, SubgridBaseProps>;
138+
139+
const Subgrid = React.forwardRef(
140+
<T extends React.ElementType = 'div'>(
141+
{
142+
as,
143+
className: customClassName,
144+
children,
145+
mode,
146+
...rest
147+
}: SubgridProps<T>,
148+
ref?: PolymorphicRef<T>
149+
) => {
150+
const prefix = usePrefix();
151+
const className = cx(customClassName, {
152+
[`${prefix}--subgrid`]: true,
153+
[`${prefix}--subgrid--condensed`]: mode === 'condensed',
154+
[`${prefix}--subgrid--narrow`]: mode === 'narrow',
155+
[`${prefix}--subgrid--wide`]: mode === 'wide',
156+
});
157+
const BaseComponent = as || 'div';
158+
return (
159+
<BaseComponent {...rest} ref={ref} className={className}>
160+
{children}
161+
</BaseComponent>
162+
);
163+
}
164+
);
149165

150166
Subgrid.propTypes = {
151167
/**

packages/react/src/components/Grid/FlexGrid.stories.js

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -418,6 +418,36 @@ export const Default = (args) => {
418418
);
419419
};
420420

421+
export const Test1 = (args) => {
422+
// Grab the style from here to see the visual example
423+
//https://github.com/carbon-design-system/carbon/blob/main/packages/react/src/components/Grid/FlexGrid.stories.scss
424+
function DemoContent({ children }) {
425+
return (
426+
<div className="outside">
427+
<div className="inside">{children}</div>
428+
</div>
429+
);
430+
}
431+
return (
432+
<FlexGrid {...args} as="section">
433+
<Row>
434+
<Column>
435+
<DemoContent>1/4</DemoContent>
436+
</Column>
437+
<Column>
438+
<DemoContent>1/4</DemoContent>
439+
</Column>
440+
<Column>
441+
<DemoContent>1/4</DemoContent>
442+
</Column>
443+
<Column>
444+
<DemoContent>1/4</DemoContent>
445+
</Column>
446+
</Row>
447+
</FlexGrid>
448+
);
449+
};
450+
421451
Default.args = {
422452
as: 'div',
423453
fullWidth: false,

packages/react/src/components/Grid/FlexGrid.tsx

Lines changed: 32 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -11,33 +11,39 @@ import React from 'react';
1111
import { usePrefix } from '../../internal/usePrefix';
1212
import { GridSettings } from './GridContext';
1313
import { GridComponent, GridProps } from './GridTypes';
14+
import { PolymorphicRef } from '../../internal/PolymorphicProps';
1415

15-
function FlexGrid<T extends React.ElementType>({
16-
as: BaseComponent = 'div' as T,
17-
condensed = false,
18-
narrow = false,
19-
fullWidth = false,
20-
className: containerClassName,
21-
children,
22-
...rest
23-
}: GridProps<T>) {
24-
const prefix = usePrefix();
25-
const className = cx(containerClassName, {
26-
[`${prefix}--grid`]: true,
27-
[`${prefix}--grid--condensed`]: condensed,
28-
[`${prefix}--grid--narrow`]: narrow,
29-
[`${prefix}--grid--full-width`]: fullWidth,
30-
});
31-
// cast as any to let TypeScript allow passing in attributes to base component
32-
const BaseComponentAsAny: any = BaseComponent;
33-
return (
34-
<GridSettings mode="flexbox" subgrid={false}>
35-
<BaseComponentAsAny className={className} {...rest}>
36-
{children}
37-
</BaseComponentAsAny>
38-
</GridSettings>
39-
);
40-
}
16+
const FlexGrid = React.forwardRef(
17+
<T extends React.ElementType = 'div'>(
18+
{
19+
as,
20+
condensed = false,
21+
narrow = false,
22+
fullWidth = false,
23+
className: containerClassName,
24+
children,
25+
...rest
26+
}: GridProps<T>,
27+
ref?: PolymorphicRef<T>
28+
) => {
29+
const prefix = usePrefix();
30+
const className = cx(containerClassName, {
31+
[`${prefix}--grid`]: true,
32+
[`${prefix}--grid--condensed`]: condensed,
33+
[`${prefix}--grid--narrow`]: narrow,
34+
[`${prefix}--grid--full-width`]: fullWidth,
35+
});
36+
// cast as any to let TypeScript allow passing in attributes to base component
37+
const BaseComponent = as || 'div';
38+
return (
39+
<GridSettings mode="flexbox" subgrid={false}>
40+
<BaseComponent className={className} ref={ref} {...rest}>
41+
{children}
42+
</BaseComponent>
43+
</GridSettings>
44+
);
45+
}
46+
);
4147

4248
FlexGrid.propTypes = {
4349
/**

packages/react/src/components/Grid/Grid.stories.js

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,19 @@ export const Condensed = () => {
111111
);
112112
};
113113

114+
export const Test2 = () => {
115+
// Grab the style from here to see the visual example
116+
// https://github.com/carbon-design-system/carbon/blob/main/packages/react/src/components/Grid/Grid.stories.scss
117+
return (
118+
<Grid condensed as="section">
119+
<Column sm={4} />
120+
<Column sm={4} />
121+
<Column sm={4} />
122+
<Column sm={4} />
123+
</Grid>
124+
);
125+
};
126+
114127
export const FullWidth = () => {
115128
// Grab the style from here to see the visual example
116129
// https://github.com/carbon-design-system/carbon/blob/main/packages/react/src/components/Grid/Grid.stories.scss

packages/react/src/components/Grid/GridTypes.ts

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
* LICENSE file in the root directory of this source tree.
66
*/
77

8-
import { PolymorphicProps } from '../../types/common';
8+
import { PolymorphicComponentPropWithRef } from '../../internal/PolymorphicProps';
99

1010
export interface GridBaseProps {
1111
/**
@@ -36,14 +36,13 @@ export interface GridBaseProps {
3636
narrow?: boolean;
3737
}
3838

39-
export type GridProps<T extends React.ElementType> = PolymorphicProps<
40-
T,
41-
GridBaseProps
42-
>;
39+
export type GridProps<T extends React.ElementType> =
40+
PolymorphicComponentPropWithRef<T, GridBaseProps>;
4341

4442
export interface GridComponent {
45-
<T extends React.ElementType>(
46-
props: GridProps<T>,
47-
context?: any
48-
): React.ReactElement<any, any> | null;
43+
<T extends React.ElementType = 'div'>(
44+
props: GridProps<T>
45+
): React.ReactElement | null;
46+
displayName?: string;
47+
propTypes?: React.WeakValidationMap<GridProps<any>>;
4948
}

0 commit comments

Comments
 (0)