diff --git a/packages/ui-top-nav-bar/src/TopNavBar/TopNavBarBrand/__examples__/TopNavBarBrand.examples.tsx b/packages/ui-top-nav-bar/src/TopNavBar/TopNavBarBrand/__examples__/TopNavBarBrand.examples.tsx new file mode 100644 index 0000000000..2e0bf5e003 --- /dev/null +++ b/packages/ui-top-nav-bar/src/TopNavBar/TopNavBarBrand/__examples__/TopNavBarBrand.examples.tsx @@ -0,0 +1,54 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2015 - present Instructure, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +import type { StoryConfig } from '@instructure/ui-test-utils' + +import { elevateIconInverse } from '../../utils/exampleSvgFiles' +import type { TopNavBarBrandProps } from '../props' + +export default { + propValues: { + renderIcon: [undefined, 'Logo', elevateIconInverse], + iconBackground: [undefined, '#ddd'], + href: [undefined, '/#home'], + onClick: [undefined, () => {}] + }, + getComponentProps: () => { + return { + screenReaderLabel: 'Brand name' + } + }, + filter: (props) => { + if (props.iconBackground && !props.renderIcon) { + return true + } + if ( + (props.href || props.onClick) && + !(typeof props.renderIcon === 'object' && props.iconBackground) + ) { + return true + } + return false + } +} as StoryConfig diff --git a/packages/ui-top-nav-bar/src/TopNavBar/TopNavBarBrand/__tests__/TopNavBarBrand.test.tsx b/packages/ui-top-nav-bar/src/TopNavBar/TopNavBarBrand/__tests__/TopNavBarBrand.test.tsx new file mode 100644 index 0000000000..22a1865320 --- /dev/null +++ b/packages/ui-top-nav-bar/src/TopNavBar/TopNavBarBrand/__tests__/TopNavBarBrand.test.tsx @@ -0,0 +1,247 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2015 - present Instructure, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +import { vi } from 'vitest' +import type { MockInstance } from 'vitest' +import { render, screen, waitFor } from '@testing-library/react' +import userEvent from '@testing-library/user-event' +import '@testing-library/jest-dom' + +// eslint-disable-next-line no-restricted-imports +import { generateA11yTests } from '@instructure/ui-scripts/lib/test/generateA11yTests' +import { runAxeCheck } from '@instructure/ui-axe-check' +import { SmallViewportModeWrapper } from '../../utils/exampleHelpers' +import { elevateIcon } from '../../utils/exampleSvgFiles' +import { TopNavBarBrand } from '../index' +import TopNavBarBrandExamples from '../__examples__/TopNavBarBrand.examples' + +describe('', () => { + let consoleWarningMock: ReturnType + let consoleErrorMock: ReturnType + + beforeEach(() => { + // Mocking console to prevent test output pollution + consoleWarningMock = vi + .spyOn(console, 'warn') + .mockImplementation(() => {}) as MockInstance + consoleErrorMock = vi + .spyOn(console, 'error') + .mockImplementation(() => {}) as MockInstance + }) + + afterEach(() => { + consoleWarningMock.mockRestore() + consoleErrorMock.mockRestore() + }) + + it('should render', async () => { + const { container } = render( + + ) + const component = container.querySelector('[class$="-topNavBarBrand"]') + + expect(component).toBeInTheDocument() + }) + + describe('elementRef prop should return a ref to the root element', () => { + it('should return root element', async () => { + const elementRef = vi.fn() + const { container } = render( + + ) + const component = container.querySelector('[class$="-topNavBarBrand"]') + + expect(elementRef).toHaveBeenCalledWith(component) + }) + }) + + describe('screenReaderLabel prop', () => { + it('should render label for SR', async () => { + const { container } = render( + + ) + const screenReaderLabel = container.querySelector( + '[class$="-screenReaderContent"]' + ) + + expect(screenReaderLabel).toHaveTextContent('Test label') + }) + }) + + describe('renderIcon prop', () => { + it('should render name in desktop mode', async () => { + const { container } = render( + + ) + const iconContainer = container.querySelector('[class$="iconContainer"]') + const icon = container.querySelector('svg') + + expect(iconContainer).toBeVisible() + expect(icon).toBeVisible() + expect(icon).toHaveAttribute('id', 'elevateIcon') + }) + + it('should not render name in smallViewport mode', async () => { + const { container } = render( + + + + ) + const iconContainer = container.querySelector('[class$="iconContainer"]') + + expect(iconContainer).not.toBeInTheDocument() + }) + }) + + describe('iconBackground prop', () => { + it('should be visible in desktop mode', async () => { + const { container } = render( + + ) + const iconContainer = container.querySelector('[class$="iconContainer"]')! + + expect(getComputedStyle(iconContainer).backgroundColor).toBe( + 'rgb(0, 0, 255)' + ) + }) + + it('should not be visible in smallViewport mode', async () => { + const { container } = render( + + + + ) + const iconContainer = container.querySelector('[class$="iconContainer"]') + + expect(iconContainer).not.toBeInTheDocument() + }) + }) + + describe('href prop', () => { + it('should render component as link', async () => { + render( + + ) + const link = screen.getByRole('link') + + expect(link).toBeInTheDocument() + expect(link.tagName).toBe('A') + expect(link).toHaveAttribute('href', '/#TestHref') + }) + }) + + describe('onClick prop', () => { + it('should render component button', async () => { + const onClick = vi.fn() + render( + + ) + const button = screen.getByRole('button') + + userEvent.click(button) + + await waitFor(() => { + expect(button.tagName).toBe('BUTTON') + expect(onClick).toHaveBeenCalled() + }) + }) + }) + + describe('as prop', () => { + it('should render component as passed element', async () => { + const onClick = vi.fn() + render( + + ) + const button = screen.getByRole('button') + + userEvent.click(button) + + await waitFor(() => { + expect(button.tagName).toBe('BUTTON') + expect(onClick).toHaveBeenCalled() + }) + }) + }) + + it('should be accessible', async () => { + const { container } = render( + + ) + const axeCheck = await runAxeCheck(container) + + expect(axeCheck).toBe(true) + }) + + describe('with generated examples', () => { + const generatedComponents = generateA11yTests( + TopNavBarBrand, + TopNavBarBrandExamples + ) + + it.each(generatedComponents)( + 'should be accessible with example: $description', + async ({ content }) => { + const { container } = render(content) + const axeCheck = await runAxeCheck(container) + expect(axeCheck).toBe(true) + } + ) + }) +})