mirror of
https://github.com/sourcegraph/sourcegraph.git
synced 2026-02-06 17:31:43 +00:00
<Icon />: Update to support @mdi/react instead of mdi-react (#37387)
This commit is contained in:
parent
2a467db469
commit
8f02ed2871
@ -1,12 +1,14 @@
|
||||
import { mdiClose } from '@mdi/js'
|
||||
import { Story, Meta } from '@storybook/react'
|
||||
import CloseIcon from 'mdi-react/CloseIcon'
|
||||
|
||||
import { BrandedStory } from '@sourcegraph/branded/src/components/BrandedStory'
|
||||
import webStyles from '@sourcegraph/web/src/SourcegraphWebApp.scss'
|
||||
|
||||
import { H3 } from '..'
|
||||
import { Icon } from '..'
|
||||
import { H3 } from '../..'
|
||||
import { SourcegraphIcon } from '../SourcegraphIcon'
|
||||
|
||||
import { Icon } from './Icon'
|
||||
import { Code } from '../Typography'
|
||||
|
||||
const config: Meta = {
|
||||
title: 'wildcard/Icon',
|
||||
@ -39,5 +41,15 @@ export const Simple: Story = () => (
|
||||
|
||||
<H3>Medium Icon</H3>
|
||||
<Icon as={SourcegraphIcon} size="md" aria-label="Sourcegraph logo" />
|
||||
|
||||
<H3>
|
||||
Legacy <Code>mdi-react</Code> Icon
|
||||
</H3>
|
||||
<Icon as={CloseIcon} size="md" aria-label="Close" />
|
||||
|
||||
<H3>
|
||||
New <Code>@mdi/react</Code> Icon
|
||||
</H3>
|
||||
<Icon svgPath={mdiClose} size="md" aria-label="Close" />
|
||||
</>
|
||||
)
|
||||
|
||||
@ -1,16 +1,55 @@
|
||||
import { mdiClose } from '@mdi/js'
|
||||
import { render } from '@testing-library/react'
|
||||
import CloseIcon from 'mdi-react/CloseIcon'
|
||||
|
||||
import { SourcegraphIcon } from '../SourcegraphIcon'
|
||||
|
||||
import { Icon } from './Icon'
|
||||
|
||||
describe('Icon', () => {
|
||||
it('renders a simple inline icon correctly', () => {
|
||||
const { asFragment } = render(<Icon as={SourcegraphIcon} aria-hidden={true} />)
|
||||
expect(asFragment()).toMatchSnapshot()
|
||||
describe('custom icons', () => {
|
||||
it('renders a simple inline icon correctly', () => {
|
||||
const { asFragment } = render(<Icon as={SourcegraphIcon} aria-hidden={true} />)
|
||||
expect(asFragment()).toMatchSnapshot()
|
||||
})
|
||||
|
||||
it('renders a medium icon correctly', () => {
|
||||
const { asFragment } = render(<Icon as={SourcegraphIcon} size="md" aria-label="Sourcegraph logo" />)
|
||||
expect(asFragment()).toMatchSnapshot()
|
||||
})
|
||||
})
|
||||
it('renders a medium icon correctly', () => {
|
||||
const { asFragment } = render(<Icon as={SourcegraphIcon} size="md" aria-hidden={true} />)
|
||||
expect(asFragment()).toMatchSnapshot()
|
||||
|
||||
describe('legacy mdi-react icons', () => {
|
||||
it('renders a simple inline icon correctly', () => {
|
||||
const { asFragment } = render(<Icon as={CloseIcon} aria-hidden={true} />)
|
||||
expect(asFragment()).toMatchSnapshot()
|
||||
})
|
||||
|
||||
it('renders a medium icon correctly', () => {
|
||||
const { asFragment } = render(<Icon as={CloseIcon} size="md" aria-label="Checkmark" />)
|
||||
expect(asFragment()).toMatchSnapshot()
|
||||
})
|
||||
})
|
||||
|
||||
describe('new @mdi/react icons', () => {
|
||||
it('renders a simple inline icon correctly', () => {
|
||||
const { asFragment } = render(<Icon svgPath={mdiClose} aria-hidden={true} />)
|
||||
expect(asFragment()).toMatchSnapshot()
|
||||
})
|
||||
it('renders a medium icon correctly', () => {
|
||||
const { asFragment } = render(<Icon svgPath={mdiClose} size="md" aria-label="Checkmark" />)
|
||||
expect(asFragment()).toMatchSnapshot()
|
||||
})
|
||||
})
|
||||
|
||||
describe('styled as icons', () => {
|
||||
it('renders a simple span', () => {
|
||||
const { asFragment } = render(
|
||||
<Icon as="span" aria-label="Smile">
|
||||
:)
|
||||
</Icon>
|
||||
)
|
||||
expect(asFragment()).toMatchSnapshot()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import React, { ComponentType, ElementType, PropsWithChildren } from 'react'
|
||||
import React, { AriaRole, ComponentType, ElementType } from 'react'
|
||||
|
||||
import MDIIcon from '@mdi/react'
|
||||
import classNames from 'classnames'
|
||||
import { MdiReactIconProps } from 'mdi-react'
|
||||
|
||||
@ -9,12 +10,19 @@ import { ICON_SIZES } from './constants'
|
||||
|
||||
import styles from './Icon.module.scss'
|
||||
|
||||
interface BaseIconProps extends Omit<MdiReactIconProps, 'children'> {
|
||||
className?: string
|
||||
interface BaseIconProps extends Omit<React.ComponentProps<typeof MDIIcon>, 'size' | 'path' | 'color'> {
|
||||
/**
|
||||
* Provide a custom `svgPath` to build an SVG.
|
||||
*
|
||||
* If using a Material Design icon, simply import the path from '@mdj/js'.
|
||||
*/
|
||||
svgPath?: string
|
||||
/**
|
||||
* The variant style of the icon. defaults to 'sm'
|
||||
*/
|
||||
size?: typeof ICON_SIZES[number]
|
||||
className?: string
|
||||
role?: AriaRole
|
||||
}
|
||||
|
||||
interface ScreenReaderIconProps extends BaseIconProps {
|
||||
@ -25,25 +33,33 @@ interface HiddenIconProps extends BaseIconProps {
|
||||
'aria-hidden': true | 'true'
|
||||
}
|
||||
|
||||
// We're currently migrating our icons to provide a descriptive label or use aria-hidden to be excluded from screen readers.
|
||||
// Migration issue: https://github.com/sourcegraph/sourcegraph/issues/34582
|
||||
// TODO: We should enforce that these props are provided once that migration is complete.
|
||||
export type IconProps = HiddenIconProps | ScreenReaderIconProps
|
||||
|
||||
export const Icon = React.forwardRef((props, reference) => {
|
||||
const { children, inline = true, className, size, as: Component = 'svg', role = 'img', ...attributes } = props
|
||||
// eslint-disable-next-line react/display-name
|
||||
export const Icon = React.forwardRef(({ children, className, size, ...props }, reference) => {
|
||||
const iconStyle = classNames(styles.iconInline, size === 'md' && styles.iconInlineMd, className)
|
||||
|
||||
if (props.svgPath) {
|
||||
const { svgPath, 'aria-label': ariaLabel, ...attributes } = props
|
||||
|
||||
return (
|
||||
<MDIIcon
|
||||
ref={reference as React.RefObject<SVGSVGElement>}
|
||||
path={svgPath}
|
||||
className={iconStyle}
|
||||
title={ariaLabel}
|
||||
{...attributes}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
const { as: IconComponent = 'svg', role = 'img', ...attributes } = props
|
||||
|
||||
return (
|
||||
<Component
|
||||
className={classNames(styles.iconInline, size === 'md' && styles.iconInlineMd, className)}
|
||||
ref={reference}
|
||||
role={role}
|
||||
{...attributes}
|
||||
>
|
||||
<IconComponent ref={reference} className={iconStyle} role={role} {...attributes}>
|
||||
{children}
|
||||
</Component>
|
||||
</IconComponent>
|
||||
)
|
||||
}) as ForwardReferenceComponent<
|
||||
ComponentType<React.PropsWithChildren<MdiReactIconProps>> | ElementType,
|
||||
PropsWithChildren<IconProps>
|
||||
>
|
||||
}) as ForwardReferenceComponent<ComponentType<React.PropsWithChildren<MdiReactIconProps>> | ElementType, IconProps>
|
||||
|
||||
Icon.displayName = 'Icon'
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`Icon renders a medium icon correctly 1`] = `
|
||||
exports[`Icon custom icons renders a medium icon correctly 1`] = `
|
||||
<DocumentFragment>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
aria-label="Sourcegraph logo"
|
||||
class="iconInline iconInlineMd"
|
||||
fill="none"
|
||||
role="img"
|
||||
@ -26,7 +26,7 @@ exports[`Icon renders a medium icon correctly 1`] = `
|
||||
</DocumentFragment>
|
||||
`;
|
||||
|
||||
exports[`Icon renders a simple inline icon correctly 1`] = `
|
||||
exports[`Icon custom icons renders a simple inline icon correctly 1`] = `
|
||||
<DocumentFragment>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
@ -51,3 +51,87 @@ exports[`Icon renders a simple inline icon correctly 1`] = `
|
||||
</svg>
|
||||
</DocumentFragment>
|
||||
`;
|
||||
|
||||
exports[`Icon legacy mdi-react icons renders a medium icon correctly 1`] = `
|
||||
<DocumentFragment>
|
||||
<svg
|
||||
aria-label="Checkmark"
|
||||
class="mdi-icon iconInline iconInlineMd"
|
||||
fill="currentColor"
|
||||
height="24"
|
||||
role="img"
|
||||
viewBox="0 0 24 24"
|
||||
width="24"
|
||||
>
|
||||
<path
|
||||
d="M19,6.41L17.59,5L12,10.59L6.41,5L5,6.41L10.59,12L5,17.59L6.41,19L12,13.41L17.59,19L19,17.59L13.41,12L19,6.41Z"
|
||||
/>
|
||||
</svg>
|
||||
</DocumentFragment>
|
||||
`;
|
||||
|
||||
exports[`Icon legacy mdi-react icons renders a simple inline icon correctly 1`] = `
|
||||
<DocumentFragment>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
class="mdi-icon iconInline"
|
||||
fill="currentColor"
|
||||
height="24"
|
||||
role="img"
|
||||
viewBox="0 0 24 24"
|
||||
width="24"
|
||||
>
|
||||
<path
|
||||
d="M19,6.41L17.59,5L12,10.59L6.41,5L5,6.41L10.59,12L5,17.59L6.41,19L12,13.41L17.59,19L19,17.59L13.41,12L19,6.41Z"
|
||||
/>
|
||||
</svg>
|
||||
</DocumentFragment>
|
||||
`;
|
||||
|
||||
exports[`Icon new @mdi/react icons renders a medium icon correctly 1`] = `
|
||||
<DocumentFragment>
|
||||
<svg
|
||||
aria-labelledby="icon_labelledby_2"
|
||||
class="iconInline iconInlineMd"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<title
|
||||
id="icon_labelledby_2"
|
||||
>
|
||||
Checkmark
|
||||
</title>
|
||||
<path
|
||||
d="M19,6.41L17.59,5L12,10.59L6.41,5L5,6.41L10.59,12L5,17.59L6.41,19L12,13.41L17.59,19L19,17.59L13.41,12L19,6.41Z"
|
||||
style="fill: currentColor;"
|
||||
/>
|
||||
</svg>
|
||||
</DocumentFragment>
|
||||
`;
|
||||
|
||||
exports[`Icon new @mdi/react icons renders a simple inline icon correctly 1`] = `
|
||||
<DocumentFragment>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
class="iconInline"
|
||||
role="presentation"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
d="M19,6.41L17.59,5L12,10.59L6.41,5L5,6.41L10.59,12L5,17.59L6.41,19L12,13.41L17.59,19L19,17.59L13.41,12L19,6.41Z"
|
||||
style="fill: currentColor;"
|
||||
/>
|
||||
</svg>
|
||||
</DocumentFragment>
|
||||
`;
|
||||
|
||||
exports[`Icon styled as icons renders a simple span 1`] = `
|
||||
<DocumentFragment>
|
||||
<span
|
||||
aria-label="Smile"
|
||||
class="iconInline"
|
||||
role="img"
|
||||
>
|
||||
:)
|
||||
</span>
|
||||
</DocumentFragment>
|
||||
`;
|
||||
|
||||
@ -362,6 +362,8 @@
|
||||
"@codemirror/state": "^0.20.0",
|
||||
"@codemirror/view": "^0.20.4",
|
||||
"@lezer/highlight": "^0.16.0",
|
||||
"@mdi/js": "^6.7.96",
|
||||
"@mdi/react": "^1.6.0",
|
||||
"@microsoft/fetch-event-source": "^2.0.1",
|
||||
"@radix-ui/react-tooltip": "^0.1.8 || 0.1.8-rc.2",
|
||||
"@reach/accordion": "^0.16.1",
|
||||
|
||||
12
yarn.lock
12
yarn.lock
@ -2864,6 +2864,18 @@
|
||||
find-up "^4.1.0"
|
||||
fs-extra "^8.1.0"
|
||||
|
||||
"@mdi/js@^6.7.96":
|
||||
version "6.7.96"
|
||||
resolved "https://registry.npmjs.org/@mdi/js/-/js-6.7.96.tgz#6cd13ba64eeaf347c857feb3c328d31af3e75876"
|
||||
integrity sha512-vZvhFrNN9LQx+Awu3nU6ESfXXDpRA/CA4mwikzU5g8uf9NpAocK43ecQvVNntwiXlLKpyplas8d4lsfpqjtXLA==
|
||||
|
||||
"@mdi/react@^1.6.0":
|
||||
version "1.6.0"
|
||||
resolved "https://registry.npmjs.org/@mdi/react/-/react-1.6.0.tgz#09b470c6b444a626206a1bd8901abcecf56bb3eb"
|
||||
integrity sha512-FdeJ3Ee+m9nbBkZXW0XgSkDvevgOCloLRFrBlF6pRGWgT8v2U1M2aeXi8+uRM5FZ58o3dfVFduAtmCq7quclNA==
|
||||
dependencies:
|
||||
prop-types "^15.7.2"
|
||||
|
||||
"@mdn/browser-compat-data@2.0.7":
|
||||
version "2.0.7"
|
||||
resolved "https://registry.npmjs.org/@mdn/browser-compat-data/-/browser-compat-data-2.0.7.tgz#72ec37b9c1e00ce0b4e0309d753be18e2da12ee3"
|
||||
|
||||
Loading…
Reference in New Issue
Block a user