Stack: Add size props (#86900)

* Abstract sizing styles from Box

* Upd name

* Stack: Add sizing props

* Revert

* Update LoginServiceButtons
This commit is contained in:
Alex Khomenko
2024-04-26 07:17:16 +02:00
committed by GitHub
parent ff761bb7d6
commit 8117aad9af
4 changed files with 95 additions and 71 deletions

View File

@@ -1,5 +1,4 @@
import { css } from '@emotion/css';
import { Property } from 'csstype';
import { css, cx } from '@emotion/css';
import React, { ElementType, forwardRef, PropsWithChildren } from 'react';
import { GrafanaTheme2, ThemeSpacingTokens, ThemeShape, ThemeShadows } from '@grafana/data';
@@ -7,6 +6,7 @@ import { GrafanaTheme2, ThemeSpacingTokens, ThemeShape, ThemeShadows } from '@gr
import { useStyles2 } from '../../../themes';
import { AlignItems, Direction, FlexProps, JustifyContent } from '../types';
import { ResponsiveProp, getResponsiveStyle } from '../utils/responsiveness';
import { getSizeStyles, SizeProps } from '../utils/styles';
type Display = 'flex' | 'block' | 'inline' | 'inline-block' | 'none';
export type BackgroundColor = keyof GrafanaTheme2['colors']['background'] | 'error' | 'success' | 'warning' | 'info';
@@ -15,7 +15,7 @@ export type BorderColor = keyof GrafanaTheme2['colors']['border'] | 'error' | 's
export type BorderRadius = keyof ThemeShape['radius'];
export type BoxShadow = keyof ThemeShadows;
interface BoxProps extends FlexProps, Omit<React.HTMLAttributes<HTMLElement>, 'className' | 'style'> {
interface BoxProps extends FlexProps, SizeProps, Omit<React.HTMLAttributes<HTMLElement>, 'className' | 'style'> {
// Margin props
/** Sets the property `margin` */
margin?: ResponsiveProp<ThemeSpacingTokens>;
@@ -59,15 +59,6 @@ interface BoxProps extends FlexProps, Omit<React.HTMLAttributes<HTMLElement>, 'c
justifyContent?: ResponsiveProp<JustifyContent>;
gap?: ResponsiveProp<ThemeSpacingTokens>;
// Size props
minWidth?: ResponsiveProp<Property.MinWidth<number>>;
maxWidth?: ResponsiveProp<Property.MaxWidth<number>>;
width?: ResponsiveProp<Property.Width<number>>;
minHeight?: ResponsiveProp<Property.MinHeight<number>>;
maxHeight?: ResponsiveProp<Property.MaxHeight<number>>;
height?: ResponsiveProp<Property.Height<number>>;
// Other props
backgroundColor?: ResponsiveProp<BackgroundColor>;
display?: ResponsiveProp<Display>;
@@ -145,18 +136,13 @@ export const Box = forwardRef<HTMLElement, PropsWithChildren<BoxProps>>((props,
justifyContent,
alignItems,
boxShadow,
gap,
width,
minWidth,
maxWidth,
height,
minHeight,
maxHeight
gap
);
const sizeStyles = useStyles2(getSizeStyles, width, minWidth, maxWidth, height, minHeight, maxHeight);
const Element = element ?? 'div';
return (
<Element ref={ref} className={styles.root} {...rest}>
<Element ref={ref} className={cx(styles.root, sizeStyles)} {...rest}>
{children}
</Element>
);
@@ -217,13 +203,7 @@ const getStyles = (
justifyContent: BoxProps['justifyContent'],
alignItems: BoxProps['alignItems'],
boxShadow: BoxProps['boxShadow'],
gap: BoxProps['gap'],
width: BoxProps['width'],
minWidth: BoxProps['minWidth'],
maxWidth: BoxProps['maxWidth'],
height: BoxProps['height'],
minHeight: BoxProps['minHeight'],
maxHeight: BoxProps['maxHeight']
gap: BoxProps['gap']
) => {
return {
root: css([
@@ -318,24 +298,6 @@ const getStyles = (
getResponsiveStyle(theme, gap, (val) => ({
gap: theme.spacing(val),
})),
getResponsiveStyle(theme, width, (val) => ({
width: theme.spacing(val),
})),
getResponsiveStyle(theme, minWidth, (val) => ({
minWidth: theme.spacing(val),
})),
getResponsiveStyle(theme, maxWidth, (val) => ({
maxWidth: theme.spacing(val),
})),
getResponsiveStyle(theme, height, (val) => ({
height: theme.spacing(val),
})),
getResponsiveStyle(theme, minHeight, (val) => ({
minHeight: theme.spacing(val),
})),
getResponsiveStyle(theme, maxHeight, (val) => ({
maxHeight: theme.spacing(val),
})),
]),
};
};

View File

@@ -1,4 +1,4 @@
import { css } from '@emotion/css';
import { css, cx } from '@emotion/css';
import React from 'react';
import { GrafanaTheme2, ThemeSpacingTokens } from '@grafana/data';
@@ -6,8 +6,9 @@ import { GrafanaTheme2, ThemeSpacingTokens } from '@grafana/data';
import { useStyles2 } from '../../../themes';
import { AlignItems, Direction, FlexProps, JustifyContent, Wrap } from '../types';
import { ResponsiveProp, getResponsiveStyle } from '../utils/responsiveness';
import { getSizeStyles, SizeProps } from '../utils/styles';
interface StackProps extends FlexProps, Omit<React.HTMLAttributes<HTMLElement>, 'className' | 'style'> {
interface StackProps extends FlexProps, SizeProps, Omit<React.HTMLAttributes<HTMLElement>, 'className' | 'style'> {
gap?: ResponsiveProp<ThemeSpacingTokens>;
alignItems?: ResponsiveProp<AlignItems>;
justifyContent?: ResponsiveProp<JustifyContent>;
@@ -17,11 +18,29 @@ interface StackProps extends FlexProps, Omit<React.HTMLAttributes<HTMLElement>,
}
export const Stack = React.forwardRef<HTMLDivElement, StackProps>((props, ref) => {
const { gap = 1, alignItems, justifyContent, direction, wrap, children, grow, shrink, basis, flex, ...rest } = props;
const {
gap = 1,
alignItems,
justifyContent,
direction,
wrap,
children,
grow,
shrink,
basis,
flex,
width,
minWidth,
maxWidth,
height,
minHeight,
maxHeight,
...rest
} = props;
const styles = useStyles2(getStyles, gap, alignItems, justifyContent, direction, wrap, grow, shrink, basis, flex);
const sizeStyles = useStyles2(getSizeStyles, width, minWidth, maxWidth, height, minHeight, maxHeight);
return (
<div ref={ref} className={styles.flex} {...rest}>
<div ref={ref} className={cx(styles.flex, sizeStyles)} {...rest}>
{children}
</div>
);

View File

@@ -0,0 +1,46 @@
import { css } from '@emotion/css';
import { Property } from 'csstype';
import { GrafanaTheme2 } from '@grafana/data';
import { getResponsiveStyle, ResponsiveProp } from './responsiveness';
export interface SizeProps {
minWidth?: ResponsiveProp<Property.MinWidth<number>>;
maxWidth?: ResponsiveProp<Property.MaxWidth<number>>;
width?: ResponsiveProp<Property.Width<number>>;
minHeight?: ResponsiveProp<Property.MinHeight<number>>;
maxHeight?: ResponsiveProp<Property.MaxHeight<number>>;
height?: ResponsiveProp<Property.Height<number>>;
}
export const getSizeStyles = (
theme: GrafanaTheme2,
width: SizeProps['width'],
minWidth: SizeProps['minWidth'],
maxWidth: SizeProps['maxWidth'],
height: SizeProps['height'],
minHeight: SizeProps['minHeight'],
maxHeight: SizeProps['maxHeight']
) => {
return css([
getResponsiveStyle(theme, width, (val) => ({
width: theme.spacing(val),
})),
getResponsiveStyle(theme, minWidth, (val) => ({
minWidth: theme.spacing(val),
})),
getResponsiveStyle(theme, maxWidth, (val) => ({
maxWidth: theme.spacing(val),
})),
getResponsiveStyle(theme, height, (val) => ({
height: theme.spacing(val),
})),
getResponsiveStyle(theme, minHeight, (val) => ({
minHeight: theme.spacing(val),
})),
getResponsiveStyle(theme, maxHeight, (val) => ({
maxHeight: theme.spacing(val),
})),
]);
};

View File

@@ -149,27 +149,24 @@ export const LoginServiceButtons = () => {
if (hasServices) {
return (
// TODO: Remove extra div when Stack supports width
<div style={{ width: '100%' }}>
<Stack direction={'column'}>
<LoginDivider />
{Object.entries(enabledServices).map(([key, service]) => {
const serviceName = service.name;
return (
<LinkButton
key={key}
className={getButtonStyleFor(service, styles, theme)}
href={`login/${service.hrefName ? service.hrefName : key}`}
target="_self"
fullWidth
>
<Icon className={styles.buttonIcon} name={service.icon} />
<Trans i18nKey="login.services.sing-in-with-prefix">Sign in with {{ serviceName }}</Trans>
</LinkButton>
);
})}
</Stack>
</div>
<Stack direction={'column'} width={'100%'}>
<LoginDivider />
{Object.entries(enabledServices).map(([key, service]) => {
const serviceName = service.name;
return (
<LinkButton
key={key}
className={getButtonStyleFor(service, styles, theme)}
href={`login/${service.hrefName ? service.hrefName : key}`}
target="_self"
fullWidth
>
<Icon className={styles.buttonIcon} name={service.icon} />
<Trans i18nKey="login.services.sing-in-with-prefix">Sign in with {{ serviceName }}</Trans>
</LinkButton>
);
})}
</Stack>
);
}