Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Emotion][perf] Memoize simpler medium-impact components #7638

Merged
merged 7 commits into from
Apr 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 6 additions & 5 deletions src/components/badge/badge.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,17 @@ import React, {
useMemo,
} from 'react';
import classNames from 'classnames';
import { CommonProps, ExclusiveUnion, PropsOf } from '../common';

import {
useEuiTheme,
useEuiMemoizedStyles,
getSecureRelForTarget,
wcagContrastMin,
} from '../../services';
import { validateHref } from '../../services/security/href_validator';
import { CommonProps, ExclusiveUnion, PropsOf } from '../common';
import { EuiInnerText } from '../inner_text';
import { EuiIcon, IconType } from '../icon';
import { validateHref } from '../../services/security/href_validator';

import { getTextColor, getColorContrast, getIsValidColor } from './color_utils';
import { euiBadgeStyles } from './badge.styles';
Expand Down Expand Up @@ -124,12 +126,11 @@ export const EuiBadge: FunctionComponent<EuiBadgeProps> = ({
style,
...rest
}) => {
const euiTheme = useEuiTheme();

const isHrefValid = !href || validateHref(href);
const isDisabled = _isDisabled || !isHrefValid;
const isNamedColor = COLORS.includes(color as BadgeColor);

const euiTheme = useEuiTheme();
const customColorStyles = useMemo(() => {
// Named colors set their styles via Emotion CSS and not inline styles
if (isNamedColor) return style;
Expand Down Expand Up @@ -165,7 +166,7 @@ export const EuiBadge: FunctionComponent<EuiBadgeProps> = ({
}
}, [color, isNamedColor, style, euiTheme]);

const styles = euiBadgeStyles(euiTheme);
const styles = useEuiMemoizedStyles(euiBadgeStyles);
const cssStyles = [
styles.euiBadge,
isNamedColor && styles[color as BadgeColor],
Expand Down
9 changes: 4 additions & 5 deletions src/components/badge/badge_group/badge_group.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,9 @@

import React, { forwardRef, HTMLAttributes, Ref, ReactNode } from 'react';
import classNames from 'classnames';

import { useEuiMemoizedStyles } from '../../../services';
import { CommonProps } from '../../common';
import { useEuiTheme } from '../../../services';

import { euiBadgeGroupStyles } from './badge_group.styles';

Expand All @@ -35,13 +36,11 @@ export const EuiBadgeGroup = forwardRef<
{ children, className, gutterSize = 'xs', ...rest },
ref: Ref<HTMLDivElement>
) => {
const euiTheme = useEuiTheme();
const classes = classNames('euiBadgeGroup', className);

const styles = euiBadgeGroupStyles(euiTheme);
const styles = useEuiMemoizedStyles(euiBadgeGroupStyles);
const cssStyles = [styles.euiBadgeGroup, styles[gutterSize]];

const classes = classNames('euiBadgeGroup', className);

return (
<div css={cssStyles} className={classes} ref={ref} {...rest}>
{children}
Expand Down
10 changes: 3 additions & 7 deletions src/components/badge/beta_badge/beta_badge.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,10 @@ import React, {
ReactNode,
} from 'react';
import classNames from 'classnames';
import { CommonProps, ExclusiveUnion } from '../../common';

import { getSecureRelForTarget, useEuiTheme } from '../../../services';

import { getSecureRelForTarget, useEuiMemoizedStyles } from '../../../services';
import { CommonProps, ExclusiveUnion } from '../../common';
import { EuiToolTip, EuiToolTipProps, ToolTipPositions } from '../../tool_tip';

import { EuiIcon, IconType } from '../../icon';

import { euiBetaBadgeStyles } from './beta_badge.styles';
Expand Down Expand Up @@ -146,14 +144,12 @@ export const EuiBetaBadge: FunctionComponent<EuiBetaBadgeProps> = ({
alignment = 'baseline',
...rest
}) => {
const euiTheme = useEuiTheme();

const singleLetter = !!(typeof label === 'string' && label.length === 1);
const isCircular = iconType || singleLetter;

const classes = classNames('euiBetaBadge', className);

const styles = euiBetaBadgeStyles(euiTheme);
const styles = useEuiMemoizedStyles(euiBetaBadgeStyles);
const cssStyles = [
styles.euiBetaBadge,
styles[color],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,9 @@

import React, { HTMLAttributes, ReactNode, FunctionComponent } from 'react';
import classNames from 'classnames';

import { useEuiMemoizedStyles } from '../../../services';
import { CommonProps } from '../../common';
import { useEuiTheme } from '../../../services';

import { euiNotificationBadgeStyles } from './badge_notification.styles';

Expand All @@ -33,13 +34,11 @@ export interface EuiNotificationBadgeProps
export const EuiNotificationBadge: FunctionComponent<
EuiNotificationBadgeProps
> = ({ children, className, size = 's', color = 'accent', ...rest }) => {
const euiTheme = useEuiTheme();
const classes = classNames('euiNotificationBadge', className);

const styles = euiNotificationBadgeStyles(euiTheme);
const styles = useEuiMemoizedStyles(euiNotificationBadgeStyles);
const cssStyles = [styles.euiNotificationBadge, styles[size], styles[color]];

const classes = classNames('euiNotificationBadge', className);

return (
<span css={cssStyles} className={classes} {...rest}>
{children}
Expand Down
12 changes: 6 additions & 6 deletions src/components/call_out/call_out.styles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,15 +45,10 @@ export const euiCallOutStyles = ({ euiTheme }: UseEuiTheme) => {
${logicalCSS('right', euiTheme.size.s)}
`,
},
euiCallOut__icon: css`
position: relative;
${logicalCSS('top', '-1px')}
${logicalCSS('margin-right', euiTheme.size.s)}
`,
};
};

export const euiCallOutHeadingStyles = ({ euiTheme }: UseEuiTheme) => {
export const euiCallOutHeaderStyles = ({ euiTheme }: UseEuiTheme) => {
return {
euiCallOutHeader: css`
font-weight: ${euiTheme.font.weight.medium};
Expand All @@ -75,5 +70,10 @@ export const euiCallOutHeadingStyles = ({ euiTheme }: UseEuiTheme) => {
danger: css`
color: ${euiTheme.colors.dangerText};
`,
euiCallOut__icon: css`
position: relative;
${logicalCSS('top', '-1px')}
${logicalCSS('margin-right', euiTheme.size.s)}
`,
};
};
143 changes: 73 additions & 70 deletions src/components/call_out/call_out.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,22 +6,21 @@
* Side Public License, v 1.
*/

import React, { forwardRef, HTMLAttributes, ReactNode } from 'react';
import React, { forwardRef, HTMLAttributes, ReactNode, useMemo } from 'react';

import classNames from 'classnames';

import { useEuiMemoizedStyles } from '../../services';
import { CommonProps } from '../common';
import { IconType, EuiIcon } from '../icon';

import { EuiButtonIcon } from '../button';
import { EuiText } from '../text';
import { useEuiTheme } from '../../services';
import { EuiPanel } from '../panel';
import { EuiSpacer } from '../spacer';
import { EuiTitle } from '../title';
import { EuiI18n } from '../i18n';

import { euiCallOutStyles, euiCallOutHeadingStyles } from './call_out.styles';
import { euiCallOutStyles, euiCallOutHeaderStyles } from './call_out.styles';

export const COLORS = ['primary', 'success', 'warning', 'danger'] as const;
export type Color = (typeof COLORS)[number];
Expand Down Expand Up @@ -63,22 +62,12 @@ export const EuiCallOut = forwardRef<HTMLDivElement, EuiCallOutProps>(
},
ref
) => {
const theme = useEuiTheme();
const styles = euiCallOutStyles(theme);
const styles = useEuiMemoizedStyles(euiCallOutStyles);
const cssStyles = [
styles.euiCallOut,
onDismiss && styles.hasDismissButton.hasDimissButton,
onDismiss && styles.hasDismissButton[size],
];
const cssDismissButtonStyles = [
styles.dismissButton.euiCallOut__dismissButton,
styles.dismissButton[size],
];
const headerStyles = euiCallOutHeadingStyles(theme);
const cssHeaderStyles = [
headerStyles.euiCallOutHeader,
headerStyles[color],
];

const classes = classNames(
'euiCallOut',
Expand All @@ -88,67 +77,64 @@ export const EuiCallOut = forwardRef<HTMLDivElement, EuiCallOutProps>(
className
);

const dismissButton = onDismiss && (
<EuiI18n
token="euiCallOut.dismissAriaLabel"
default="Dismiss this callout"
>
{(dismissAriaLabel: string) => (
<EuiButtonIcon
iconType="cross"
onClick={onDismiss}
aria-label={dismissAriaLabel}
css={cssDismissButtonStyles}
color={color}
data-test-subj="euiDismissCalloutButton"
/>
)}
</EuiI18n>
);

const headerIcon = iconType && (
<EuiIcon
css={styles.euiCallOut__icon}
type={iconType}
size="m"
aria-hidden="true"
color="inherit"
/>
);
const H: Heading = heading;
const header = title && (
<EuiTitle size={size === 's' ? 'xxs' : 'xs'} css={cssHeaderStyles}>
<H className="euiCallOutHeader__title">
{headerIcon}
{title}
</H>
</EuiTitle>
);
const dismissButton = useMemo(() => {
if (!onDismiss) return;

const cssStyles = [
styles.dismissButton.euiCallOut__dismissButton,
styles.dismissButton[size],
];

return (
<EuiI18n
token="euiCallOut.dismissAriaLabel"
default="Dismiss this callout"
>
{(dismissAriaLabel: string) => (
<EuiButtonIcon
iconType="cross"
onClick={onDismiss}
aria-label={dismissAriaLabel}
css={cssStyles}
color={color}
data-test-subj="euiDismissCalloutButton"
/>
)}
</EuiI18n>
);
}, [onDismiss, styles, color, size]);

const headerStyles = useEuiMemoizedStyles(euiCallOutHeaderStyles);
const header = useMemo(() => {
if (!title) return;

const H: Heading = heading;
const cssStyles = [headerStyles.euiCallOutHeader, headerStyles[color]];

return (
<EuiTitle size={size === 's' ? 'xxs' : 'xs'} css={cssStyles}>
<H className="euiCallOutHeader__title">
{iconType && (
<EuiIcon
css={headerStyles.euiCallOut__icon}
type={iconType}
size="m"
aria-hidden="true"
color="inherit"
/>
)}
{title}
</H>
</EuiTitle>
);
}, [title, heading, iconType, size, color, headerStyles]);

const optionalChildren = children && (
<EuiText size={size === 's' ? 'xs' : 's'} color="default">
{children}
</EuiText>
);

// Note: the DOM position of the dismiss button matters to screen reader users.
// We generally want them to have some context of _what_ they're dismissing,
// instead of navigating to the dismiss button first before the callout content
const calloutContent =
header && optionalChildren ? (
<>
{header}
{dismissButton}
<EuiSpacer size="s" />
{optionalChildren}
</>
) : (
<>
{header || optionalChildren}
{dismissButton}
</>
);

return (
<EuiPanel
borderRadius="none"
Expand All @@ -160,7 +146,24 @@ export const EuiCallOut = forwardRef<HTMLDivElement, EuiCallOutProps>(
grow={false}
{...rest}
>
{calloutContent}
{
// Note: the DOM position of the dismiss button matters to screen reader users.
// We generally want them to have some context of _what_ they're dismissing,
// instead of navigating to the dismiss button first before the callout content
header && optionalChildren ? (
<>
{header}
{dismissButton}
<EuiSpacer size="s" />
{optionalChildren}
</>
) : (
<>
{header || optionalChildren}
{dismissButton}
</>
)
}
</EuiPanel>
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ exports[`EuiContextMenu can pass-through horizontal rule props 1`] = `
</div>
<div>
<hr
class="euiHorizontalRule euiHorizontalRule--half euiHorizontalRule--marginSmall emotion-euiHorizontalRule-half-s"
class="euiHorizontalRule emotion-euiHorizontalRule-half-s"
/>
</div>
</div>
Expand Down Expand Up @@ -168,7 +168,7 @@ exports[`EuiContextMenu renders isSeparator items 1`] = `
</span>
</div>
<hr
class="euiHorizontalRule euiHorizontalRule--full emotion-euiHorizontalRule-full"
class="euiHorizontalRule emotion-euiHorizontalRule-full"
/>
<div
class="euiContextMenuItem emotion-euiContextMenuItem-m-center"
Expand Down
Loading
Loading