From e4fde777c28cb68fe9b71da60a5f6c60efb130c3 Mon Sep 17 00:00:00 2001 From: Josh Howenstine Date: Tue, 15 Oct 2024 09:14:27 -0700 Subject: [PATCH] fix: add text magnifier button and unit tests --- .../src/mixins/withAnnouncer/index.js | 19 +++-- .../withAnnouncer/withAnnouncer.test.js | 73 ++++++++++++++++++- .../addons/decorators/withLightning.js | 11 +-- .../.storybook/addons/toolbars/Announce.js | 21 +++--- .../lightning-ui-docs/.storybook/manager.js | 8 ++ .../lightning-ui-docs/.storybook/preview.js | 3 + 6 files changed, 112 insertions(+), 23 deletions(-) diff --git a/packages/@lightningjs/ui-components/src/mixins/withAnnouncer/index.js b/packages/@lightningjs/ui-components/src/mixins/withAnnouncer/index.js index 518f0590f..e0c1fef31 100644 --- a/packages/@lightningjs/ui-components/src/mixins/withAnnouncer/index.js +++ b/packages/@lightningjs/ui-components/src/mixins/withAnnouncer/index.js @@ -124,11 +124,10 @@ export default function withAnnouncer(Base, speak = Speech, options = {}) { set textMagnifierEnabled(val) { this._textMagnifierEnabled = val; - this._focusChange(); + this._updateTextMagnifier(true); } get textMagnifierEnabled() { - return true; return this._textMagnifierEnabled; } @@ -136,18 +135,24 @@ export default function withAnnouncer(Base, speak = Speech, options = {}) { if (!this._resetFocusTimer) { return; } - this._resetFocusTimer(); this.$announcerCancel(); this._debounceAnnounceFocusChanges(); this._updateTextMagnifier(); } - _updateTextMagnifier() { - if (!this.textMagnifierEnabled) return; + _updateTextMagnifier(force = false) { + if (!this.textMagnifierEnabled) { + if (this.tag('TextMagnifier')) { + this.patch({ + TextMagnifier: undefined + }); + } + return; + } const focusPath = this.application.focusPath || []; - const lastFocusPath = this._lastFocusPath || []; + const lastFocusPath = force ? [] : this._lastFocusPath || []; const focusDiff = focusPath.filter(elm => !lastFocusPath.includes(elm)); this._lastFocusPath = [...focusPath]; // Shallow copy of focusPath @@ -169,7 +174,7 @@ export default function withAnnouncer(Base, speak = Speech, options = {}) { this.patch({ TextMagnifier: { - type: textMagnifier ? undefined : TextMagnifier, // Set type only if not already tagged + type: textMagnifier ? undefined : TextMagnifier, location: stickToTop ? 'top' : 'bottom', content, zIndex: 9999 diff --git a/packages/@lightningjs/ui-components/src/mixins/withAnnouncer/withAnnouncer.test.js b/packages/@lightningjs/ui-components/src/mixins/withAnnouncer/withAnnouncer.test.js index 86a65d706..ac34420ae 100644 --- a/packages/@lightningjs/ui-components/src/mixins/withAnnouncer/withAnnouncer.test.js +++ b/packages/@lightningjs/ui-components/src/mixins/withAnnouncer/withAnnouncer.test.js @@ -83,7 +83,7 @@ class Row extends lng.Component { } } -class Item extends lng.Component {} +class Item extends lng.Component { } const Items = { Items: { @@ -503,4 +503,75 @@ describe('AppAnnouncer', () => { expect(speak).toHaveBeenCalledWith('CT', language); }); }); + + describe('TextMagnifier', () => { + let announcer, testRenderer; + + beforeEach(() => { + jest.clearAllMocks(); + testRenderer = TestRenderer.create(Component); + announcer = testRenderer.getInstance(); + }); + + it('should enable TextMagnifier when textMagnifierEnabled is true', () => { + announcer.textMagnifierEnabled = true; + announcer._updateTextMagnifier(true); + + const textMagnifier = announcer.tag('TextMagnifier'); + expect(textMagnifier).toBeDefined(); + }); + + it('should disable TextMagnifier when textMagnifierEnabled is false', () => { + announcer.textMagnifierEnabled = false; + announcer._updateTextMagnifier(true); + + const textMagnifier = announcer.tag('TextMagnifier'); + expect(textMagnifier).toBeUndefined(); + }); + + it('should update the content of the TextMagnifier with the focused component title or announce', () => { + announcer.textMagnifierEnabled = true; + testRenderer.keyPress('Right'); + announcer._updateTextMagnifier(true); + + const textMagnifier = announcer.tag('TextMagnifier'); + const expectedContent = 'Transformers'; + expect(textMagnifier.content).toEqual(expectedContent); + }); + + it.skip('should stick the TextMagnifier to the top when focusedY is greater than half the stage height', () => { + announcer.textMagnifierEnabled = true; + + const focusedElement = announcer.application.focusPath[0]; + focusedElement.core.renderContext = { py: announcer.stage.h / 2 + 1 }; // Bottom half of the screen + announcer._updateTextMagnifier(true); + + const textMagnifier = announcer.tag('TextMagnifier'); + expect(textMagnifier.location).toEqual('top'); + }); + + it.skip('should stick the TextMagnifier to the bottom when focusedY is less than or equal to half the stage height', () => { + announcer.textMagnifierEnabled = true; + + const focusedElement = announcer.application.focusPath[0]; + focusedElement.core.renderContext = { py: announcer.stage.h / 2 - 1 }; // Top half of the screen + announcer._updateTextMagnifier(true); + + const textMagnifier = announcer.tag('TextMagnifier'); + expect(textMagnifier.location).toEqual('bottom'); + }); + + it.skip('should update TextMagnifier content with concatenated announce values when an array is passed', () => { + announcer.textMagnifierEnabled = true; + const toAnnounce = ['Item 1', 'Context 1']; + + announcer._voiceOut(toAnnounce); + announcer._updateTextMagnifier(true); + + const textMagnifier = announcer.tag('TextMagnifier'); + const expectedContent = 'Item 1. Context 1'; + expect(textMagnifier.content).toEqual(expectedContent); + }); + }); + }); diff --git a/packages/apps/lightning-ui-docs/.storybook/addons/decorators/withLightning.js b/packages/apps/lightning-ui-docs/.storybook/addons/decorators/withLightning.js index 8d5ebf7c2..bc04f2f87 100644 --- a/packages/apps/lightning-ui-docs/.storybook/addons/decorators/withLightning.js +++ b/packages/apps/lightning-ui-docs/.storybook/addons/decorators/withLightning.js @@ -83,6 +83,7 @@ export const withLightning = ( const app = createApp({ theme: globals.LUITheme }); clearInspector(); app.announcerEnabled = globals.announce; + app.textMagnifierEnabled = globals.magnifier; app.debug = globals.announce; // toggle stage color !globals.stageColor @@ -176,12 +177,12 @@ export const withLightning = ( app.tag('StoryComponent').patch( parameters.storyDetails ? { - x: context.theme.layout.marginX - } + x: context.theme.layout.marginX + } : { - x: context.theme.layout.marginX, - y: context.theme.layout.marginY - } + x: context.theme.layout.marginX, + y: context.theme.layout.marginY + } ); }); if (!app.tag('GridOverlay')) { diff --git a/packages/apps/lightning-ui-docs/.storybook/addons/toolbars/Announce.js b/packages/apps/lightning-ui-docs/.storybook/addons/toolbars/Announce.js index 2c0ab26f3..89d8eab07 100644 --- a/packages/apps/lightning-ui-docs/.storybook/addons/toolbars/Announce.js +++ b/packages/apps/lightning-ui-docs/.storybook/addons/toolbars/Announce.js @@ -52,31 +52,32 @@ export const Announce = memo(function MyAddonSelector() { }); export const Magnifier = memo(function MyAddonSelector() { - const [{ announce }, updateGlobals] = useGlobals(); + const [{ magnifier }, updateGlobals] = useGlobals(); const api = useStorybookApi(); - const isActive = [true, 'true'].includes(announce); - const toggleAnnounce = useCallback(() => { + const isActive = [true, 'true'].includes(magnifier); + const toggleMagnifier = useCallback(() => { + console.log('here') updateGlobals({ - announce: !isActive + magnifier: !isActive }); }, [isActive]); useEffect(() => { api.setAddonShortcut(ADDON_ID, { - label: 'Announce Toggle [0]', - actionName: 'Announce', - action: toggleAnnounce + label: 'Magnifier Toggle [0]', + actionName: 'Magnifier', + action: toggleMagnifier }); - }, [toggleAnnounce, api]); + }, [toggleMagnifier, api]); return ( - + ); }); diff --git a/packages/apps/lightning-ui-docs/.storybook/manager.js b/packages/apps/lightning-ui-docs/.storybook/manager.js index 7e4f008d9..825ffcf88 100644 --- a/packages/apps/lightning-ui-docs/.storybook/manager.js +++ b/packages/apps/lightning-ui-docs/.storybook/manager.js @@ -21,6 +21,7 @@ import theme from './theme'; import * as ids from './addons/constants'; import { Announce, + Magnifier, StageColor, ThemeDownload, ThemePicker @@ -45,6 +46,13 @@ addons.register(ids.ADDON_ID, () => { match: ({ viewMode }) => viewMode === 'story', // show only in story render: Announce }); + // Magnifier toggle + addons.add(ids.MAGNIFIER_ID, { + type: types.TOOL, + title: 'Magnifier Toggle', + match: ({ viewMode }) => viewMode === 'story', // show only in story + render: Magnifier + }); // Theme Picker addons.add(ids.THEMEPICKER_ID, { type: types.TOOL, diff --git a/packages/apps/lightning-ui-docs/.storybook/preview.js b/packages/apps/lightning-ui-docs/.storybook/preview.js index 7737f6a40..1f0ecec7c 100644 --- a/packages/apps/lightning-ui-docs/.storybook/preview.js +++ b/packages/apps/lightning-ui-docs/.storybook/preview.js @@ -118,6 +118,9 @@ const preview = { announce: { defaultValue: false }, + magnifier: { + defaultValue: false + }, stageColor: { defaultValue: false }