Improve props

Signed-off-by: Charles de Dreuille <charles.dedreuille@gmail.com>
This commit is contained in:
Charles de Dreuille
2025-03-18 15:33:17 +00:00
parent 403bb43d3d
commit a2d2ea4db0
11 changed files with 344 additions and 210 deletions
@@ -1,9 +1,10 @@
import { CodeBlock } from '@/components/CodeBlock';
import { PropsTable } from '@/components/PropsTable';
import { spacePropsList } from '@/utils/spaceProps';
import { Tabs } from '@/components/Tabs';
import { Snippet } from '@/components/Snippet';
import { BoxPreview } from '@/snippets/box';
import { boxPropDefs } from './props';
import { spacingPropDefs } from '../../../../utils/propDefs';
# Box
@@ -36,60 +37,13 @@ Box is the lowest-level component in Canon. It provides a consistent API for sty
This is the Box component, our lowest-level component. Here are all the
available properties.
<PropsTable
data={{
alignItems: {
type: ['stretch', 'start', 'center', 'end'],
responsive: true,
},
border: {
type: ['none', 'base', 'error', 'warning', 'selected'],
responsive: true,
},
borderRadius: {
type: ['none', '2xs', 'xs', 'sm', 'md', 'lg', 'xl', '2xl'],
responsive: true,
},
children: {
type: 'ReactNode',
responsive: false,
},
className: {
type: 'string',
responsive: false,
},
display: {
type: ['none', 'flex', 'block', 'inline'],
responsive: true,
},
flexDirection: {
type: ['row', 'column'],
responsive: true,
},
flexWrap: {
type: ['wrap', 'nowrap', 'wrap-reverse'],
responsive: true,
},
gap: {
type: ['2xs', 'xs', 'sm', 'md', 'lg', 'xl', '2xl', '3xl', '4xl', '5xl'],
responsive: true,
},
justifyContent: {
type: ['stretch', 'start', 'center', 'end', 'around', 'between'],
responsive: true,
},
style: {
type: 'CSSProperties',
responsive: false,
},
}}
/>
<PropsTable data={boxPropDefs} />
Padding and margin are used to create space around your component using our
predefined spacing tokens. We would recommend to use padding over margin to
avoid collapsing margins but both are available.
<PropsTable data={spacePropsList} />
<PropsTable data={spacingPropDefs} />
## Examples
@@ -0,0 +1,24 @@
import {
classNamePropDefs,
displayPropDefs,
heightPropDefs,
positionPropDefs,
stylePropDefs,
widthPropDefs,
} from '../../../../utils/propDefs';
import type { PropDef } from '../../../../utils/propDefs';
export const boxPropDefs: Record<string, PropDef> = {
as: {
type: 'enum',
values: ['div', 'span'],
default: 'div',
responsive: true,
},
...widthPropDefs,
...heightPropDefs,
...positionPropDefs,
...displayPropDefs,
...classNamePropDefs,
...stylePropDefs,
};
@@ -4,6 +4,7 @@ import { spacePropsList } from '@/utils/spaceProps';
import { Tabs } from '@/components/Tabs';
import { Snippet } from '@/components/Snippet';
import { ContainerPreview } from '@/snippets/container';
import { containerPropDefs } from './props';
# Container
@@ -32,46 +33,7 @@ content on the page.
## API reference
<PropsTable
data={{
children: {
type: 'ReactNode',
responsive: false,
},
className: {
type: 'string',
responsive: false,
},
marginY: {
type: spacePropsList.marginY.type,
responsive: spacePropsList.marginY.responsive,
},
marginBottom: {
type: spacePropsList.marginBottom.type,
responsive: spacePropsList.marginBottom.responsive,
},
marginTop: {
type: spacePropsList.marginTop.type,
responsive: spacePropsList.marginTop.responsive,
},
paddingY: {
type: spacePropsList.paddingY.type,
responsive: spacePropsList.paddingY.responsive,
},
paddingBottom: {
type: spacePropsList.paddingBottom.type,
responsive: spacePropsList.paddingBottom.responsive,
},
paddingTop: {
type: spacePropsList.paddingTop.type,
responsive: spacePropsList.paddingTop.responsive,
},
style: {
type: 'CSSProperties',
responsive: false,
},
}}
/>
<PropsTable data={containerPropDefs} />
## Examples
@@ -0,0 +1,12 @@
import {
classNamePropDefs,
stylePropDefs,
gapPropDefs,
} from '../../../../utils/propDefs';
import type { PropDef } from '../../../../utils/propDefs';
export const containerPropDefs: Record<string, PropDef> = {
...gapPropDefs,
...classNamePropDefs,
...stylePropDefs,
};
@@ -5,6 +5,8 @@ import { Tabs } from '@/components/Tabs';
import { grid } from '@/snippets/_snippets';
import { Snippet } from '@/components/Snippet';
import { GridPreview } from '@/snippets/grid';
import { spacingPropDefs } from '../../../../utils/propDefs';
import { gridPropDefs } from './props';
# Grid
@@ -38,34 +40,11 @@ This is the grid container component. It will help to define the number of
columns that will be used in the grid. You can also define the gap between the
columns. All values are responsive.
<PropsTable
data={{
columns: {
type: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 'auto'],
responsive: true,
},
gap: {
type: ['xs', 'sm', 'md', 'lg', 'xl'],
responsive: true,
},
children: {
type: 'ReactNode',
required: false,
},
className: {
type: 'string',
required: false,
},
style: {
type: 'CSSProperties',
required: false,
},
}}
/>
<PropsTable data={gridPropDefs} />
The grid component also accepts all the spacing props from the Box component.
<PropsTable data={spacePropsList} />
<PropsTable data={spacingPropDefs} />
### Grid.Item
@@ -0,0 +1,36 @@
import {
PropDef,
classNamePropDefs,
displayPropDefs,
stylePropDefs,
} from '../../../../utils/propDefs';
export const gridPropDefs: Record<string, PropDef> = {
columns: {
type: 'enum | string',
values: [
'1',
'2',
'3',
'4',
'5',
'6',
'7',
'8',
'9',
'10',
'11',
'12',
'auto',
],
responsive: true,
default: 'auto',
},
children: {
type: 'enum',
values: ['ReactNode'],
required: true,
},
...classNamePropDefs,
...stylePropDefs,
};
@@ -9,6 +9,7 @@ type PropData = {
values?: string | string[];
responsive?: boolean;
default?: string;
type?: string;
};
// Modify the PropsTable component to use the new type
@@ -17,57 +18,56 @@ export const PropsTable = <T extends Record<string, PropData>>({
}: {
data: T;
}) => {
const completeData = {
...data,
className: {
values: 'string',
responsive: false,
},
style: {
values: 'CSSProperties',
responsive: false,
},
};
if (!data) return null;
return (
<Table.Root>
<Table.Header>
<Table.HeaderRow>
<Table.HeaderCell>Prop</Table.HeaderCell>
<Table.HeaderCell>Type</Table.HeaderCell>
<Table.HeaderCell>Default</Table.HeaderCell>
<Table.HeaderCell>Responsive</Table.HeaderCell>
<Table.HeaderCell style={{ width: '16%' }}>Prop</Table.HeaderCell>
<Table.HeaderCell style={{ width: '56%' }}>Type</Table.HeaderCell>
<Table.HeaderCell style={{ width: '14%' }}>Default</Table.HeaderCell>
<Table.HeaderCell style={{ width: '14%' }}>
Responsive
</Table.HeaderCell>
</Table.HeaderRow>
</Table.Header>
<Table.Body>
{Object.keys(completeData).map(n => (
<Table.Row key={n}>
<Table.Cell>
<Chip head>{n}</Chip>
</Table.Cell>
<Table.Cell>
<div
style={{ display: 'flex', flexWrap: 'wrap', gap: '0.375rem' }}
>
{completeData[n].values === 'icon' ? (
Object.keys(icons).map(icon => <Chip key={icon}>{icon}</Chip>)
) : Array.isArray(completeData[n].values) ? (
completeData[n].values.map(t => <Chip key={t}>{t}</Chip>)
) : (
<Chip>{completeData[n].values}</Chip>
)}
</div>
</Table.Cell>
<Table.Cell>
<Chip>
{completeData[n].default ? completeData[n].default : '-'}
</Chip>
</Table.Cell>
<Table.Cell>
<Chip>{completeData[n].responsive ? 'Yes' : 'No'}</Chip>
</Table.Cell>
</Table.Row>
))}
{Object.keys(data).map(n => {
const enumValues =
data[n].values === 'icon'
? Object.keys(icons).map(icon => <Chip key={icon}>{icon}</Chip>)
: Array.isArray(data[n].values) &&
data[n].values.map(t => <Chip key={t}>{t}</Chip>);
return (
<Table.Row key={n}>
<Table.Cell style={{ width: '16%' }}>
<Chip head>{n}</Chip>
</Table.Cell>
<Table.Cell style={{ width: '56%' }}>
<div
style={{ display: 'flex', flexWrap: 'wrap', gap: '0.375rem' }}
>
{data[n].type === 'string' && <Chip>string</Chip>}
{data[n].type === 'enum' && enumValues}
{data[n].type === 'enum | string' && (
<>
{enumValues}
<Chip>string</Chip>
</>
)}
</div>
</Table.Cell>
<Table.Cell style={{ width: '14%' }}>
<Chip>{data[n].default ? data[n].default : '-'}</Chip>
</Table.Cell>
<Table.Cell style={{ width: '14%' }}>
<Chip>{data[n].responsive ? 'Yes' : 'No'}</Chip>
</Table.Cell>
</Table.Row>
);
})}
</Table.Body>
</Table.Root>
);
+23 -20
View File
@@ -1,19 +1,3 @@
/*
* Copyright 2024 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import React from 'react';
import styles from './styles.module.css';
@@ -37,9 +21,18 @@ export const HeaderRow = ({ children }: { children: React.ReactNode }) => {
return <tr>{children}</tr>;
};
export const HeaderCell = ({ children }: { children: React.ReactNode }) => {
export const HeaderCell = ({
children,
style,
}: {
children: React.ReactNode;
style?: React.CSSProperties;
}) => {
return (
<th className={`${styles.tableCell} ${styles.tableHeaderCell}`}>
<th
className={`${styles.tableCell} ${styles.tableHeaderCell}`}
style={style}
>
{children}
</th>
);
@@ -49,6 +42,16 @@ export const Row = ({ children }: { children: React.ReactNode }) => {
return <tr className={styles.tableRow}>{children}</tr>;
};
export const Cell = ({ children }: { children: React.ReactNode }) => {
return <td className={styles.tableCell}>{children}</td>;
export const Cell = ({
children,
style,
}: {
children: React.ReactNode;
style?: React.CSSProperties;
}) => {
return (
<td className={styles.tableCell} style={style}>
{children}
</td>
);
};
@@ -1,19 +1,3 @@
/*
* Copyright 2024 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
.wrapper {
border: 1px solid var(--border);
border-radius: 4px;
+196
View File
@@ -0,0 +1,196 @@
import type { Breakpoint } from '@backstage/canon/src/types';
const breakpoints = ['initial', 'xs', 'sm', 'md', 'lg', 'xl'] as Breakpoint[];
export type PropDef = {
type: 'string' | 'enum' | 'enum | string';
values?: string | string[];
default?: string;
required?: boolean;
responsive?: boolean;
};
export { breakpoints };
export type { Breakpoint };
export const spacingValues = [
'0.5',
'1',
'1.5',
'2',
'3',
'4',
'5',
'6',
'7',
'8',
'9',
'10',
'11',
'12',
'13',
'14',
];
export const paddingPropDefs = (
spacingValues: string[],
): Record<string, PropDef> => ({
p: {
type: 'enum | string',
values: spacingValues,
responsive: true,
},
px: {
type: 'enum | string',
values: spacingValues,
responsive: true,
},
py: {
type: 'enum | string',
values: spacingValues,
responsive: true,
},
pt: {
type: 'enum | string',
values: spacingValues,
responsive: true,
},
pr: {
type: 'enum | string',
values: spacingValues,
responsive: true,
},
pb: {
type: 'enum | string',
values: spacingValues,
responsive: true,
},
pl: {
type: 'enum | string',
values: spacingValues,
responsive: true,
},
});
export const marginPropDefs = (
spacingValues: string[],
): Record<string, PropDef> => ({
m: {
type: 'enum | string',
values: spacingValues,
responsive: true,
},
mx: {
type: 'enum | string',
values: spacingValues,
responsive: true,
},
my: {
type: 'enum | string',
values: spacingValues,
responsive: true,
},
mt: {
type: 'enum | string',
values: spacingValues,
responsive: true,
},
mr: {
type: 'enum | string',
values: spacingValues,
responsive: true,
},
mb: {
type: 'enum | string',
values: spacingValues,
responsive: true,
},
ml: {
type: 'enum | string',
values: spacingValues,
responsive: true,
},
});
export const spacingPropDefs = {
...paddingPropDefs(spacingValues),
...marginPropDefs(spacingValues),
};
export const displayPropDefs: Record<string, PropDef> = {
display: {
type: 'enum',
values: ['none', 'inline', 'inline-block', 'block'],
default: 'block',
responsive: true,
},
};
export const gapPropDefs: Record<string, PropDef> = {
gap: {
type: 'enum | string',
values: spacingValues,
responsive: true,
default: '4',
},
};
export const widthPropDefs: Record<string, PropDef> = {
width: {
type: 'string',
default: '0',
responsive: true,
},
minWidth: {
type: 'string',
default: '0',
responsive: true,
},
maxWidth: {
type: 'string',
default: '0',
responsive: true,
},
};
export const heightPropDefs: Record<string, PropDef> = {
height: {
type: 'string',
default: '0',
responsive: true,
},
minHeight: {
type: 'string',
default: '0',
responsive: true,
},
maxHeight: {
type: 'string',
default: '0',
responsive: true,
},
};
export const positionPropDefs: Record<string, PropDef> = {
position: {
type: 'enum',
values: ['static', 'relative', 'absolute', 'fixed', 'sticky'],
default: 'static',
responsive: true,
},
};
export const classNamePropDefs: Record<string, PropDef> = {
className: {
type: 'string',
responsive: false,
},
};
export const stylePropDefs: Record<string, PropDef> = {
style: {
type: 'enum',
values: ['CSSProperties'],
responsive: false,
},
};
-16
View File
@@ -1,19 +1,3 @@
/*
* Copyright 2024 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export const spacePropsList = [
'margin',
'marginBottom',