Skip to content

Commit

Permalink
Merge remote-tracking branch 'upstream/main' into #3630
Browse files Browse the repository at this point in the history
  • Loading branch information
alexwizp committed Jul 1, 2024
2 parents ff8623e + 3c0e0ae commit a4910b7
Show file tree
Hide file tree
Showing 7 changed files with 100 additions and 14 deletions.
1 change: 1 addition & 0 deletions packages/eui/changelogs/upcoming/7862.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
- Updated `EuiThemeProvider`s to allow modifying/setting custom `breakpoint`s in nested usage (as opposed to only at the top `EuiProvider` level)
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,8 @@ export const CustomBreakpointsJS = () => {
<>
<p>
Theme breakpoints can be overriden or added via{' '}
<EuiCode>EuiProvider</EuiCode>&apos;s <EuiCode>modify</EuiCode>{' '}
<EuiCode>EuiProvider</EuiCode>&apos;s or{' '}
<EuiCode>EuiThemeProvider</EuiCode>&apos;s <EuiCode>modify</EuiCode>{' '}
prop.
</p>
<p>
Expand All @@ -232,7 +233,7 @@ export const CustomBreakpointsJS = () => {
Current custom breakpoint: <strong>{currentBreakpoint}</strong>
</p>
}
snippet={`<EuiProvider
snippet={`<EuiThemeProvider
modify={{
breakpoint: {
xxs: 0,
Expand All @@ -246,7 +247,7 @@ export const CustomBreakpointsJS = () => {
}}
>
<App />
</EuiProvider>
</EuiThemeProvider>
`}
snippetLanguage="js"
/>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React, { useContext, useMemo } from 'react';
import { EuiSpacer, EuiText, EuiProvider } from '../../../../../src';
import { EuiSpacer, EuiText, EuiThemeProvider } from '../../../../../src';

import { GuideSection } from '../../../components/guide_section/guide_section';
import { ThemeContext } from '../../../components/with_theme';
Expand Down Expand Up @@ -51,7 +51,7 @@ export default () => {
<GuideSection color="transparent">{valuesContent}</GuideSection>

{currentLanguage.includes('js') && (
<EuiProvider modify={{ breakpoint: CUSTOM_BREAKPOINTS }}>
<EuiThemeProvider modify={{ breakpoint: CUSTOM_BREAKPOINTS }}>
<GuideSection color="subdued">
<EuiText grow={false}>
<h2 id={breakpointSections[1].id}>
Expand All @@ -70,7 +70,7 @@ export default () => {
</GuideSection>

<GuideSection color="transparent">{valuesContent}</GuideSection>
</EuiProvider>
</EuiThemeProvider>
)}
</>
);
Expand Down
5 changes: 1 addition & 4 deletions packages/eui/src/components/provider/provider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ import {
EuiThemeProvider,
EuiThemeProviderProps,
EuiThemeSystem,
CurrentEuiBreakpointProvider,
} from '../../services';
import { emitEuiProviderWarning } from '../../services/theme/warning';
import { cache as fallbackCache } from '../../services/emotion/css';
Expand Down Expand Up @@ -146,9 +145,7 @@ export const EuiProvider = <T extends {} = {}>({
</>
)}
<EuiComponentDefaultsProvider componentDefaults={componentDefaults}>
<CurrentEuiBreakpointProvider>
{children}
</CurrentEuiBreakpointProvider>
{children}
</EuiComponentDefaultsProvider>
</EuiThemeProvider>
</EuiCacheProvider>
Expand Down
8 changes: 5 additions & 3 deletions packages/eui/src/services/breakpoint/current_breakpoint.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import {
_EuiThemeBreakpoint,
_EuiThemeBreakpoints,
} from '../../global_styling/variables/breakpoint';
import { useEuiTheme } from '../theme';
import { useEuiTheme } from '../theme/hooks';
import { throttle } from '../throttle';
import { sortMapByLargeToSmallValues } from './_sorting';

Expand All @@ -31,8 +31,10 @@ export const CurrentEuiBreakpointContext =
createContext<CurrentEuiBreakpoint>(undefined);

/**
* Top level provider (nested within EuiProvider) which provides a single
* resize listener that returns the current breakpoint based on window width
* Returns the current breakpoint based on window width.
* Typically only called by the top-level `EuiProvider` (to reduce the number
* of window resize listeners on the page). Also conditionally called if a
* nested `EuiThemeProvider` defines a `modify.breakpoint` override
*/
export const CurrentEuiBreakpointProvider: FunctionComponent<
PropsWithChildren
Expand Down
72 changes: 72 additions & 0 deletions packages/eui/src/services/theme/provider.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { render } from '@testing-library/react'; // Note - don't use the EUI cus
import { css } from '@emotion/react';

import { EuiProvider } from '../../components/provider';
import { useCurrentEuiBreakpoint } from '../breakpoint';
import { EuiNestedThemeContext } from './context';
import { EuiThemeProvider } from './provider';

Expand Down Expand Up @@ -75,6 +76,77 @@ describe('EuiThemeProvider', () => {
});
});

describe('conditional CurrentEuiBreakpointProvider', () => {
const PrintCurrentBreakpoint = () => <>{useCurrentEuiBreakpoint()}</>;
let resizeListenerCount = 0;

beforeAll(() => {
// React 16 and 17 register a bunch of error listeners which we don't need to capture
window.addEventListener = jest.fn((event: string) => {
if (event === 'resize') resizeListenerCount++;
});
});

beforeEach(() => {
// Reset counts
resizeListenerCount = 0;
});

afterAll(() => {
jest.restoreAllMocks();
});

describe('in the top-level global theme provider', () => {
it('is always rendered regardless of modified breakpoints', () => {
const { container } = render(
<EuiProvider>
<PrintCurrentBreakpoint />
</EuiProvider>
);
expect(container.textContent).toEqual('l');
expect(resizeListenerCount).toEqual(1);
});
});

describe('in nested child theme providers', () => {
beforeAll(() => {
window.innerWidth = 2500;
});

afterAll(() => {
// Reset window width to jsdom's default
window.innerWidth = 1024;
});

it('is rendered if modify.breakpoint is passed', () => {
const customBreakpoints = { xxl: 2000 };

const { container } = render(
<EuiProvider>
<EuiThemeProvider modify={{ breakpoint: customBreakpoints }}>
<PrintCurrentBreakpoint />
</EuiThemeProvider>
</EuiProvider>
);

expect(container.textContent).toEqual('xxl');
expect(resizeListenerCount).toBeGreaterThanOrEqual(2); // Technically 3 due to EuiThemeProvider rerendering
});

it('is not rendered if modify.breakpoint is not passed', () => {
const { container } = render(
<EuiProvider>
<EuiThemeProvider>
<PrintCurrentBreakpoint />
</EuiThemeProvider>
</EuiProvider>
);
expect(container.textContent).toEqual('xl');
expect(resizeListenerCount).toEqual(1);
});
});
});

describe('nested EuiThemeProviders', () => {
it('renders with a span wrapper that sets the inherited text color', () => {
const { container } = render(
Expand Down
15 changes: 14 additions & 1 deletion packages/eui/src/services/theme/provider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,15 @@ import React, {
useCallback,
PropsWithChildren,
HTMLAttributes,
Fragment,
} from 'react';
import { Global, type CSSObject } from '@emotion/react';
import isEqual from 'lodash/isEqual';

import type { CommonProps } from '../../components/common';
import { cloneElementWithCss } from '../emotion';
import { css, cx } from '../emotion/css';
import { CurrentEuiBreakpointProvider } from '../breakpoint/current_breakpoint';

import {
EuiSystemContext,
Expand Down Expand Up @@ -80,6 +82,15 @@ export const EuiThemeProvider = <T extends {} = {}>({
const [system, setSystem] = useState(_system || parentSystem);
const prevSystemKey = useRef(system.key);

// To reduce the number of window resize listeners, only render a
// CurrentEuiBreakpointProvider for the top level parent theme, or for
// nested themes only if modified breakpoint overrides are passed
const EuiConditionalBreakpointProvider = useMemo(() => {
return isGlobalTheme || _modifications?.breakpoint
? CurrentEuiBreakpointProvider
: Fragment;
}, [isGlobalTheme, _modifications]);

const [modifications, setModifications] = useState<EuiThemeModifications>(
mergeDeep(parentModifications, _modifications)
);
Expand Down Expand Up @@ -232,7 +243,9 @@ export const EuiThemeProvider = <T extends {} = {}>({
<EuiNestedThemeContext.Provider value={nestedThemeContext}>
<EuiThemeMemoizedStylesProvider>
<EuiEmotionThemeProvider>
{renderedChildren}
<EuiConditionalBreakpointProvider>
{renderedChildren}
</EuiConditionalBreakpointProvider>
</EuiEmotionThemeProvider>
</EuiThemeMemoizedStylesProvider>
</EuiNestedThemeContext.Provider>
Expand Down

0 comments on commit a4910b7

Please sign in to comment.