diff --git a/packages/design-system/package.json b/packages/design-system/package.json index 0b3824c03c0..4549b1160a2 100644 --- a/packages/design-system/package.json +++ b/packages/design-system/package.json @@ -49,6 +49,7 @@ "@babel/core": "^7.22.20", "@cypress/react": "^7.0.3", "@cypress/webpack-dev-server": "^3.6.1", + "@jest/globals": "^29.7.0", "@storybook/addon-a11y": "^7.4.1", "@storybook/addon-actions": "^7.4.1", "@storybook/addon-essentials": "^7.4.1", @@ -69,6 +70,7 @@ "@talend/scripts-config-react-webpack": "^16.0.0", "@testing-library/cypress": "^9.0.0", "@types/classnames": "^2.3.1", + "@types/jest-axe": "^3.5.6", "@types/react": "^17.0.65", "@types/react-dom": "^17.0.20", "@types/react-is": "^17.0.0", @@ -82,7 +84,7 @@ "i18next": "^20.6.1", "i18next-scanner": "^4.4.0", "i18next-scanner-typescript": "^1.1.1", - "jest-axe": "^7.0.1", + "jest-axe": "^8.0.0", "mdx-embed": "^1.1.2", "react": "^17.0.2", "react-dom": "^17.0.2", diff --git a/packages/design-system/src/components/ButtonIcon/ButtonIcon.test.tsx b/packages/design-system/src/components/ButtonIcon/ButtonIcon.test.tsx index e98512f8899..3a5ae653adc 100644 --- a/packages/design-system/src/components/ButtonIcon/ButtonIcon.test.tsx +++ b/packages/design-system/src/components/ButtonIcon/ButtonIcon.test.tsx @@ -1,6 +1,6 @@ import { axe } from 'jest-axe'; import { render } from '@testing-library/react'; -import { ButtonIcon, ButtonIconFloating, ButtonIconToggle } from './'; +import { ButtonIcon } from './'; describe('ButtonIcon', () => { it('should render accessible button', async () => { diff --git a/packages/design-system/src/components/Tabs/Primitive/Tabs.tsx b/packages/design-system/src/components/Tabs/Primitive/Tabs.tsx index 192b4533255..2efcab63ffe 100644 --- a/packages/design-system/src/components/Tabs/Primitive/Tabs.tsx +++ b/packages/design-system/src/components/Tabs/Primitive/Tabs.tsx @@ -14,9 +14,9 @@ export type TabsPropTypes = { export function Tabs({ children }: TabsPropTypes) { return ( - + ); } Tabs.displayName = 'Tabs'; @@ -32,14 +32,14 @@ export type TabPropTypes = { export function Tab(props: TabPropTypes) { const context = useContext(TabsInternalContext); - const content = ( + let content = ( ); if (props.tooltip) { -
  • - {content} -
  • ; + content = {content}; } - return
  • {content}
  • ; + return content; } Tab.displayName = 'Tab'; diff --git a/packages/design-system/src/components/Tabs/Primitive/TabsProvider.tsx b/packages/design-system/src/components/Tabs/Primitive/TabsProvider.tsx index efba097d252..ff899fc2a46 100644 --- a/packages/design-system/src/components/Tabs/Primitive/TabsProvider.tsx +++ b/packages/design-system/src/components/Tabs/Primitive/TabsProvider.tsx @@ -29,10 +29,12 @@ export function TabsProvider(props: TabsProviderPropTypes & WithChildren) { }, }); return ( - - - {props.children} - - + ); } diff --git a/packages/design-system/src/components/Tabs/Tabs.test.tsx b/packages/design-system/src/components/Tabs/Tabs.test.tsx new file mode 100644 index 00000000000..685319ec36c --- /dev/null +++ b/packages/design-system/src/components/Tabs/Tabs.test.tsx @@ -0,0 +1,26 @@ +import { expect } from '@jest/globals'; +import { axe } from 'jest-axe'; +import { render } from '@testing-library/react'; +import { Tabs, TabPanel, Tab, TabsProvider } from './'; + +describe('ButtonIcon', () => { + it('should render accessible button', async () => { + // note we need to add the aria-label to be accessible + // TODO: make it required + const { container } = render( + + + + + + + Tab content for Home + Tab content for Profile + Tab content for Contact + , + ); + expect(container.firstChild).toMatchSnapshot(); + const results = await axe(document.body); + expect(results).toHaveNoViolations(); + }); +}); diff --git a/packages/design-system/src/components/Tabs/__snapshots__/Tabs.test.tsx.snap b/packages/design-system/src/components/Tabs/__snapshots__/Tabs.test.tsx.snap new file mode 100644 index 00000000000..2fcce3c9480 --- /dev/null +++ b/packages/design-system/src/components/Tabs/__snapshots__/Tabs.test.tsx.snap @@ -0,0 +1,87 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`ButtonIcon should render accessible button 1`] = ` + +`; diff --git a/packages/design-system/src/components/Tabs/variants/Tabs.tsx b/packages/design-system/src/components/Tabs/variants/Tabs.tsx deleted file mode 100644 index 7051f979cad..00000000000 --- a/packages/design-system/src/components/Tabs/variants/Tabs.tsx +++ /dev/null @@ -1,70 +0,0 @@ -import { Children, forwardRef, ReactElement, Ref } from 'react'; - -import { TabProps } from '../Primitive/TabElement'; -import TabList from '../Primitive/TabList'; -import TabNavigation from '../Primitive/TabNavigation'; -import TabPanel from '../Primitive/TabPanel'; - -type TabElement = ReactElement; - -export type TabsPropTypes = { - children: TabElement | TabElement[]; - onSelectTab?: (tabId: string) => void; - selectedTab?: string; - size?: 'M' | 'L'; -}; - -const renderTabNavigation = ( - child: TabElement, - onSelectTab?: (tabId: string) => void, - size?: 'M' | 'L', -) => { - const { id, title, icon, tag, tooltip } = child.props; - - return ( - - {title} - - ); -}; - -const renderTabPanel = (child: TabElement) => { - const { id, children } = child.props; - - return ( - - {children} - - ); -}; - -const Tabs = forwardRef( - ({ children, onSelectTab, selectedTab, size }: TabsPropTypes, ref: Ref) => { - return ( -
    - - {Children.map(children, child => { - return renderTabNavigation(child, onSelectTab, size); - })} - - - {Children.map(children, child => { - // Only render selected tab - return selectedTab === child.props.id ? renderTabPanel(child) : undefined; - })} -
    - ); - }, -); - -Tabs.displayName = 'Tabs'; - -export default Tabs; diff --git a/packages/design-system/src/components/Tabs/variants/TabsAsLinkList.module.scss b/packages/design-system/src/components/Tabs/variants/TabsAsLinkList.module.scss deleted file mode 100644 index 0242c1a8fe9..00000000000 --- a/packages/design-system/src/components/Tabs/variants/TabsAsLinkList.module.scss +++ /dev/null @@ -1,7 +0,0 @@ -.tabList { - > ul { - list-style-type: none; - padding: 0; - margin: 0; - } -} diff --git a/packages/design-system/src/components/Tabs/variants/TabsAsLinkList.tsx b/packages/design-system/src/components/Tabs/variants/TabsAsLinkList.tsx deleted file mode 100644 index 2fb3c7d0658..00000000000 --- a/packages/design-system/src/components/Tabs/variants/TabsAsLinkList.tsx +++ /dev/null @@ -1,33 +0,0 @@ -import { forwardRef, Ref } from 'react'; -import TabAsLink, { TabAsLinkProps } from '../Primitive/TabAsLink'; -import { StackHorizontal } from '../../Stack'; -import { DataAttributes } from '../../../types'; - -import styles from './TabsAsLinkList.module.scss'; - -type TabsAsLinkListProps = { - tabs: TabAsLinkProps[]; - size?: 'M' | 'L'; -} & DataAttributes; - -const TabsAsLinkList = forwardRef( - ({ tabs, size = 'M', ...props }: TabsAsLinkListProps, ref: Ref) => { - return ( -
    - - {tabs.map((tab, index) => { - return ( -
  • - -
  • - ); - })} -
    -
    - ); - }, -); - -TabsAsLinkList.displayName = 'TabsAsLinkList'; - -export default TabsAsLinkList; diff --git a/packages/design-system/src/components/Tabs/variants/TabsKit.tsx b/packages/design-system/src/components/Tabs/variants/TabsKit.tsx deleted file mode 100644 index b436f00603e..00000000000 --- a/packages/design-system/src/components/Tabs/variants/TabsKit.tsx +++ /dev/null @@ -1,75 +0,0 @@ -import { createContext, forwardRef, ReactNode, Ref, useContext, useMemo } from 'react'; - -import { IconNameWithSize } from '@talend/icons'; - -import TabList from '../Primitive/TabList'; -import { TabStateReturn, useTabState } from '../Primitive/TabState'; - -export type TabsProps = { - children: ReactNode | ReactNode[]; - selectedId?: string; -}; - -const TabsContext = createContext(null); - -const Tabs = ({ children, ...initialState }: TabsProps) => { - const tabs = useTabState(initialState); - const value = useMemo(() => tabs, [tabs]); - return {children}; -}; - -type TabComponentProps = { - size?: 'M' | 'L'; - id: string; - tooltip?: string; -}; - -type TabComponentPropsWithChildren = TabComponentProps & { - children: ReactNode | ReactNode[]; - - title?: never; - icon?: never; - tag?: never; -}; - -type TabComponentPropsWithTitleProps = TabComponentProps & { - title: string; - icon?: IconNameWithSize<'S'>; - tag?: string | number; - - children?: never; -}; - -const TabComponent = forwardRef( - ( - props: TabComponentPropsWithChildren | TabComponentPropsWithTitleProps, - ref: Ref, - ) => { - const tabs = useContext(TabsContext); - if (!tabs) { - return null; - } - return null; //; - }, -); -TabComponent.displayName = 'Tab'; - -type TabPanelProps = { - id: string; - children: ReactNode | ReactNode[]; -}; - -const TabPanelComponent = forwardRef((props: TabPanelProps, ref: Ref) => { - const tab = useContext(TabsContext); - if (!tab) { - return null; - } - return null; //; -}); -TabPanelComponent.displayName = 'TabPanel'; - -Tabs.Tab = TabComponent; -Tabs.TabList = TabList; -Tabs.TabPanel = TabPanelComponent; - -export default Tabs; diff --git a/yarn.lock b/yarn.lock index b1d616c1e7e..4bce361eb5a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4816,6 +4816,14 @@ dependencies: "@types/istanbul-lib-report" "*" +"@types/jest-axe@^3.5.6": + version "3.5.6" + resolved "https://registry.yarnpkg.com/@types/jest-axe/-/jest-axe-3.5.6.tgz#ae4df83897ed29ec9bd6a99eb7b02965c703bd0d" + integrity sha512-dQyD3XWfB+wUqVqWwGDyW4j7NmLyWWOdFeNIGXl01HR8/n8LeP3BtQTFdygHtInDm/EYKizaNprhgjSr8sPxHA== + dependencies: + "@types/jest" "*" + axe-core "^3.5.5" + "@types/jest@*", "@types/jest@^29.5.5": version "29.5.5" resolved "https://registry.yarnpkg.com/@types/jest/-/jest-29.5.5.tgz#727204e06228fe24373df9bae76b90f3e8236a2a" @@ -6261,10 +6269,15 @@ aws4@^1.8.0: resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.12.0.tgz#ce1c9d143389679e253b314241ea9aa5cec980d3" integrity sha512-NmWvPnx0F1SfrQbYwOi7OeaNGokp9XhzNioJ/CSBs8Qa4vxug81mhJEAVZwxXuBmYB5KDRfMq/F3RR0BIU7sWg== -axe-core@4.5.1: - version "4.5.1" - resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-4.5.1.tgz#04d561c11b6d76d096d34e9d14ba2c294fb20cdc" - integrity sha512-1exVbW0X1O/HSr/WMwnaweyqcWOgZgLiVxdLG34pvSQk4NlYQr9OUy0JLwuhFfuVNQzzqgH57eYzkFBCb3bIsQ== +axe-core@4.7.2: + version "4.7.2" + resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-4.7.2.tgz#040a7342b20765cb18bb50b628394c21bccc17a0" + integrity sha512-zIURGIS1E1Q4pcrMjp+nnEh+16G56eG/MUllJH8yEvw7asDo7Ac9uhC9KIH5jzpITueEZolfYglnCGIuSBz39g== + +axe-core@^3.5.5: + version "3.5.6" + resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-3.5.6.tgz#e762a90d7f6dbd244ceacb4e72760ff8aad521b5" + integrity sha512-LEUDjgmdJoA3LqklSTwKYqkjcZ4HKc4ddIYGSAiSkr46NTjzg2L9RNB+lekO9P7Dlpa87+hBtzc2Fzn/+GUWMQ== axe-core@^4.2.0, axe-core@^4.6.2: version "4.8.1" @@ -11850,12 +11863,12 @@ javascript-natural-sort@0.7.1: resolved "https://registry.yarnpkg.com/javascript-natural-sort/-/javascript-natural-sort-0.7.1.tgz#f9e2303d4507f6d74355a73664d1440fb5a0ef59" integrity sha512-nO6jcEfZWQXDhOiBtG2KvKyEptz7RVbpGP4vTD2hLBdmNQSsCiicO2Ioinv6UI4y9ukqnBpy+XZ9H6uLNgJTlw== -jest-axe@^7.0.1: - version "7.0.1" - resolved "https://registry.yarnpkg.com/jest-axe/-/jest-axe-7.0.1.tgz#ddc81ac979a39d081872cbd2cd32c111c7365031" - integrity sha512-1JoEla6gL4rcsTxEWm+VBcWMwOhP3f9w4dH7/YW3H41nU08Dds3gUFqxgdAq/pzBNPpauC3QPr/BuO+0W8eamg== +jest-axe@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/jest-axe/-/jest-axe-8.0.0.tgz#4d89a1756bda2999a4271e851370981319389155" + integrity sha512-4kNcNn7J0jPO4jANEYZOHeQ/tSBvkXS+MxTbX1CKbXGd0+ZbRGDn/v/8IYWI/MmYX15iLVyYRnRev9X3ksePWA== dependencies: - axe-core "4.5.1" + axe-core "4.7.2" chalk "4.1.2" jest-matcher-utils "29.2.2" lodash.merge "4.6.2"