Grafana UI: Add `columnGap` + `rowGap` to `Stack`/`Grid` (#102883)

add columnGap/rowGap to Stack/Grid
pull/102890/head
Ashley Harrison 4 months ago committed by GitHub
parent eff2da96d0
commit 0f0519eae5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 11
      packages/grafana-ui/src/components/Layout/Grid/Grid.story.tsx
  2. 14
      packages/grafana-ui/src/components/Layout/Grid/Grid.tsx
  3. 6
      packages/grafana-ui/src/components/Layout/Stack/Stack.story.tsx
  4. 27
      packages/grafana-ui/src/components/Layout/Stack/Stack.tsx

@ -1,6 +1,7 @@
import { Meta, StoryFn } from '@storybook/react'; import { Meta, StoryFn } from '@storybook/react';
import { useTheme2 } from '../../../themes'; import { useTheme2 } from '../../../themes';
import { SpacingTokenControl } from '../../../utils/storybook/themeStorybookControls';
import { Grid } from './Grid'; import { Grid } from './Grid';
import mdx from './Grid.mdx'; import mdx from './Grid.mdx';
@ -39,6 +40,9 @@ ColumnsNumber.argTypes = {
control: 'select', control: 'select',
options: ['stretch', 'flex-start', 'flex-end', 'center', 'baseline', 'start', 'end', 'self-start', 'self-end'], options: ['stretch', 'flex-start', 'flex-end', 'center', 'baseline', 'start', 'end', 'self-start', 'self-end'],
}, },
gap: SpacingTokenControl,
rowGap: SpacingTokenControl,
columnGap: SpacingTokenControl,
}; };
ColumnsNumber.args = { ColumnsNumber.args = {
columns: 3, columns: 3,
@ -52,7 +56,7 @@ ColumnsNumber.parameters = {
export const ColumnsMinWidth: StoryFn<typeof Grid> = (args) => { export const ColumnsMinWidth: StoryFn<typeof Grid> = (args) => {
const theme = useTheme2(); const theme = useTheme2();
return ( return (
<Grid gap={args.gap} minColumnWidth={args.minColumnWidth}> <Grid {...args}>
{Array.from({ length: 9 }).map((_, i) => ( {Array.from({ length: 9 }).map((_, i) => (
<div key={i} style={{ background: theme.colors.background.secondary, textAlign: 'center' }}> <div key={i} style={{ background: theme.colors.background.secondary, textAlign: 'center' }}>
N# {i} N# {i}
@ -61,6 +65,11 @@ export const ColumnsMinWidth: StoryFn<typeof Grid> = (args) => {
</Grid> </Grid>
); );
}; };
ColumnsMinWidth.argTypes = {
gap: SpacingTokenControl,
rowGap: SpacingTokenControl,
columnGap: SpacingTokenControl,
};
ColumnsMinWidth.args = { ColumnsMinWidth.args = {
minColumnWidth: 21, minColumnWidth: 21,
}; };

@ -12,6 +12,8 @@ interface GridPropsBase extends Omit<HTMLAttributes<HTMLDivElement>, 'className'
children: NonNullable<React.ReactNode>; children: NonNullable<React.ReactNode>;
/** Specifies the gutters between columns and rows. It is overwritten when a column or row gap has a value. */ /** Specifies the gutters between columns and rows. It is overwritten when a column or row gap has a value. */
gap?: ResponsiveProp<ThemeSpacingTokens>; gap?: ResponsiveProp<ThemeSpacingTokens>;
rowGap?: ResponsiveProp<ThemeSpacingTokens>;
columnGap?: ResponsiveProp<ThemeSpacingTokens>;
alignItems?: ResponsiveProp<AlignItems>; alignItems?: ResponsiveProp<AlignItems>;
} }
@ -33,8 +35,8 @@ interface PropsWithMinColumnWidth extends GridPropsBase {
type GridProps = PropsWithColumns | PropsWithMinColumnWidth; type GridProps = PropsWithColumns | PropsWithMinColumnWidth;
export const Grid = forwardRef<HTMLDivElement, GridProps>((props, ref) => { export const Grid = forwardRef<HTMLDivElement, GridProps>((props, ref) => {
const { alignItems, children, gap, columns, minColumnWidth, ...rest } = props; const { alignItems, children, gap, rowGap, columnGap, columns, minColumnWidth, ...rest } = props;
const styles = useStyles2(getGridStyles, gap, columns, minColumnWidth, alignItems); const styles = useStyles2(getGridStyles, gap, rowGap, columnGap, columns, minColumnWidth, alignItems);
return ( return (
<div ref={ref} {...rest} className={styles.grid}> <div ref={ref} {...rest} className={styles.grid}>
@ -48,6 +50,8 @@ Grid.displayName = 'Grid';
const getGridStyles = ( const getGridStyles = (
theme: GrafanaTheme2, theme: GrafanaTheme2,
gap: GridProps['gap'], gap: GridProps['gap'],
rowGap: GridProps['rowGap'],
columnGap: GridProps['columnGap'],
columns: GridProps['columns'], columns: GridProps['columns'],
minColumnWidth: GridProps['minColumnWidth'], minColumnWidth: GridProps['minColumnWidth'],
alignItems: GridProps['alignItems'] alignItems: GridProps['alignItems']
@ -58,6 +62,12 @@ const getGridStyles = (
getResponsiveStyle(theme, gap, (val) => ({ getResponsiveStyle(theme, gap, (val) => ({
gap: theme.spacing(val), gap: theme.spacing(val),
})), })),
getResponsiveStyle(theme, rowGap, (val) => ({
rowGap: theme.spacing(val),
})),
getResponsiveStyle(theme, columnGap, (val) => ({
columnGap: theme.spacing(val),
})),
minColumnWidth && minColumnWidth &&
getResponsiveStyle(theme, minColumnWidth, (val) => ({ getResponsiveStyle(theme, minColumnWidth, (val) => ({
gridTemplateColumns: `repeat(auto-fill, minmax(${theme.spacing(val)}, 1fr))`, gridTemplateColumns: `repeat(auto-fill, minmax(${theme.spacing(val)}, 1fr))`,

@ -36,11 +36,11 @@ const meta: Meta<typeof Stack> = {
}, },
}; };
export const Basic: StoryFn<typeof Stack> = ({ direction, wrap, alignItems, justifyContent, gap }) => { export const Basic: StoryFn<typeof Stack> = (args) => {
const theme = useTheme2(); const theme = useTheme2();
return ( return (
<div style={{ width: '600px', height: '600px', border: '1px solid grey' }}> <div style={{ width: '600px', height: '600px', border: '1px solid grey' }}>
<Stack direction={direction} wrap={wrap} alignItems={alignItems} justifyContent={justifyContent} gap={gap}> <Stack {...args}>
{Array.from({ length: 5 }).map((_, i) => ( {Array.from({ length: 5 }).map((_, i) => (
<Item key={i} color={theme.colors.warning.main} text={i + 1} /> <Item key={i} color={theme.colors.warning.main} text={i + 1} />
))} ))}
@ -51,6 +51,8 @@ export const Basic: StoryFn<typeof Stack> = ({ direction, wrap, alignItems, just
Basic.argTypes = { Basic.argTypes = {
gap: SpacingTokenControl, gap: SpacingTokenControl,
rowGap: SpacingTokenControl,
columnGap: SpacingTokenControl,
direction: { control: 'select', options: ['row', 'row-reverse', 'column', 'column-reverse'] }, direction: { control: 'select', options: ['row', 'row-reverse', 'column', 'column-reverse'] },
wrap: { control: 'select', options: ['nowrap', 'wrap', 'wrap-reverse'] }, wrap: { control: 'select', options: ['nowrap', 'wrap', 'wrap-reverse'] },
alignItems: { alignItems: {

@ -10,6 +10,8 @@ import { getSizeStyles, SizeProps } from '../utils/styles';
interface StackProps extends FlexProps, SizeProps, Omit<React.HTMLAttributes<HTMLElement>, 'className' | 'style'> { interface StackProps extends FlexProps, SizeProps, Omit<React.HTMLAttributes<HTMLElement>, 'className' | 'style'> {
gap?: ResponsiveProp<ThemeSpacingTokens>; gap?: ResponsiveProp<ThemeSpacingTokens>;
rowGap?: ResponsiveProp<ThemeSpacingTokens>;
columnGap?: ResponsiveProp<ThemeSpacingTokens>;
alignItems?: ResponsiveProp<AlignItems>; alignItems?: ResponsiveProp<AlignItems>;
justifyContent?: ResponsiveProp<JustifyContent>; justifyContent?: ResponsiveProp<JustifyContent>;
direction?: ResponsiveProp<Direction>; direction?: ResponsiveProp<Direction>;
@ -20,6 +22,8 @@ interface StackProps extends FlexProps, SizeProps, Omit<React.HTMLAttributes<HTM
export const Stack = React.forwardRef<HTMLDivElement, StackProps>((props, ref) => { export const Stack = React.forwardRef<HTMLDivElement, StackProps>((props, ref) => {
const { const {
gap = 1, gap = 1,
rowGap,
columnGap,
alignItems, alignItems,
justifyContent, justifyContent,
direction, direction,
@ -37,7 +41,20 @@ export const Stack = React.forwardRef<HTMLDivElement, StackProps>((props, ref) =
maxHeight, maxHeight,
...rest ...rest
} = props; } = props;
const styles = useStyles2(getStyles, gap, alignItems, justifyContent, direction, wrap, grow, shrink, basis, flex); const styles = useStyles2(
getStyles,
gap,
rowGap,
columnGap,
alignItems,
justifyContent,
direction,
wrap,
grow,
shrink,
basis,
flex
);
const sizeStyles = useStyles2(getSizeStyles, width, minWidth, maxWidth, height, minHeight, maxHeight); const sizeStyles = useStyles2(getSizeStyles, width, minWidth, maxWidth, height, minHeight, maxHeight);
return ( return (
<div ref={ref} className={cx(styles.flex, sizeStyles)} {...rest}> <div ref={ref} className={cx(styles.flex, sizeStyles)} {...rest}>
@ -51,6 +68,8 @@ Stack.displayName = 'Stack';
const getStyles = ( const getStyles = (
theme: GrafanaTheme2, theme: GrafanaTheme2,
gap: StackProps['gap'], gap: StackProps['gap'],
rowGap: StackProps['rowGap'],
columnGap: StackProps['columnGap'],
alignItems: StackProps['alignItems'], alignItems: StackProps['alignItems'],
justifyContent: StackProps['justifyContent'], justifyContent: StackProps['justifyContent'],
direction: StackProps['direction'], direction: StackProps['direction'],
@ -80,6 +99,12 @@ const getStyles = (
getResponsiveStyle(theme, gap, (val) => ({ getResponsiveStyle(theme, gap, (val) => ({
gap: theme.spacing(val), gap: theme.spacing(val),
})), })),
getResponsiveStyle(theme, rowGap, (val) => ({
rowGap: theme.spacing(val),
})),
getResponsiveStyle(theme, columnGap, (val) => ({
columnGap: theme.spacing(val),
})),
getResponsiveStyle(theme, grow, (val) => ({ getResponsiveStyle(theme, grow, (val) => ({
flexGrow: val, flexGrow: val,
})), })),

Loading…
Cancel
Save