From 211b1f7890cf1e10d7d8d6475388cbd34debb146 Mon Sep 17 00:00:00 2001 From: Timothy Sullivan Date: Fri, 7 Jun 2024 14:30:57 -0700 Subject: [PATCH 1/4] [Theme/Warning] Allow optional callback argument to setEuiDevProviderWarning --- packages/eui/changelogs/CHANGELOG_2024.md | 2 + .../src/views/provider/provider_example.js | 19 ++++ .../eui/src/services/theme/warning.test.ts | 86 +++++++++++++------ packages/eui/src/services/theme/warning.ts | 17 +++- 4 files changed, 97 insertions(+), 27 deletions(-) diff --git a/packages/eui/changelogs/CHANGELOG_2024.md b/packages/eui/changelogs/CHANGELOG_2024.md index 06e533dcdc8..fa9a40a4350 100644 --- a/packages/eui/changelogs/CHANGELOG_2024.md +++ b/packages/eui/changelogs/CHANGELOG_2024.md @@ -1,5 +1,7 @@ ## [`v94.6.0`](https://github.com/elastic/eui/releases/v94.6.0) +- Updated `setEuiDevProviderWarning` to support an optional callback function as the 2nd argument. + - Updated `EuiComboBox` to support rendering `option.append` and `option.prepend` in group labels ([#7800](https://github.com/elastic/eui/pull/7800)) **Accessibility** diff --git a/packages/eui/src-docs/src/views/provider/provider_example.js b/packages/eui/src-docs/src/views/provider/provider_example.js index cd7e33ba3ee..06d84f3c407 100644 --- a/packages/eui/src-docs/src/views/provider/provider_example.js +++ b/packages/eui/src-docs/src/views/provider/provider_example.js @@ -254,6 +254,25 @@ export const ProviderExample = { exception + +

+ Developers can provide an optional callback function which will + receive the log message or Error object. +

