diff --git a/src/components/AppNotification/AppNotification.tsx b/src/components/AppNotification/AppNotification.tsx index cfbc51693..b8ef688d5 100644 --- a/src/components/AppNotification/AppNotification.tsx +++ b/src/components/AppNotification/AppNotification.tsx @@ -84,9 +84,7 @@ export const AppNotification = ({ onClick={onDismiss} rank="tertiary" variant={color === 'dark' ? 'inverse' : 'neutral'} - > - Close - + > )} diff --git a/src/components/Button/Button.test.tsx b/src/components/Button/Button.test.tsx index 8516dd340..bee889a9f 100644 --- a/src/components/Button/Button.test.tsx +++ b/src/components/Button/Button.test.tsx @@ -7,6 +7,18 @@ import * as stories from './Button.stories'; import type { StoryFile } from '../../util/utility-types'; describe('); + + expect(consoleWarnMock).toHaveBeenCalledTimes(0); + expect(consoleErrorMock).toHaveBeenCalledTimes(1); + }); + + it('warns when icon-only Button instances contain children', () => { + render( + , + ); + + expect(consoleWarnMock).toHaveBeenCalledTimes(1); + expect(consoleErrorMock).toHaveBeenCalledTimes(0); + }); + }); }); diff --git a/src/components/Button/Button.tsx b/src/components/Button/Button.tsx index 255c29d2f..645840905 100644 --- a/src/components/Button/Button.tsx +++ b/src/components/Button/Button.tsx @@ -1,5 +1,6 @@ import clsx from 'clsx'; import React, { forwardRef } from 'react'; +import { assertEdsUsage } from '../../util/logging'; import type { Size } from '../../util/variant-types'; import Icon, { type IconName } from '../Icon'; import LoadingIndicator from '../LoadingIndicator'; @@ -132,6 +133,20 @@ export const Button = forwardRef( isLoading && styles['button--is-loading'], ); + assertEdsUsage( + [ + typeof isDisabled === 'undefined' && + typeof other.disabled !== 'undefined', + ], + 'Use "isDisabled" instead of "disabled" on button instances', + 'error', + ); + + assertEdsUsage( + [iconLayout === 'icon-only' && typeof children !== 'undefined'], + 'Specifying content for "children" when using icon-only layout is not required and can be removed.', + ); + return ( ', () => { + beforeEach(() => { + const consoleMock = jest.spyOn(console, 'warn'); + consoleMock.mockImplementation(); + }); + + afterEach(() => { + jest.resetAllMocks(); + }); + generateSnapshots(stories as StoryFile); it('renders the text in the link', () => { @@ -63,4 +72,42 @@ describe('', () => { const link = screen.getByRole('link'); expect(link).toHaveFocus(); }); + + describe('emits warnings when misused', () => { + it('warns when inline links are using emphasis=low', () => { + const consoleMock = jest.spyOn(console, 'warn'); + consoleMock.mockImplementation(); + render( + + Click + , + ); + + expect(consoleMock).toHaveBeenCalledTimes(1); + }); + + it('warns when inline links have icons specified', () => { + const consoleMock = jest.spyOn(console, 'warn'); + consoleMock.mockImplementation(); + render( + + Click + , + ); + + expect(consoleMock).toHaveBeenCalledTimes(1); + }); + + it('warns when chevron-right is not used in low emphasis mode', () => { + const consoleMock = jest.spyOn(console, 'warn'); + consoleMock.mockImplementation(); + render( + + Click + , + ); + + expect(consoleMock).toHaveBeenCalledTimes(1); + }); + }); }); diff --git a/src/components/Link/Link.tsx b/src/components/Link/Link.tsx index 55fbba83b..e261e1f25 100644 --- a/src/components/Link/Link.tsx +++ b/src/components/Link/Link.tsx @@ -1,5 +1,6 @@ import clsx from 'clsx'; import React, { forwardRef } from 'react'; +import { assertEdsUsage } from '../../util/logging'; import type { Size } from '../../util/variant-types'; import Icon, { type IconName } from '../Icon'; @@ -83,6 +84,21 @@ export const Link = forwardRef( const iconSize = size && (['xl', 'lg'].includes(size) ? '1.5rem' : '1rem'); + assertEdsUsage( + [context === 'inline' && emphasis === 'low'], + 'Inline links cannot be lowEmphasis', + ); + + assertEdsUsage( + [context === 'inline' && !!icon], + 'Inline links cannot show icons', + ); + + assertEdsUsage( + [icon === 'chevron-right' && emphasis !== 'low'], + 'Icon "chevron-right" only allowed when lowEmphasis is used', + ); + return ( {children} diff --git a/src/components/Modal/Modal.tsx b/src/components/Modal/Modal.tsx index c7a4ba4ff..87ee5fb67 100644 --- a/src/components/Modal/Modal.tsx +++ b/src/components/Modal/Modal.tsx @@ -253,9 +253,7 @@ export const ModalContent = (props: ModalContentProps) => { onClick={onClose} rank="tertiary" variant="neutral" - > - Close - + > )} {children} diff --git a/src/components/PageNotification/PageNotification.tsx b/src/components/PageNotification/PageNotification.tsx index 3d75aa364..a9090f09c 100644 --- a/src/components/PageNotification/PageNotification.tsx +++ b/src/components/PageNotification/PageNotification.tsx @@ -114,9 +114,7 @@ export const PageNotification = ({ rank="tertiary" size="lg" variant="neutral" - > - Close - + > )} ); diff --git a/src/components/Slider/Slider.stories.tsx b/src/components/Slider/Slider.stories.tsx index 472a4c868..c8c43b57a 100644 --- a/src/components/Slider/Slider.stories.tsx +++ b/src/components/Slider/Slider.stories.tsx @@ -198,7 +198,7 @@ export const UsingControlButtons: Story = {
+ > )}
); diff --git a/src/util/findLowestTenMultiplier.ts b/src/util/findLowestTenMultiplier.ts index f9f08c388..8ed94e1ce 100644 --- a/src/util/findLowestTenMultiplier.ts +++ b/src/util/findLowestTenMultiplier.ts @@ -1,3 +1,5 @@ +import { assertEdsUsage } from './logging'; + /** * Returns the lowest multiple of 10 that multiplies with all numbers in a list to make them integers. * Useful for floating point math. @@ -8,12 +10,12 @@ * @returns {number} Lowest multiple of 10. */ export function findLowestTenMultiplier(numbers: number[]): number { - if ( - process.env.NODE_ENV !== 'production' && - numbers.some((number) => !Number.isFinite(number)) - ) { - throw 'Number should be a real finite number'; - } + assertEdsUsage( + [numbers.some((number) => !Number.isFinite(number))], + 'Number should be a real finite number', + 'error', + ); + let multiplier = 1; while ( numbers.some((number) => !Number.isInteger((number * multiplier) % 1)) diff --git a/src/util/logging.ts b/src/util/logging.ts new file mode 100644 index 000000000..1f05aacfd --- /dev/null +++ b/src/util/logging.ts @@ -0,0 +1,22 @@ +import identity from 'lodash/identity'; + +type Check = boolean; +type LogLevel = 'warn' | 'error'; + +/** + * Logging function used to check whether a usage of EDS is proper and advised. When using, it defaults + * to warning, where it will print a message to the console for developers to see. LogLevel supported. + * + * @param checks set of boolean checks to assert whether the component usages are compatible + * @param message Message to print when the component is not being used as advised + * @param [loglevel] Severity of the tracked issue + */ +export function assertEdsUsage( + checks: Check[], + message: string, + loglevel: LogLevel = 'warn', +): void { + if (process.env.NODE_ENV !== 'production' && [...checks].some(identity)) { + console[loglevel](message); + } +}