+ ), }, diff --git a/packages/eui/src/services/theme/warning.test.ts b/packages/eui/src/services/theme/warning.test.ts index 46a7bb13bc3..2f278b7718c 100644 --- a/packages/eui/src/services/theme/warning.test.ts +++ b/packages/eui/src/services/theme/warning.test.ts @@ -43,42 +43,80 @@ describe('EUI provider dev warnings', () => { consoleWarnSpy.mockRestore(); }); - const providerMessage = 'hello world'; + describe('emits log or throws', () => { + const providerMessage = 'hello world'; - it('does nothing if the warning level is undefined', () => { - emitEuiProviderWarning(providerMessage); + it('does nothing if the warning level is undefined', () => { + emitEuiProviderWarning(providerMessage); - expect(consoleLogSpy).not.toHaveBeenCalled(); - expect(consoleWarnSpy).not.toHaveBeenCalled(); - }); + expect(consoleLogSpy).not.toHaveBeenCalled(); + expect(consoleWarnSpy).not.toHaveBeenCalled(); + }); - it('emits a console log when level is log', () => { - setEuiDevProviderWarning('log'); + it('emits a console log when level is log', () => { + setEuiDevProviderWarning('log'); - emitEuiProviderWarning(providerMessage); + emitEuiProviderWarning(providerMessage); - expect(consoleLogSpy).toHaveBeenCalledWith('hello world'); - expect(consoleWarnSpy).not.toHaveBeenCalled(); - }); + expect(consoleLogSpy).toHaveBeenCalledWith('hello world'); + expect(consoleWarnSpy).not.toHaveBeenCalled(); + }); - it('emits a console warning when level is warn', () => { - setEuiDevProviderWarning('warn'); + it('emits a console warning when level is warn', () => { + setEuiDevProviderWarning('warn'); - emitEuiProviderWarning(providerMessage); + emitEuiProviderWarning(providerMessage); - expect(consoleLogSpy).not.toHaveBeenCalled(); - expect(consoleWarnSpy).toHaveBeenCalledWith('hello world'); + expect(consoleLogSpy).not.toHaveBeenCalled(); + expect(consoleWarnSpy).toHaveBeenCalledWith('hello world'); + }); + + it('throws an error when level is error', () => { + setEuiDevProviderWarning('error'); + + expect(() => emitEuiProviderWarning(providerMessage)).toThrowError( + 'hello world' + ); + + expect(consoleLogSpy).not.toHaveBeenCalled(); + expect(consoleWarnSpy).not.toHaveBeenCalled(); + }); }); - it('throws an error when level is error', () => { - setEuiDevProviderWarning('error'); + describe('calls optional callback function', () => { + const providerMessage = 'hello callback'; + const devCallback = jest.fn(); + + afterEach(() => { + devCallback.mockReset(); + }); + + it('when level is log', () => { + setEuiDevProviderWarning('log', devCallback); + + emitEuiProviderWarning(providerMessage); + + expect(devCallback).toHaveBeenCalledWith('hello callback'); + expect(devCallback).toHaveBeenCalledTimes(1); + }); + + it('when level is warn', () => { + setEuiDevProviderWarning('warn', devCallback); - expect(() => emitEuiProviderWarning(providerMessage)).toThrowError( - 'hello world' - ); + emitEuiProviderWarning(providerMessage); - expect(consoleLogSpy).not.toHaveBeenCalled(); - expect(consoleWarnSpy).not.toHaveBeenCalled(); + expect(devCallback).toHaveBeenCalledWith('hello callback'); + expect(devCallback).toHaveBeenCalledTimes(1); + }); + + it('when level is error', () => { + setEuiDevProviderWarning('error', devCallback); + + expect(() => emitEuiProviderWarning(providerMessage)).toThrow(); + + expect(devCallback).toHaveBeenCalledWith(new Error('hello callback')); + expect(devCallback).toHaveBeenCalledTimes(1); + }); }); }); }); diff --git a/packages/eui/src/services/theme/warning.ts b/packages/eui/src/services/theme/warning.ts index f4d2ac7f9b5..e0620f634f6 100644 --- a/packages/eui/src/services/theme/warning.ts +++ b/packages/eui/src/services/theme/warning.ts @@ -7,11 +7,18 @@ */ type LEVELS = 'log' | 'warn' | 'error'; +type ProviderCallback = (message: string | Error) => void; let providerWarning: LEVELS | undefined = undefined; +let providerCallback: ProviderCallback | undefined; -export const setEuiDevProviderWarning = (level: LEVELS | undefined) => - (providerWarning = level); +export const setEuiDevProviderWarning = ( + level: LEVELS | undefined, + cb?: ProviderCallback +) => { + providerWarning = level; + providerCallback = cb; +}; export const getEuiDevProviderWarning = () => providerWarning; @@ -20,12 +27,16 @@ export const emitEuiProviderWarning = (providerMessage: string) => { switch (providerWarning) { case 'log': console.log(providerMessage); + providerCallback?.(providerMessage); break; case 'warn': console.warn(providerMessage); + providerCallback?.(providerMessage); break; case 'error': - throw new Error(providerMessage); + const providerError = new Error(providerMessage); + providerCallback?.(providerError); + throw providerError; case undefined: default: break; From 48dd689a1fb3fcac08da466c89de6d000d9b5018 Mon Sep 17 00:00:00 2001 From: Cee Chen Date: Mon, 17 Jun 2024 16:08:43 -0700 Subject: [PATCH 2/4] PR feedback: simplify args to allow `providerWarning` to be a function callback --- .../eui/src/services/theme/warning.test.ts | 89 +++++++------------ packages/eui/src/services/theme/warning.ts | 23 ++--- 2 files changed, 41 insertions(+), 71 deletions(-) diff --git a/packages/eui/src/services/theme/warning.test.ts b/packages/eui/src/services/theme/warning.test.ts index 2f278b7718c..e83e57acb64 100644 --- a/packages/eui/src/services/theme/warning.test.ts +++ b/packages/eui/src/services/theme/warning.test.ts @@ -43,80 +43,55 @@ describe('EUI provider dev warnings', () => { consoleWarnSpy.mockRestore(); }); - describe('emits log or throws', () => { - const providerMessage = 'hello world'; + const providerMessage = 'hello world'; - it('does nothing if the warning level is undefined', () => { - emitEuiProviderWarning(providerMessage); + it('does nothing if the warning level is undefined', () => { + emitEuiProviderWarning(providerMessage); - expect(consoleLogSpy).not.toHaveBeenCalled(); - expect(consoleWarnSpy).not.toHaveBeenCalled(); - }); + expect(consoleLogSpy).not.toHaveBeenCalled(); + expect(consoleWarnSpy).not.toHaveBeenCalled(); + }); - it('emits a console log when level is log', () => { - setEuiDevProviderWarning('log'); + it('emits a console log when level is log', () => { + setEuiDevProviderWarning('log'); - emitEuiProviderWarning(providerMessage); + emitEuiProviderWarning(providerMessage); - expect(consoleLogSpy).toHaveBeenCalledWith('hello world'); - expect(consoleWarnSpy).not.toHaveBeenCalled(); - }); + expect(consoleLogSpy).toHaveBeenCalledWith('hello world'); + expect(consoleWarnSpy).not.toHaveBeenCalled(); + }); - it('emits a console warning when level is warn', () => { - setEuiDevProviderWarning('warn'); + it('emits a console warning when level is warn', () => { + setEuiDevProviderWarning('warn'); - emitEuiProviderWarning(providerMessage); + emitEuiProviderWarning(providerMessage); - expect(consoleLogSpy).not.toHaveBeenCalled(); - expect(consoleWarnSpy).toHaveBeenCalledWith('hello world'); - }); + expect(consoleLogSpy).not.toHaveBeenCalled(); + expect(consoleWarnSpy).toHaveBeenCalledWith('hello world'); + }); - it('throws an error when level is error', () => { - setEuiDevProviderWarning('error'); + it('throws an error when level is error', () => { + setEuiDevProviderWarning('error'); - expect(() => emitEuiProviderWarning(providerMessage)).toThrowError( - 'hello world' - ); + expect(() => emitEuiProviderWarning(providerMessage)).toThrowError( + 'hello world' + ); - expect(consoleLogSpy).not.toHaveBeenCalled(); - expect(consoleWarnSpy).not.toHaveBeenCalled(); - }); + expect(consoleLogSpy).not.toHaveBeenCalled(); + expect(consoleWarnSpy).not.toHaveBeenCalled(); }); - describe('calls optional callback function', () => { - const providerMessage = 'hello callback'; + it('passes messages to callback functions', () => { const devCallback = jest.fn(); + setEuiDevProviderWarning(devCallback); - afterEach(() => { - devCallback.mockReset(); - }); - - it('when level is log', () => { - setEuiDevProviderWarning('log', devCallback); + emitEuiProviderWarning(providerMessage); - emitEuiProviderWarning(providerMessage); + expect(devCallback).toHaveBeenCalledWith('hello world'); + expect(devCallback).toHaveBeenCalledTimes(1); - expect(devCallback).toHaveBeenCalledWith('hello callback'); - expect(devCallback).toHaveBeenCalledTimes(1); - }); - - it('when level is warn', () => { - setEuiDevProviderWarning('warn', devCallback); - - emitEuiProviderWarning(providerMessage); - - expect(devCallback).toHaveBeenCalledWith('hello callback'); - expect(devCallback).toHaveBeenCalledTimes(1); - }); - - it('when level is error', () => { - setEuiDevProviderWarning('error', devCallback); - - expect(() => emitEuiProviderWarning(providerMessage)).toThrow(); - - expect(devCallback).toHaveBeenCalledWith(new Error('hello callback')); - expect(devCallback).toHaveBeenCalledTimes(1); - }); + expect(consoleLogSpy).not.toHaveBeenCalled(); + expect(consoleWarnSpy).not.toHaveBeenCalled(); }); }); }); diff --git a/packages/eui/src/services/theme/warning.ts b/packages/eui/src/services/theme/warning.ts index e0620f634f6..bcff37ed4ac 100644 --- a/packages/eui/src/services/theme/warning.ts +++ b/packages/eui/src/services/theme/warning.ts @@ -9,34 +9,29 @@ type LEVELS = 'log' | 'warn' | 'error'; type ProviderCallback = (message: string | Error) => void; -let providerWarning: LEVELS | undefined = undefined; -let providerCallback: ProviderCallback | undefined; +let providerWarning: LEVELS | ProviderCallback | undefined = undefined; -export const setEuiDevProviderWarning = ( - level: LEVELS | undefined, - cb?: ProviderCallback -) => { - providerWarning = level; - providerCallback = cb; -}; +export const setEuiDevProviderWarning = (warningType: typeof providerWarning) => + (providerWarning = warningType); export const getEuiDevProviderWarning = () => providerWarning; // Not a public top-level EUI export, currently for internal use export const emitEuiProviderWarning = (providerMessage: string) => { + // Handle callback types + if (typeof providerWarning === 'function') { + return providerWarning(providerMessage); + } + // Handle level types switch (providerWarning) { case 'log': console.log(providerMessage); - providerCallback?.(providerMessage); break; case 'warn': console.warn(providerMessage); - providerCallback?.(providerMessage); break; case 'error': - const providerError = new Error(providerMessage); - providerCallback?.(providerError); - throw providerError; + throw new Error(providerMessage); case undefined: default: break; From 68f5901d0cd25a6ed9ea25433e21fd9be436ead5 Mon Sep 17 00:00:00 2001 From: Cee Chen Date: Mon, 17 Jun 2024 16:48:38 -0700 Subject: [PATCH 3/4] Update docs --- .../src/views/provider/provider_example.js | 25 +++++-------------- .../src/views/provider/provider_warning.tsx | 13 ++++++++++ 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/packages/eui/src-docs/src/views/provider/provider_example.js b/packages/eui/src-docs/src/views/provider/provider_example.js index 06d84f3c407..0808935cb91 100644 --- a/packages/eui/src-docs/src/views/provider/provider_example.js +++ b/packages/eui/src-docs/src/views/provider/provider_example.js @@ -15,7 +15,7 @@ import { GuideSectionPropsTable } from '../../components/guide_section/guide_sec import Setup from './provider_setup'; import GlobalStyles from './provider_styles'; -import Warnings from './provider_warning'; +import Warnings, { CallbackExample } from './provider_warning'; import { EuiComponentDefaultsProps, euiProviderComponentDefaultsSnippet, @@ -237,8 +237,7 @@ export const ProviderExample = {

- setEuiDevProviderWarning - accepts three levels: + setEuiDevProviderWarning accepts three levels:

  • @@ -256,23 +255,11 @@ export const ProviderExample = {

- Developers can provide an optional callback function which will - receive the log message or Error object. + It also accepts a callback function instead of a default warning + level. The warning message string will be passed to your callback, + where any custom action can be performed on it. Example usage:

-
    -
  • - setEuiDevProviderWarning('log', callbackFn): - callbackFn will be called with a string -
  • -
  • - setEuiDevProviderWarning('warn', callbackFn): - callbackFn will be called with a string -
  • -
  • - setEuiDevProviderWarning('error', callbackFn): - callbackFn will be called with an Error object -
  • -
+ ), }, diff --git a/packages/eui/src-docs/src/views/provider/provider_warning.tsx b/packages/eui/src-docs/src/views/provider/provider_warning.tsx index 3d5b1fc5dc3..debd683eb55 100644 --- a/packages/eui/src-docs/src/views/provider/provider_warning.tsx +++ b/packages/eui/src-docs/src/views/provider/provider_warning.tsx @@ -35,3 +35,16 @@ const AppWithDuplicateProvider = () => ( ); }; + +export const CallbackExample = () => ( + + {`import { setEuiDevProviderWarning } from '@elastic/eui'; + +const customWarningHandler = (message: string) => { + sendWarningToTelemetryService(message); + console.debug(message); +}; + +setEuiDevProviderWarning(customWarningHandler);`} + +); From ee96079434de96710d6c481b30daad8716ac5ab8 Mon Sep 17 00:00:00 2001 From: Cee Chen Date: Mon, 17 Jun 2024 17:29:48 -0700 Subject: [PATCH 4/4] changelog --- packages/eui/changelogs/CHANGELOG_2024.md | 2 -- packages/eui/changelogs/upcoming/7820.md | 1 + 2 files changed, 1 insertion(+), 2 deletions(-) create mode 100644 packages/eui/changelogs/upcoming/7820.md diff --git a/packages/eui/changelogs/CHANGELOG_2024.md b/packages/eui/changelogs/CHANGELOG_2024.md index b9909708090..0ba8a1f6f42 100644 --- a/packages/eui/changelogs/CHANGELOG_2024.md +++ b/packages/eui/changelogs/CHANGELOG_2024.md @@ -41,8 +41,6 @@ ## [`v94.6.0`](https://github.com/elastic/eui/releases/v94.6.0) -- Updated `setEuiDevProviderWarning` to support an optional callback function as the 2nd argument. - - Updated `EuiComboBox` to support rendering `option.append` and `option.prepend` in group labels ([#7800](https://github.com/elastic/eui/pull/7800)) **Accessibility** diff --git a/packages/eui/changelogs/upcoming/7820.md b/packages/eui/changelogs/upcoming/7820.md new file mode 100644 index 00000000000..9198600ece9 --- /dev/null +++ b/packages/eui/changelogs/upcoming/7820.md @@ -0,0 +1 @@ +- Updated `setEuiDevProviderWarning` to additionally accept a custom callback function, which warning messages will be passed to