Scalable
diff --git a/packages/design-system/.eslintrc.json b/packages/design-system/.eslintrc.json
index 3f455118522..8464e11418a 100644
--- a/packages/design-system/.eslintrc.json
+++ b/packages/design-system/.eslintrc.json
@@ -1,7 +1,4 @@
{
"root": true,
- "extends": ["@talend"],
- "rules": {
- "react/display-name": "warn"
- }
+ "extends": "@talend"
}
diff --git a/packages/design-system/package.json b/packages/design-system/package.json
index d0968ba8a86..4549b1160a2 100644
--- a/packages/design-system/package.json
+++ b/packages/design-system/package.json
@@ -10,8 +10,9 @@
"build:lib:umd:min": "talend-scripts build --umd --prod",
"pre-release": "npm run build:lib:umd && npm run build:lib:umd:min",
"watch": "talend-scripts build --watch",
- "test": "echo Tests are managed by Cypress using yarn test:cy",
+ "test": "talend-scripts test",
"test:cy": "cypress run --component --spec **.cy.tsx",
+ "cy:run": "cypress run --component",
"extract-i18n": "i18next-scanner --config i18next-scanner.config.js",
"lint": "talend-scripts lint",
"start": "talend-scripts start-storybook"
@@ -31,14 +32,16 @@
"access": "public"
},
"dependencies": {
+ "@floating-ui/react": "^0.24.2",
"@talend/assets-api": "^1.2.2",
"@talend/design-tokens": "^2.9.0",
+ "@talend/utils": "^2.5.1",
"classnames": "^2.3.2",
"keycode": "^2.2.1",
"modern-css-reset": "^1.4.0",
"polished": "^4.2.2",
"react-use": "^17.4.0",
- "reakit": "^1.3.11",
+ "react-transition-group": "^2.2.9",
"typeface-inconsolata": "^1.1.13",
"typeface-source-sans-pro": "^1.1.13"
},
@@ -46,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",
@@ -66,9 +70,11 @@
"@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": "^16.7.2",
+ "@types/react-is": "^17.0.0",
+ "@types/react-transition-group": "2.9.2",
"browser-sync": "^2.29.3",
"browser-sync-webpack-plugin": "^2.3.0",
"concurrently": "^7.6.0",
@@ -78,12 +84,13 @@
"i18next": "^20.6.1",
"i18next-scanner": "^4.4.0",
"i18next-scanner-typescript": "^1.1.1",
+ "jest-axe": "^8.0.0",
"mdx-embed": "^1.1.2",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react-helmet": "^6.1.0",
"react-i18next": "^11.18.6",
- "react-is": "^16.13.1",
+ "react-is": "^17.0.0",
"react-router-dom": "~6.3.0",
"storybook-docs-toc": "^1.7.0"
},
@@ -94,6 +101,6 @@
"react": "^16.14.0 || ^17.0.0 || ^18.0.0",
"react-dom": "^16.14.0 || ^17.0.0 || ^18.0.0",
"react-i18next": "^11.18.6",
- "react-is": "^16.13.1"
+ "react-is": "^16.13.1 || ^17.0.0 || ^18.0.0"
}
}
diff --git a/packages/design-system/src/components/WIP/Accordion/Accordion.cy.tsx b/packages/design-system/src/components/Accordion/Accordion.cy.tsx
similarity index 97%
rename from packages/design-system/src/components/WIP/Accordion/Accordion.cy.tsx
rename to packages/design-system/src/components/Accordion/Accordion.cy.tsx
index 32d2abc31d2..cf80ce4157d 100644
--- a/packages/design-system/src/components/WIP/Accordion/Accordion.cy.tsx
+++ b/packages/design-system/src/components/Accordion/Accordion.cy.tsx
@@ -40,9 +40,9 @@ context('
', () => {
it('should expand and collapse', () => {
cy.mount(
);
- cy.get('#CollapsiblePanel__control--panel-with-action').click();
+ cy.get('#CollapsiblePanel__control--panel-with-action').focus().click();
cy.findByTestId('panel.section').should('be.visible');
- cy.get('#CollapsiblePanel__control--panel-with-action').click();
+ cy.get('#CollapsiblePanel__control--panel-with-action').focus().click();
cy.findByTestId('panel.section').should('not.exist');
});
diff --git a/packages/design-system/src/components/Accordion/Accordion.test.tsx b/packages/design-system/src/components/Accordion/Accordion.test.tsx
new file mode 100644
index 00000000000..4054c71c715
--- /dev/null
+++ b/packages/design-system/src/components/Accordion/Accordion.test.tsx
@@ -0,0 +1,50 @@
+import { describe, it, expect } from '@jest/globals';
+import { axe } from 'jest-axe';
+import { render } from '@testing-library/react';
+import { Accordion, CollapsiblePanel } from './';
+
+jest.mock('@talend/utils', () => {
+ let i = 0;
+ return {
+ // we need stable but different uuid (is fixed to 42 by current mock)
+ randomUUID: () => `mocked-uuid-${i++}`,
+ };
+});
+
+describe('Accordion', () => {
+ it('should render a11y html', async () => {
+ const { container } = render(
+
+
+
+
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi quis nisl convallis.
+
+
+
+
+ In hac habitasse platea dictumst. Etiam sed orci ullamcorper, venenatis tellus ut,
+ cursus nulla.
+
+
+
+ Cras orci neque, placerat non rutrum eleifend, convallis at lorem.
+
+
+
+ Morbi consectetur vehicula dignissim. Phasellus ullamcorper risus in erat accumsan, eu
+ ullamcorper ante dictum.
+
+
+
+ Duis orci neque, vehicula eget pulvinar quis, pellentesque non odio.
+
+
+ ,
+ ,
+ );
+ expect(container.firstChild).toMatchSnapshot();
+ const results = await axe(document.body);
+ expect(results).toHaveNoViolations();
+ });
+});
diff --git a/packages/design-system/src/components/WIP/Accordion/Accordion.tsx b/packages/design-system/src/components/Accordion/Accordion.tsx
similarity index 80%
rename from packages/design-system/src/components/WIP/Accordion/Accordion.tsx
rename to packages/design-system/src/components/Accordion/Accordion.tsx
index edba973f05f..479e67a74c4 100644
--- a/packages/design-system/src/components/WIP/Accordion/Accordion.tsx
+++ b/packages/design-system/src/components/Accordion/Accordion.tsx
@@ -1,10 +1,11 @@
-import { Children, cloneElement, ReactElement, useState } from 'react';
+import { Children, cloneElement, useState } from 'react';
+import type { ReactElement } from 'react';
-export type AccordionPropsType = {
+export type AccordionProps = {
children: ReactElement[];
};
-const Accordion = ({ children }: AccordionPropsType) => {
+export const Accordion = ({ children }: AccordionProps) => {
const panelCount = Children.count(children);
const [openedPanelIndex, setOpenedPanelIndex] = useState
(null);
@@ -37,5 +38,3 @@ const Accordion = ({ children }: AccordionPropsType) => {
};
Accordion.displayName = 'Accordion';
-
-export default Accordion;
diff --git a/packages/design-system/src/components/WIP/Accordion/Primitive/CollapsiblePanel.module.scss b/packages/design-system/src/components/Accordion/Primitive/CollapsiblePanel.module.scss
similarity index 100%
rename from packages/design-system/src/components/WIP/Accordion/Primitive/CollapsiblePanel.module.scss
rename to packages/design-system/src/components/Accordion/Primitive/CollapsiblePanel.module.scss
diff --git a/packages/design-system/src/components/WIP/Accordion/Primitive/CollapsiblePanel.tsx b/packages/design-system/src/components/Accordion/Primitive/CollapsiblePanel.tsx
similarity index 82%
rename from packages/design-system/src/components/WIP/Accordion/Primitive/CollapsiblePanel.tsx
rename to packages/design-system/src/components/Accordion/Primitive/CollapsiblePanel.tsx
index c1f14fdc99e..2e6d8408b29 100644
--- a/packages/design-system/src/components/WIP/Accordion/Primitive/CollapsiblePanel.tsx
+++ b/packages/design-system/src/components/Accordion/Primitive/CollapsiblePanel.tsx
@@ -1,10 +1,9 @@
import { forwardRef, ReactChild, Ref, useState, useEffect, HTMLAttributes } from 'react';
import classNames from 'classnames';
-import { unstable_useId as useId } from 'reakit/Id';
-import { DataAttributes } from '../../../../types';
-
-import { variants } from '../../../Status/Primitive/StatusPrimitive';
+import { useId } from '../../../useId';
+import { DataAttributes } from '../../../types';
+import { variants } from '../../Status/Primitive/StatusPrimitive';
import CollapsiblePanelHeader from './CollapsiblePanelHeader';
import { PanelHeaderAction } from './types';
@@ -25,20 +24,20 @@ type CollapsiblePanelCommonPropsType = {
} & Omit, 'className' | 'style'> &
DataAttributes;
-export type CollapsiblePanelWithTitlePropsType = {
+type CollapsiblePanelWithTitlePropsType = {
title: ReactChild;
status?: never;
};
-export type CollapsiblePanelWithStatusPropsType = {
+type CollapsiblePanelWithStatusPropsType = {
title?: never;
status: keyof typeof variants;
};
-export type CollapsiblePanelPropsType = CollapsiblePanelCommonPropsType &
+export type CollapsiblePanelProps = CollapsiblePanelCommonPropsType &
(CollapsiblePanelWithTitlePropsType | CollapsiblePanelWithStatusPropsType);
-const CollapsiblePanel = forwardRef(
+export const CollapsiblePanel = forwardRef(
(
{
id,
@@ -56,13 +55,12 @@ const CollapsiblePanel = forwardRef(
isLast = false,
disabled = false,
...rest
- }: CollapsiblePanelPropsType,
+ }: CollapsiblePanelProps,
ref: Ref,
) => {
const [localExpanded, setLocalExpanded] = useState(!!expanded);
- const { id: reakitId } = useId();
- const componentId = id || reakitId;
+ const componentId = useId(id);
const controlId = `CollapsiblePanel__control--${componentId}`;
const sectionId = `CollapsiblePanel__content--${componentId}`;
@@ -121,5 +119,3 @@ const CollapsiblePanel = forwardRef(
);
CollapsiblePanel.displayName = 'CollapsiblePanel';
-
-export default CollapsiblePanel;
diff --git a/packages/design-system/src/components/WIP/Accordion/Primitive/CollapsiblePanelHeader.module.scss b/packages/design-system/src/components/Accordion/Primitive/CollapsiblePanelHeader.module.scss
similarity index 100%
rename from packages/design-system/src/components/WIP/Accordion/Primitive/CollapsiblePanelHeader.module.scss
rename to packages/design-system/src/components/Accordion/Primitive/CollapsiblePanelHeader.module.scss
diff --git a/packages/design-system/src/components/WIP/Accordion/Primitive/CollapsiblePanelHeader.tsx b/packages/design-system/src/components/Accordion/Primitive/CollapsiblePanelHeader.tsx
similarity index 91%
rename from packages/design-system/src/components/WIP/Accordion/Primitive/CollapsiblePanelHeader.tsx
rename to packages/design-system/src/components/Accordion/Primitive/CollapsiblePanelHeader.tsx
index 506dfd2ae8d..d6933c2c1fd 100644
--- a/packages/design-system/src/components/WIP/Accordion/Primitive/CollapsiblePanelHeader.tsx
+++ b/packages/design-system/src/components/Accordion/Primitive/CollapsiblePanelHeader.tsx
@@ -3,12 +3,12 @@ import classnames from 'classnames';
import tokens from '@talend/design-tokens';
-import { ButtonIcon } from '../../../ButtonIcon';
-import { SizedIcon } from '../../../Icon';
-import Divider from '../../../Divider';
-import { StackHorizontal } from '../../../Stack';
-import { Status } from '../../../Status';
-import { variants } from '../../../Status/Primitive/StatusPrimitive';
+import { ButtonIcon } from '../../ButtonIcon';
+import { SizedIcon } from '../../Icon';
+import { Divider } from '../../Divider';
+import { StackHorizontal } from '../../Stack';
+import { Status } from '../../Status';
+import { variants } from '../../Status/Primitive/StatusPrimitive';
import { PanelHeaderAction } from './types';
import styles from './CollapsiblePanelHeader.module.scss';
@@ -63,6 +63,7 @@ const CollapsiblePanelHeader = forwardRef(
return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ,
+
+`;
diff --git a/packages/design-system/src/components/Accordion/index.ts b/packages/design-system/src/components/Accordion/index.ts
new file mode 100644
index 00000000000..be9e4624973
--- /dev/null
+++ b/packages/design-system/src/components/Accordion/index.ts
@@ -0,0 +1,2 @@
+export * from './Accordion';
+export * from './Primitive/CollapsiblePanel';
diff --git a/packages/design-system/src/components/Badge/Badge.test.tsx b/packages/design-system/src/components/Badge/Badge.test.tsx
new file mode 100644
index 00000000000..ba54febe539
--- /dev/null
+++ b/packages/design-system/src/components/Badge/Badge.test.tsx
@@ -0,0 +1,52 @@
+import { describe, it, expect } from '@jest/globals';
+import { axe } from 'jest-axe';
+import { render } from '@testing-library/react';
+import { Badge, BadgeDropdown } from './';
+
+jest.mock('@talend/utils', () => {
+ let i = 0;
+ return {
+ // we need stable but different uuid (is fixed to 42 by current mock)
+ randomUUID: () => `mocked-uuid-${i++}`,
+ };
+});
+
+describe('Badge', () => {
+ it('should render a11y html', async () => {
+ const selectedValue = '3';
+ const setSelectedValue = jest.fn();
+ const { container } = render(
+
+
+
+
+
+
+
+ TODO: add popover and tag
+ ,
+ );
+ expect(container.firstChild).toMatchSnapshot();
+ const results = await axe(document.body);
+ expect(results).toHaveNoViolations();
+ });
+});
diff --git a/packages/design-system/src/components/Badge/__snapshots__/Badge.test.tsx.snap b/packages/design-system/src/components/Badge/__snapshots__/Badge.test.tsx.snap
new file mode 100644
index 00000000000..764475d9b55
--- /dev/null
+++ b/packages/design-system/src/components/Badge/__snapshots__/Badge.test.tsx.snap
@@ -0,0 +1,273 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`Badge should render a11y html 1`] = `
+
+
+
+
+
+ Awesome
+
+
+
+
+
+
+ Component
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Feature
+
+
+
+
+
+
+ Item
+
+
+
+
+
+
+ Component
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Awesome
+
+
+
+
+
+
+ Component
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Feature
+
+
+
+
+
+
+ Item
+
+
+
+
+
+
+ Component
+
+
+
+
+
+
+
+
+
+ TODO: add popover and tag
+
+`;
diff --git a/packages/design-system/src/components/Badge/button/BadgeButton.tsx b/packages/design-system/src/components/Badge/button/BadgeButton.tsx
index b0d9b75c518..b8779591997 100644
--- a/packages/design-system/src/components/Badge/button/BadgeButton.tsx
+++ b/packages/design-system/src/components/Badge/button/BadgeButton.tsx
@@ -2,8 +2,8 @@ import { forwardRef, Ref } from 'react';
import classnames from 'classnames';
import styles from './BadgeButton.module.scss';
-import Clickable from '../../Clickable';
import { DataAttributes } from 'src/types';
+import { Clickable } from '../../Clickable/Clickable';
type BadgeButtonProps = {
/**
diff --git a/packages/design-system/src/components/Badge/primitive/BadgePrimitive.tsx b/packages/design-system/src/components/Badge/primitive/BadgePrimitive.tsx
index 0bcbec4e92d..97a5d3c0586 100644
--- a/packages/design-system/src/components/Badge/primitive/BadgePrimitive.tsx
+++ b/packages/design-system/src/components/Badge/primitive/BadgePrimitive.tsx
@@ -1,6 +1,6 @@
import { Children, PropsWithChildren, Ref } from 'react';
-import Divider from '../../Divider';
+import { Divider } from '../../Divider';
import classnames from 'classnames';
import styles from './BadgePrimitive.module.scss';
diff --git a/packages/design-system/src/components/Badge/variants/BadgeDropdown.tsx b/packages/design-system/src/components/Badge/variants/BadgeDropdown.tsx
index 6d3b84181f6..e419df1d6b6 100644
--- a/packages/design-system/src/components/Badge/variants/BadgeDropdown.tsx
+++ b/packages/design-system/src/components/Badge/variants/BadgeDropdown.tsx
@@ -2,8 +2,7 @@ import { forwardRef, Ref } from 'react';
import { useTranslation } from 'react-i18next';
import { I18N_DOMAIN_DESIGN_SYSTEM } from '../../constants';
-import Dropdown from '../../Dropdown';
-import { DropdownItemType } from '../../Dropdown/Dropdown';
+import { Dropdown, DropdownItemType } from '../../Dropdown';
import { SizedIcon } from '../../Icon';
import { StackHorizontal } from '../../Stack';
diff --git a/packages/design-system/src/components/Badge/variants/BadgePopover.tsx b/packages/design-system/src/components/Badge/variants/BadgePopover.tsx
index 77b76da3c1e..e3a15f028e5 100644
--- a/packages/design-system/src/components/Badge/variants/BadgePopover.tsx
+++ b/packages/design-system/src/components/Badge/variants/BadgePopover.tsx
@@ -1,6 +1,6 @@
import { Fragment, forwardRef, Ref } from 'react';
-import Divider from '../../Divider';
+import { Divider } from '../../Divider';
import { StackHorizontal } from '../../Stack';
import BadgeButton from '../button/BadgeButton';
import BadgePrimitive, { BadgePopoverItem, BadgePrimitiveProps } from '../primitive/BadgePrimitive';
diff --git a/packages/design-system/src/components/Badge/variants/BadgeValue.tsx b/packages/design-system/src/components/Badge/variants/BadgeValue.tsx
index 8f28a2331b6..265d8ce0e48 100644
--- a/packages/design-system/src/components/Badge/variants/BadgeValue.tsx
+++ b/packages/design-system/src/components/Badge/variants/BadgeValue.tsx
@@ -4,7 +4,7 @@ import BadgePrimitive, { BadgePrimitiveProps } from '../primitive/BadgePrimitive
import classnames from 'classnames';
import styles from './BadgeValue.module.scss';
import { StackHorizontal } from '../../Stack';
-import Divider from '../../Divider';
+import { Divider } from '../../Divider';
export type BadgeValueProps = BadgePrimitiveProps & {
/**
diff --git a/packages/design-system/src/components/Breadcrumbs/Breadcrumbs.test.tsx b/packages/design-system/src/components/Breadcrumbs/Breadcrumbs.test.tsx
new file mode 100644
index 00000000000..65d8c217c40
--- /dev/null
+++ b/packages/design-system/src/components/Breadcrumbs/Breadcrumbs.test.tsx
@@ -0,0 +1,54 @@
+import { describe, it, expect } from '@jest/globals';
+import { axe } from 'jest-axe';
+import { render } from '@testing-library/react';
+import { Breadcrumbs } from './';
+
+jest.mock('@talend/utils', () => {
+ let i = 0;
+ return {
+ // we need stable but different uuid (is fixed to 42 by current mock)
+ randomUUID: () => `mocked-uuid-${i++}`,
+ };
+});
+
+describe('Breadcrumbs', () => {
+ it('should render a11y html', async () => {
+ const { container } = render(
+
+
+ ,
+ );
+ expect(container.firstChild).toMatchSnapshot();
+ const results = await axe(document.body);
+ expect(results).toHaveNoViolations();
+ });
+});
diff --git a/packages/design-system/src/components/Breadcrumbs/Breadcrumbs.tsx b/packages/design-system/src/components/Breadcrumbs/Breadcrumbs.tsx
index a445262e034..1328d20528a 100644
--- a/packages/design-system/src/components/Breadcrumbs/Breadcrumbs.tsx
+++ b/packages/design-system/src/components/Breadcrumbs/Breadcrumbs.tsx
@@ -3,12 +3,12 @@ import { useTranslation } from 'react-i18next';
import classnames from 'classnames';
import styles from './Breadcrumbs.module.scss';
-import Link from '../Link';
-import Dropdown from '../Dropdown/Dropdown';
+import { Link } from '../Link';
+import { Dropdown } from '../Dropdown/Dropdown';
import { ButtonTertiary } from '../Button';
import { StackHorizontal } from '../Stack';
-import Divider from '../Divider';
-import VisuallyHidden from '../VisuallyHidden';
+import { Divider } from '../Divider';
+import { VisuallyHidden } from '../VisuallyHidden';
import { I18N_DOMAIN_DESIGN_SYSTEM } from '../constants';
type BreadcrumbsLink = {
@@ -25,7 +25,7 @@ type BreadcrumbsRouterLink = {
type BreadcrumbsItem = (BreadcrumbsRouterLink | BreadcrumbsLink)[];
-type BreadCrumbsProps = Omit, 'className' | 'style'> & {
+export type BreadCrumbsProps = Omit, 'className' | 'style'> & {
items: BreadcrumbsItem;
};
@@ -61,82 +61,84 @@ function BreadcrumbLink({
);
}
-const Breadcrumbs = forwardRef(({ items, ...rest }: BreadCrumbsProps, ref: Ref) => {
- const { t } = useTranslation(I18N_DOMAIN_DESIGN_SYSTEM);
- const buildEntries = () => {
- if (items.length > maxBreadcrumbsItemLength) {
- const origin = items[0];
- const suffix = items.slice(-2);
- const collapsed = items.slice(1, items.length - 2);
+export const Breadcrumbs = forwardRef(
+ ({ items, ...rest }: BreadCrumbsProps, ref: Ref) => {
+ const { t } = useTranslation(I18N_DOMAIN_DESIGN_SYSTEM);
+ const buildEntries = () => {
+ if (items.length > maxBreadcrumbsItemLength) {
+ const origin = items[0];
+ const suffix = items.slice(-2);
+ const collapsed = items.slice(1, items.length - 2);
- return (
- <>
-
+ return (
+ <>
+
-
-
- {
- const refinedProp =
- 'href' in collapsedLinks
- ? { href: collapsedLinks.href }
- : { as: collapsedLinks.as };
- return {
- label: collapsedLinks.label,
- target: collapsedLinks.target,
- type: 'link',
- ...refinedProp,
- };
- })}
- >
- {}}>
-
- {t('COLLAPSED_LINKS_BUTTON', 'Display collapsed links')}
-
- …
-
-
-
-
-
-
-
+
+
+ {
+ const refinedProp =
+ 'href' in collapsedLinks
+ ? { href: collapsedLinks.href }
+ : { as: collapsedLinks.as };
+ return {
+ label: collapsedLinks.label,
+ target: collapsedLinks.target,
+ type: 'link',
+ ...refinedProp,
+ };
+ })}
+ >
+ {}}>
+
+ {t('COLLAPSED_LINKS_BUTTON', 'Display collapsed links')}
+
+ …
+
+
+
+
+
+
+
- {suffix.map((entry, index) => {
- const isLastEntry = index === suffix.length - 1;
- return (
-
- );
- })}
- >
- );
- }
+ {suffix.map((entry, index) => {
+ const isLastEntry = index === suffix.length - 1;
+ return (
+
+ );
+ })}
+ >
+ );
+ }
- return items.map((entry, index) => {
- const isLastEntry = index === items.length - 1;
- return (
-
- );
- });
- };
+ return items.map((entry, index) => {
+ const isLastEntry = index === items.length - 1;
+ return (
+
+ );
+ });
+ };
- return (
-
-
- {buildEntries()}
-
-
- );
-});
+ return (
+
+
+ {buildEntries()}
+
+
+ );
+ },
+);
-export default Breadcrumbs;
+Breadcrumbs.displayName = 'Breadcrumbs';
diff --git a/packages/design-system/src/components/Breadcrumbs/__snapshots__/Breadcrumbs.test.tsx.snap b/packages/design-system/src/components/Breadcrumbs/__snapshots__/Breadcrumbs.test.tsx.snap
new file mode 100644
index 00000000000..e52fe526530
--- /dev/null
+++ b/packages/design-system/src/components/Breadcrumbs/__snapshots__/Breadcrumbs.test.tsx.snap
@@ -0,0 +1,195 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`Breadcrumbs should render a11y html 1`] = `
+
+
+
+
+
+
+
+
+
+
+
+ Display collapsed links
+
+
+ …
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+`;
diff --git a/packages/design-system/src/components/Breadcrumbs/index.ts b/packages/design-system/src/components/Breadcrumbs/index.ts
new file mode 100644
index 00000000000..ce977548b14
--- /dev/null
+++ b/packages/design-system/src/components/Breadcrumbs/index.ts
@@ -0,0 +1 @@
+export * from './Breadcrumbs';
diff --git a/packages/design-system/src/components/Button/Button.cy.tsx b/packages/design-system/src/components/Button/Button.cy.tsx
index f596d2996d4..28e77422666 100644
--- a/packages/design-system/src/components/Button/Button.cy.tsx
+++ b/packages/design-system/src/components/Button/Button.cy.tsx
@@ -5,7 +5,7 @@ import { useState } from 'react';
import ButtonPrimitive from './Primitive/ButtonPrimitive';
import { ButtonPrimary } from './';
-import Tooltip from '../../components/Tooltip';
+import { Tooltip } from '../../components/Tooltip';
const Loading = ({ 'data-testid': dataTestId }: { 'data-testid': string }) => {
// eslint-disable-next-line react-hooks/rules-of-hooks
@@ -55,21 +55,20 @@ context(' ', () => {
describe('loading state', () => {
it('should load', () => {
cy.mount( );
- cy.findByTestId('my.button')
- .should('have.attr', 'aria-busy', 'false')
- .click()
- .should('have.attr', 'aria-busy', 'true');
+ cy.findByTestId('my.button').should('have.attr', 'aria-busy', 'false');
+ cy.findByTestId('my.button').click({ force: true });
+ cy.findByTestId('my.button').should('have.attr', 'aria-busy', 'true');
cy.get('button').should('have.attr', 'aria-busy', 'false');
});
it('should have a tooltip', () => {
cy.mount( );
+ cy.findByTestId('my.button').focus();
cy.findByTestId('my.button')
- .focus()
.should('have.attr', 'aria-describedby')
- .then(describedBy =>
- cy.get(`#${describedBy}`).should('have.text', 'Relevant description of the basic button'),
- );
+ .then(describedBy => {
+ cy.get(`#${describedBy}`).should('have.text', 'Relevant description of the basic button');
+ });
});
});
});
diff --git a/packages/design-system/src/components/Button/Button.test.tsx b/packages/design-system/src/components/Button/Button.test.tsx
new file mode 100644
index 00000000000..22141e45565
--- /dev/null
+++ b/packages/design-system/src/components/Button/Button.test.tsx
@@ -0,0 +1,34 @@
+import { describe, it, expect } from '@jest/globals';
+import { axe } from 'jest-axe';
+import { render } from '@testing-library/react';
+import { Button } from './';
+
+describe('Button', () => {
+ it('should render a11y html', async () => {
+ const { container } = render(
+
+
+ Primary
+
+
+ Destructive
+
+
+ Secondary disabled
+
+
+ Tertiary submit
+
+
+ Tertiary isLoading
+
+
+ Tertiary isDropdown
+
+ ,
+ );
+ expect(container.firstChild).toMatchSnapshot();
+ const results = await axe(document.body);
+ expect(results).toHaveNoViolations();
+ });
+});
diff --git a/packages/design-system/src/components/Button/Button.tsx b/packages/design-system/src/components/Button/Button.tsx
index e43bc7aee53..73a3cce3e13 100644
--- a/packages/design-system/src/components/Button/Button.tsx
+++ b/packages/design-system/src/components/Button/Button.tsx
@@ -1,6 +1,6 @@
import { forwardRef, Ref } from 'react';
-import ButtonPrimary, { ButtonPrimaryPropsType } from './variations/ButtonPrimary';
+import { ButtonPrimary, ButtonPrimaryPropsType } from './variations/ButtonPrimary';
import ButtonSecondary, { ButtonSecondaryPropsType } from './variations/ButtonSecondary';
import ButtonTertiary, { ButtonTertiaryPropsType } from './variations/ButtonTertiary';
import ButtonDestructive, { ButtonDestructivePropsType } from './variations/ButtonDestructive';
@@ -54,8 +54,6 @@ function ButtonPlatform(
}
}
-const Button = forwardRef(ButtonPlatform) as (
+export const Button = forwardRef(ButtonPlatform) as (
props: ButtonType & { ref?: Ref },
) => ReturnType;
-
-export default Button;
diff --git a/packages/design-system/src/components/Button/Primitive/ButtonPrimitive.tsx b/packages/design-system/src/components/Button/Primitive/ButtonPrimitive.tsx
index 8c93d9a65b7..8781866ee89 100644
--- a/packages/design-system/src/components/Button/Primitive/ButtonPrimitive.tsx
+++ b/packages/design-system/src/components/Button/Primitive/ButtonPrimitive.tsx
@@ -2,15 +2,16 @@ import { forwardRef, ReactElement, Ref } from 'react';
// eslint-disable-next-line @talend/import-depth
import { IconNameWithSize } from '@talend/icons/dist/typeUtils';
import classnames from 'classnames';
-import Clickable, { ClickableProps } from '../../Clickable';
+import { ClickableProps } from '../../Clickable';
import { DataAttributes, DeprecatedIconNames } from '../../../types';
import { StackHorizontal } from '../../Stack';
-import Loading from '../../Loading';
+import { Loading } from '../../Loading';
import { getIconWithDeprecatedSupport } from '../../Icon/DeprecatedIconHelper';
import styles from './ButtonStyles.module.scss';
import { SizedIcon } from '../../Icon';
+import { Clickable } from '../../Clickable/Clickable';
export type AvailableVariantsTypes = 'primary' | 'destructive' | 'secondary' | 'tertiary';
export type AvailableSizes = 'M' | 'S';
@@ -22,9 +23,7 @@ export type SharedButtonTypes = {
isLoading?: boolean;
isDropdown?: boolean;
size?: S;
- icon?: S extends 'S'
- ? IconNameWithSize<'S'>
- : DeprecatedIconNames | ReactElement | IconNameWithSize<'M'>;
+ icon?: IconNameWithSize<'S'> | DeprecatedIconNames | ReactElement | IconNameWithSize<'M'>;
};
export type BaseButtonProps = Omit &
@@ -33,7 +32,7 @@ export type BaseButtonProps = Omit(
props: BaseButtonProps,
- ref?: Ref,
+ ref: Ref,
) {
const {
className,
@@ -45,11 +44,12 @@ function ButtonPrimitiveInner(
isDropdown = false,
...rest
} = props;
+ const cls = {
+ [styles['size-S']]: size === 'S',
+ };
return (
(
)}
{!isLoading && icon && (
- {getIconWithDeprecatedSupport({ iconSrc: icon, size: size === 'S' ? 'S' : 'M' })}
+ {getIconWithDeprecatedSupport({ iconSrc: icon, size: size || 'M' })}
)}
{children}
diff --git a/packages/design-system/src/components/Button/__snapshots__/Button.test.tsx.snap b/packages/design-system/src/components/Button/__snapshots__/Button.test.tsx.snap
new file mode 100644
index 00000000000..b9d45e891cf
--- /dev/null
+++ b/packages/design-system/src/components/Button/__snapshots__/Button.test.tsx.snap
@@ -0,0 +1,132 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`Button should render a11y html 1`] = `
+
+
+
+ Primary
+
+
+
+
+
+
+
+
+
+ Destructive
+
+
+
+
+ Secondary disabled
+
+
+
+
+ Tertiary submit
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Tertiary isLoading
+
+
+
+
+ Tertiary isDropdown
+
+
+
+
+
+
+
+
+`;
diff --git a/packages/design-system/src/components/Button/index.ts b/packages/design-system/src/components/Button/index.ts
index 3fd70bef5f7..030cc6dd7c1 100644
--- a/packages/design-system/src/components/Button/index.ts
+++ b/packages/design-system/src/components/Button/index.ts
@@ -1,8 +1,8 @@
-import ButtonPrimary from './variations/ButtonPrimary';
+import { ButtonPrimary } from './variations/ButtonPrimary';
import ButtonSecondary from './variations/ButtonSecondary';
import ButtonTertiary from './variations/ButtonTertiary';
import ButtonDestructive from './variations/ButtonDestructive';
-import Button from './Button';
+import { Button } from './Button';
import { AvailableSizes, BaseButtonProps } from './Primitive/ButtonPrimitive';
diff --git a/packages/design-system/src/components/Button/variations/ButtonDestructive.tsx b/packages/design-system/src/components/Button/variations/ButtonDestructive.tsx
index 366b8cdd187..aa06b878985 100644
--- a/packages/design-system/src/components/Button/variations/ButtonDestructive.tsx
+++ b/packages/design-system/src/components/Button/variations/ButtonDestructive.tsx
@@ -5,10 +5,8 @@ import styles from './ButtonDestructive.module.scss';
export type ButtonDestructivePropsType = Omit<
BaseButtonProps,
- 'className' | 'size'
-> & {
- size?: S;
-};
+ 'className'
+>;
function Destructive(
props: ButtonDestructivePropsType,
diff --git a/packages/design-system/src/components/Button/variations/ButtonPrimary.tsx b/packages/design-system/src/components/Button/variations/ButtonPrimary.tsx
index 5488c61273b..f842f596661 100644
--- a/packages/design-system/src/components/Button/variations/ButtonPrimary.tsx
+++ b/packages/design-system/src/components/Button/variations/ButtonPrimary.tsx
@@ -5,10 +5,8 @@ import styles from './ButtonPrimary.module.scss';
export type ButtonPrimaryPropsType = Omit<
BaseButtonProps,
- 'className' | 'size'
-> & {
- size?: S;
-};
+ 'className'
+>;
function Primary(
props: ButtonPrimaryPropsType,
@@ -17,8 +15,6 @@ function Primary(
return ;
}
-const ButtonPrimary = forwardRef(Primary) as (
+export const ButtonPrimary = forwardRef(Primary) as (
props: ButtonPrimaryPropsType & { ref?: Ref },
) => ReturnType;
-
-export default ButtonPrimary;
diff --git a/packages/design-system/src/components/Button/variations/ButtonSecondary.tsx b/packages/design-system/src/components/Button/variations/ButtonSecondary.tsx
index e8bed12f18e..c27a1555ba1 100644
--- a/packages/design-system/src/components/Button/variations/ButtonSecondary.tsx
+++ b/packages/design-system/src/components/Button/variations/ButtonSecondary.tsx
@@ -6,10 +6,8 @@ import { ButtonDestructivePropsType } from './ButtonDestructive';
export type ButtonSecondaryPropsType = Omit<
BaseButtonProps,
- 'className' | 'size'
-> & {
- size?: S;
-};
+ 'className'
+>;
function Secondary(
props: ButtonSecondaryPropsType,
diff --git a/packages/design-system/src/components/Button/variations/ButtonTertiary.tsx b/packages/design-system/src/components/Button/variations/ButtonTertiary.tsx
index ad960232b2e..b473653caaf 100644
--- a/packages/design-system/src/components/Button/variations/ButtonTertiary.tsx
+++ b/packages/design-system/src/components/Button/variations/ButtonTertiary.tsx
@@ -6,10 +6,8 @@ import { ButtonDestructivePropsType } from './ButtonDestructive';
export type ButtonTertiaryPropsType = Omit<
BaseButtonProps,
- 'className' | 'size'
-> & {
- size?: S;
-};
+ 'className'
+>;
function Tertiary(
props: ButtonTertiaryPropsType,
diff --git a/packages/design-system/src/components/ButtonAsLink/ButtonAsLink.test.tsx b/packages/design-system/src/components/ButtonAsLink/ButtonAsLink.test.tsx
new file mode 100644
index 00000000000..b9e9a89349e
--- /dev/null
+++ b/packages/design-system/src/components/ButtonAsLink/ButtonAsLink.test.tsx
@@ -0,0 +1,34 @@
+import { describe, it, expect } from '@jest/globals';
+import { axe } from 'jest-axe';
+import { render } from '@testing-library/react';
+import { ButtonAsLink } from './';
+
+describe('ButtonAsLink', () => {
+ it('should render a11y html', async () => {
+ const { container } = render(
+
+
+ Primary
+
+
+ Destructive
+
+
+ Secondary
+
+
+ Tertiary submit
+
+
+ Tertiary
+
+
+ Tertiary
+
+ ,
+ );
+ expect(container.firstChild).toMatchSnapshot();
+ const results = await axe(document.body);
+ expect(results).toHaveNoViolations();
+ });
+});
diff --git a/packages/design-system/src/components/ButtonAsLink/ButtonAsLink.tsx b/packages/design-system/src/components/ButtonAsLink/ButtonAsLink.tsx
index e6445c965b3..addf27ac47a 100644
--- a/packages/design-system/src/components/ButtonAsLink/ButtonAsLink.tsx
+++ b/packages/design-system/src/components/ButtonAsLink/ButtonAsLink.tsx
@@ -65,8 +65,6 @@ function ButtonPlatform(props: ButtonType, ref: Ref
}
}
-const ButtonAsLink = forwardRef(ButtonPlatform) as (
+export const ButtonAsLink = forwardRef(ButtonPlatform) as (
props: ButtonType & { ref?: Ref },
) => ReturnType;
-
-export default ButtonAsLink;
diff --git a/packages/design-system/src/components/ButtonAsLink/Primitive/ButtonPrimitiveAsLink.tsx b/packages/design-system/src/components/ButtonAsLink/Primitive/ButtonPrimitiveAsLink.tsx
index e9aa471706e..ed18b9b21d8 100644
--- a/packages/design-system/src/components/ButtonAsLink/Primitive/ButtonPrimitiveAsLink.tsx
+++ b/packages/design-system/src/components/ButtonAsLink/Primitive/ButtonPrimitiveAsLink.tsx
@@ -1,6 +1,6 @@
import { forwardRef, Ref } from 'react';
import classnames from 'classnames';
-import Linkable, { LinkableType } from '../../Linkable';
+import { Linkable, LinkableType } from '../../Linkable';
import { StackHorizontal } from '../../Stack';
diff --git a/packages/design-system/src/components/ButtonAsLink/__snapshots__/ButtonAsLink.test.tsx.snap b/packages/design-system/src/components/ButtonAsLink/__snapshots__/ButtonAsLink.test.tsx.snap
new file mode 100644
index 00000000000..ff0c2f7bf25
--- /dev/null
+++ b/packages/design-system/src/components/ButtonAsLink/__snapshots__/ButtonAsLink.test.tsx.snap
@@ -0,0 +1,75 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`ButtonAsLink should render a11y html 1`] = `
+
+
+
+ Primary
+
+
+
+
+
+
+
+
+
+ Destructive
+
+
+
+
+ Secondary
+
+
+
+
+ Tertiary submit
+
+
+
+
+ Tertiary
+
+
+
+
+ Tertiary
+
+
+
+`;
diff --git a/packages/design-system/src/components/ButtonAsLink/index.ts b/packages/design-system/src/components/ButtonAsLink/index.ts
index 567ee15986f..42c7a3b6cf2 100644
--- a/packages/design-system/src/components/ButtonAsLink/index.ts
+++ b/packages/design-system/src/components/ButtonAsLink/index.ts
@@ -2,10 +2,9 @@ import ButtonPrimaryAsLink from './variations/ButtonPrimaryAsLink';
import ButtonSecondaryAsLink from './variations/ButtonSecondaryAsLink';
import ButtonTertiaryAsLink from './variations/ButtonTertiaryAsLink';
import ButtonDestructiveAsLink from './variations/ButtonDestructiveAsLink';
-import ButtonAsLink from './ButtonAsLink';
+export * from './ButtonAsLink';
export {
- ButtonAsLink,
ButtonPrimaryAsLink,
ButtonSecondaryAsLink,
ButtonTertiaryAsLink,
diff --git a/packages/design-system/src/components/ButtonAsLink/variations/ButtonDestructiveAsLink.tsx b/packages/design-system/src/components/ButtonAsLink/variations/ButtonDestructiveAsLink.tsx
index 493cc0e1551..b634bb30536 100644
--- a/packages/design-system/src/components/ButtonAsLink/variations/ButtonDestructiveAsLink.tsx
+++ b/packages/design-system/src/components/ButtonAsLink/variations/ButtonDestructiveAsLink.tsx
@@ -17,7 +17,7 @@ function DestructiveAsLink(
}
const ButtonDestructiveAsLink = forwardRef(DestructiveAsLink) as (
- props: ButtonDestructiveAsLinkPropsType & { ref?: Ref },
+ props: ButtonDestructiveAsLinkPropsType & { ref: Ref },
) => ReturnType;
export default ButtonDestructiveAsLink;
diff --git a/packages/design-system/src/components/ButtonIcon/ButtonIcon.test.tsx b/packages/design-system/src/components/ButtonIcon/ButtonIcon.test.tsx
new file mode 100644
index 00000000000..4dde3253ff2
--- /dev/null
+++ b/packages/design-system/src/components/ButtonIcon/ButtonIcon.test.tsx
@@ -0,0 +1,20 @@
+import { describe, it, expect } from '@jest/globals';
+import { axe } from 'jest-axe';
+import { render } from '@testing-library/react';
+import { ButtonIcon } from './';
+
+describe('ButtonIcon', () => {
+ it('should render accessible html', async () => {
+ // note we need to add the aria-label to be accessible
+ // TODO: make it required
+ const { container } = render(
+ {}}>
+ children is considered as description
+ ,
+ );
+ expect(container.firstChild).toMatchSnapshot();
+ const results = await axe(document.body);
+ expect(results).toHaveNoViolations();
+ expect(container.firstChild).toHaveAttribute('aria-describedby', 'id-42');
+ });
+});
diff --git a/packages/design-system/src/components/ButtonIcon/Primitive/ButtonIconPrimitive.tsx b/packages/design-system/src/components/ButtonIcon/Primitive/ButtonIconPrimitive.tsx
index bfaac7ec1b4..3f815731b0d 100644
--- a/packages/design-system/src/components/ButtonIcon/Primitive/ButtonIconPrimitive.tsx
+++ b/packages/design-system/src/components/ButtonIcon/Primitive/ButtonIconPrimitive.tsx
@@ -2,14 +2,16 @@ import { forwardRef } from 'react';
import type { MouseEvent, ReactElement, ButtonHTMLAttributes, Ref } from 'react';
import classnames from 'classnames';
+
// eslint-disable-next-line @talend/import-depth
import { IconNameWithSize } from '@talend/icons/dist/typeUtils';
+import { mergeRefs } from '../../../mergeRef';
import { DeprecatedIconNames } from '../../../types';
-import Button from '../../Clickable';
-import Tooltip, { TooltipPlacement } from '../../Tooltip';
-import Loading from '../../Loading';
+import { Clickable } from '../../Clickable';
import { getIconWithDeprecatedSupport } from '../../Icon/DeprecatedIconHelper';
+import { Loading } from '../../Loading';
+import { Tooltip, TooltipPlacement } from '../../Tooltip';
import styles from './ButtonIcon.module.scss';
@@ -18,7 +20,7 @@ export type PossibleVariants = 'toggle' | 'floating' | 'default';
type CommonTypes> = Omit<
ButtonHTMLAttributes,
- 'className' | 'style'
+ 'className' | 'style' | 'aria-label'
> & {
children: string;
isLoading?: boolean;
@@ -35,10 +37,12 @@ export type ToggleTypes> = CommonTypes & {
export type FloatingTypes> = CommonTypes & {
variant: 'floating';
+ isActive?: never;
};
export type DefaultTypes = CommonTypes & {
variant: 'default';
+ isActive?: never;
};
export type ButtonIconProps =
@@ -58,30 +62,36 @@ function Primitive(
icon,
disabled,
tooltipPlacement,
+ isActive = false,
...rest
} = props;
- const activeButtonIconPrimitive = props.variant === 'toggle' ? props.isActive : false;
+ const activeButtonIconPrimitive = props.variant === 'toggle' ? isActive : false;
return (
-
-
- {!isLoading &&
- getIconWithDeprecatedSupport({ iconSrc: icon, size: size === 'XS' ? 'S' : 'M' })}
- {isLoading && }
-
-
+ {(triggerProps, triggerRef) => (
+
+
+ {!isLoading &&
+ getIconWithDeprecatedSupport({ iconSrc: icon, size: size === 'XS' ? 'S' : 'M' })}
+ {isLoading && }
+
+
+ )}
);
}
diff --git a/packages/design-system/src/components/ButtonIcon/__snapshots__/ButtonIcon.test.tsx.snap b/packages/design-system/src/components/ButtonIcon/__snapshots__/ButtonIcon.test.tsx.snap
new file mode 100644
index 00000000000..1b5824609da
--- /dev/null
+++ b/packages/design-system/src/components/ButtonIcon/__snapshots__/ButtonIcon.test.tsx.snap
@@ -0,0 +1,25 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`ButtonIcon should render accessible html 1`] = `
+
+
+
+
+
+`;
diff --git a/packages/design-system/src/components/ButtonIcon/index.ts b/packages/design-system/src/components/ButtonIcon/index.ts
index 1efae09741a..e9296793e6b 100644
--- a/packages/design-system/src/components/ButtonIcon/index.ts
+++ b/packages/design-system/src/components/ButtonIcon/index.ts
@@ -1,4 +1,4 @@
-import ButtonIcon from './variations/ButtonIcon';
+import { ButtonIcon } from './variations/ButtonIcon';
import ButtonIconToggle from './variations/ButtonIconToggle';
import ButtonIconFloating from './variations/ButtonIconFloating';
diff --git a/packages/design-system/src/components/ButtonIcon/variations/ButtonIcon.tsx b/packages/design-system/src/components/ButtonIcon/variations/ButtonIcon.tsx
index 56b4069e6ba..c8a83ad2ade 100644
--- a/packages/design-system/src/components/ButtonIcon/variations/ButtonIcon.tsx
+++ b/packages/design-system/src/components/ButtonIcon/variations/ButtonIcon.tsx
@@ -10,8 +10,6 @@ function Button(props: ButtonIconType, ref: Ref ;
}
-const ButtonIcon = forwardRef(Button) as (
+export const ButtonIcon = forwardRef(Button) as (
props: ButtonIconType & { ref?: Ref },
) => ReturnType;
-
-export default ButtonIcon;
diff --git a/packages/design-system/src/components/WIP/Card/Card.module.scss b/packages/design-system/src/components/Card/Card.module.scss
similarity index 100%
rename from packages/design-system/src/components/WIP/Card/Card.module.scss
rename to packages/design-system/src/components/Card/Card.module.scss
diff --git a/packages/design-system/src/components/Card/Card.test.tsx b/packages/design-system/src/components/Card/Card.test.tsx
new file mode 100644
index 00000000000..bc57648b6d1
--- /dev/null
+++ b/packages/design-system/src/components/Card/Card.test.tsx
@@ -0,0 +1,19 @@
+import { describe, it, expect } from '@jest/globals';
+import { axe } from 'jest-axe';
+import { render } from '@testing-library/react';
+import { Card } from './';
+
+describe('Card', () => {
+ it('should render a11y html', async () => {
+ const { container } = render(
+
+
+ Content
+
+ ,
+ );
+ expect(container.firstChild).toMatchSnapshot();
+ const results = await axe(document.body);
+ expect(results).toHaveNoViolations();
+ });
+});
diff --git a/packages/design-system/src/components/WIP/Card/Card.tsx b/packages/design-system/src/components/Card/Card.tsx
similarity index 71%
rename from packages/design-system/src/components/WIP/Card/Card.tsx
rename to packages/design-system/src/components/Card/Card.tsx
index f78038563cf..041fcf6d9a3 100644
--- a/packages/design-system/src/components/WIP/Card/Card.tsx
+++ b/packages/design-system/src/components/Card/Card.tsx
@@ -1,15 +1,15 @@
import type { ReactNode } from 'react';
-import { StackVertical } from '../../Stack';
+import { StackVertical } from '../Stack';
import theme from './Card.module.scss';
-interface CardPropsType {
+export type CardPropsType = {
header?: ReactNode;
children: ReactNode;
-}
+};
-function Card({ header, children }: CardPropsType) {
+export function Card({ header, children }: CardPropsType) {
return (
@@ -19,5 +19,3 @@ function Card({ header, children }: CardPropsType) {
);
}
-
-export default Card;
diff --git a/packages/design-system/src/components/Card/__snapshots__/Card.test.tsx.snap b/packages/design-system/src/components/Card/__snapshots__/Card.test.tsx.snap
new file mode 100644
index 00000000000..74f84a4c604
--- /dev/null
+++ b/packages/design-system/src/components/Card/__snapshots__/Card.test.tsx.snap
@@ -0,0 +1,26 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`Card should render a11y html 1`] = `
+
+
+
+`;
diff --git a/packages/design-system/src/components/Card/index.ts b/packages/design-system/src/components/Card/index.ts
new file mode 100644
index 00000000000..ca0b060473a
--- /dev/null
+++ b/packages/design-system/src/components/Card/index.ts
@@ -0,0 +1 @@
+export * from './Card';
diff --git a/packages/design-system/src/components/Clickable/Clickable.tsx b/packages/design-system/src/components/Clickable/Clickable.tsx
index 0f03ce8dee4..64ee5630fb6 100644
--- a/packages/design-system/src/components/Clickable/Clickable.tsx
+++ b/packages/design-system/src/components/Clickable/Clickable.tsx
@@ -1,23 +1,22 @@
-import { forwardRef, MouseEvent, ReactNode, Ref } from 'react';
+import { forwardRef, MouseEvent, ReactNode, Ref, HTMLProps } from 'react';
import classnames from 'classnames';
-import {
- Clickable as ReakitClickable,
- ClickableProps as ReakitClickableProps,
-} from 'reakit/Clickable';
import styles from './Clickable.module.scss';
-export type ClickableProps = Omit & {
+export type ClickableProps = Omit<
+ HTMLProps,
+ 'size' | 'ref' | 'wrap' | 'loop'
+> & {
type?: 'button' | 'submit' | 'reset';
- children: ReactNode | ReactNode[];
+ children: string | ReactNode | ReactNode[];
onClick?: (event: MouseEvent | KeyboardEvent) => void;
};
-const Clickable = forwardRef(
+export const Clickable = forwardRef(
// Ref: Clickable is polymorphic. Could be any HTML element
({ type = 'button', className, ...props }: ClickableProps, ref: Ref) => {
return (
- {
+ it('should render a11y html', async () => {
+ const { container } = render(
+
+
+ Content
+
+ ,
+ );
+ expect(container.firstChild).toMatchSnapshot();
+ const results = await axe(document.body);
+ expect(results).toHaveNoViolations();
+ });
+});
diff --git a/packages/design-system/src/components/Clickable/__snapshots__/Clikable.test.tsx.snap b/packages/design-system/src/components/Clickable/__snapshots__/Clikable.test.tsx.snap
new file mode 100644
index 00000000000..267fd8f5839
--- /dev/null
+++ b/packages/design-system/src/components/Clickable/__snapshots__/Clikable.test.tsx.snap
@@ -0,0 +1,14 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`Clickable should render a11y html 1`] = `
+
+
+
+ Content
+
+
+
+`;
diff --git a/packages/design-system/src/components/Clickable/index.ts b/packages/design-system/src/components/Clickable/index.ts
index c5458624f5b..cc29524ac92 100644
--- a/packages/design-system/src/components/Clickable/index.ts
+++ b/packages/design-system/src/components/Clickable/index.ts
@@ -1,4 +1 @@
-import Clickable, { ClickableProps } from './Clickable';
-
-export type { ClickableProps };
-export default Clickable;
+export * from './Clickable';
diff --git a/packages/design-system/src/components/WIP/Combobox/Combobox.module.scss b/packages/design-system/src/components/Combobox/Combobox.module.scss
similarity index 91%
rename from packages/design-system/src/components/WIP/Combobox/Combobox.module.scss
rename to packages/design-system/src/components/Combobox/Combobox.module.scss
index 7226052ce91..bab40e2e9ff 100644
--- a/packages/design-system/src/components/WIP/Combobox/Combobox.module.scss
+++ b/packages/design-system/src/components/Combobox/Combobox.module.scss
@@ -1,5 +1,5 @@
@use '~@talend/design-tokens/lib/tokens';
-@use '../../Form/Primitives/mixins' as Form;
+@use '../Form/Primitives/mixins' as Form;
.combobox {
&__input {
@@ -9,6 +9,7 @@
}
[role='listbox'] {
+ margin-top: tokens.$coral-spacing-xs;
background: tokens.$coral-color-neutral-background;
border-radius: tokens.$coral-radius-s;
box-shadow: tokens.$coral-elevation-shadow-neutral-m;
diff --git a/packages/design-system/src/components/Combobox/Combobox.test.tsx b/packages/design-system/src/components/Combobox/Combobox.test.tsx
new file mode 100644
index 00000000000..273da64d98a
--- /dev/null
+++ b/packages/design-system/src/components/Combobox/Combobox.test.tsx
@@ -0,0 +1,27 @@
+import { describe, it, expect } from '@jest/globals';
+import { axe } from 'jest-axe';
+import { render } from '@testing-library/react';
+import { Combobox } from './';
+
+jest.mock('@talend/utils', () => {
+ let i = 0;
+ return {
+ // we need stable but different uuid (is fixed to 42 by current mock)
+ randomUUID: () => `mocked-uuid-${i++}`,
+ };
+});
+
+const fruits = ['Acerola', 'Apple', 'Apricots', 'Avocado'];
+
+describe('Combobox', () => {
+ it('should render a11y html', async () => {
+ const { container } = render(
+
+
+ ,
+ );
+ expect(container.firstChild).toMatchSnapshot();
+ const results = await axe(document.body);
+ expect(results).toHaveNoViolations();
+ });
+});
diff --git a/packages/design-system/src/components/Combobox/Combobox.tsx b/packages/design-system/src/components/Combobox/Combobox.tsx
new file mode 100644
index 00000000000..9e5cc23e516
--- /dev/null
+++ b/packages/design-system/src/components/Combobox/Combobox.tsx
@@ -0,0 +1,104 @@
+import { useState, useCallback, useEffect, useRef, FocusEvent } from 'react';
+import { useTranslation } from 'react-i18next';
+
+import { useId } from '../../useId';
+import { I18N_DOMAIN_DESIGN_SYSTEM } from '../constants';
+
+import styles from './Combobox.module.scss';
+
+export type ComboboxProps = {
+ id?: string;
+ values?: string[];
+ initialValue?: string;
+};
+
+export const Combobox = ({ values, ...rest }: ComboboxProps) => {
+ const { t } = useTranslation(I18N_DOMAIN_DESIGN_SYSTEM);
+ const [show, setShow] = useState(false);
+ const [options, setOptions] = useState(values || []);
+ const [value, setValue] = useState(rest.initialValue || '');
+ const id = useId(rest.id);
+ const boxId = useId();
+ const noValue = t('COMBOBOX_NOT_RESULT', 'No results found');
+ const onKeydown = useCallback(e => {
+ if (e.key === 'Escape') {
+ setShow(false);
+ }
+ }, []);
+
+ const comboboxRef = useRef(null);
+
+ // sync options with values and value
+ useEffect(() => {
+ if (value) {
+ setOptions(
+ (values || []).filter(option => option.toLowerCase().includes(value.toLowerCase())),
+ );
+ } else {
+ setOptions(values || []);
+ }
+ }, [value, values]);
+
+ const onFocusOut = (event: FocusEvent) => {
+ // Check if where user clicks out is part of combobox
+ let relatedTarget = event.relatedTarget;
+ while (relatedTarget !== null && relatedTarget !== comboboxRef.current) {
+ relatedTarget = relatedTarget.parentElement;
+ }
+
+ // If relatedTarget is null, it means we reach top level component without finding combobox div
+ // So we are sure user doesn't click in part of combobox
+ if (relatedTarget === null) {
+ setShow(false);
+ }
+ };
+
+ return (
+
+
setShow(true)}
+ onClick={() => {
+ if (!show) setShow(true);
+ }}
+ aria-controls={boxId}
+ value={value}
+ placeholder={t('COMBOBOX_PLACEHOLDER', 'Search')}
+ onChange={e => setValue(e.target.value)}
+ />
+
+ {show && options.length ? (
+ options.map(v => (
+
{
+ setValue(v);
+ setShow(false);
+ }}
+ onKeyDown={e => {
+ if (e.key === 'Enter') {
+ setValue(v);
+ setShow(false);
+ }
+ }}
+ >
+ {v}
+
+ ))
+ ) : (
+
+ {noValue}
+
+ )}
+
+
+ );
+};
diff --git a/packages/design-system/src/components/Combobox/__snapshots__/Combobox.test.tsx.snap b/packages/design-system/src/components/Combobox/__snapshots__/Combobox.test.tsx.snap
new file mode 100644
index 00000000000..6bbb872a4af
--- /dev/null
+++ b/packages/design-system/src/components/Combobox/__snapshots__/Combobox.test.tsx.snap
@@ -0,0 +1,34 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`Combobox should render a11y html 1`] = `
+
+
+
+`;
diff --git a/packages/design-system/src/components/Combobox/index.ts b/packages/design-system/src/components/Combobox/index.ts
new file mode 100644
index 00000000000..814b172cb41
--- /dev/null
+++ b/packages/design-system/src/components/Combobox/index.ts
@@ -0,0 +1 @@
+export * from './Combobox';
diff --git a/packages/design-system/src/components/Disclosure/Disclosure.tsx b/packages/design-system/src/components/Disclosure/Disclosure.tsx
new file mode 100644
index 00000000000..280362b18b5
--- /dev/null
+++ b/packages/design-system/src/components/Disclosure/Disclosure.tsx
@@ -0,0 +1,24 @@
+import type { Ref } from 'react';
+
+import { ChildOrGenerator, renderOrClone } from '../../renderOrClone';
+
+export type DisclosureProps = {
+ children: ChildOrGenerator;
+ popref?: Ref;
+};
+
+type DisclosureButtonProps = {
+ ref?: Ref;
+};
+
+export type DisclosureFnProps = {
+ ref?: Ref;
+};
+
+export function Disclosure({ children, popref, ...rest }: DisclosureProps) {
+ const props: DisclosureButtonProps = {
+ ref: popref,
+ };
+
+ return renderOrClone(children, { ...props, ...rest }) || null;
+}
diff --git a/packages/design-system/src/components/Disclosure/DisclosureContext.ts b/packages/design-system/src/components/Disclosure/DisclosureContext.ts
new file mode 100644
index 00000000000..dca6bb2ccc0
--- /dev/null
+++ b/packages/design-system/src/components/Disclosure/DisclosureContext.ts
@@ -0,0 +1,14 @@
+import { createContext, useContext } from 'react';
+
+interface DisclosureContextValue {
+ disclosureId?: string | number;
+ onSelect?: () => void;
+ open?: boolean;
+ panelId?: string;
+}
+
+export const DisclosureContext = createContext({});
+
+export function useDisclosureCtx() {
+ return useContext(DisclosureContext);
+}
diff --git a/packages/design-system/src/components/Divider/Divider.test.tsx b/packages/design-system/src/components/Divider/Divider.test.tsx
new file mode 100644
index 00000000000..2762a8524f3
--- /dev/null
+++ b/packages/design-system/src/components/Divider/Divider.test.tsx
@@ -0,0 +1,26 @@
+import { describe, it, expect } from '@jest/globals';
+import { axe } from 'jest-axe';
+import { render } from '@testing-library/react';
+import { Divider } from './';
+
+describe('Divider', () => {
+ it('should render a11y html', async () => {
+ const { container } = render(
+
+
+
+
+
Side
+
lorem ipsum content
+
+ ,
+ );
+ expect(container.firstChild).toMatchSnapshot();
+ const results = await axe(document.body);
+ expect(results).toHaveNoViolations();
+ });
+});
diff --git a/packages/design-system/src/components/Divider/Divider.tsx b/packages/design-system/src/components/Divider/Divider.tsx
index 4aed009ab53..65ce1e5acd3 100644
--- a/packages/design-system/src/components/Divider/Divider.tsx
+++ b/packages/design-system/src/components/Divider/Divider.tsx
@@ -1,12 +1,22 @@
-import { forwardRef, Ref } from 'react';
-import { Separator as ReakitSeparator, SeparatorProps as ReakitSeparatorProps } from 'reakit';
+import { forwardRef, Ref, HTMLAttributes, RefAttributes } from 'react';
import style from './Divider.module.scss';
-type DividerProps = Omit;
+export type DividerOptions = {
+ /**
+ * Separator's orientation.
+ */
+ orientation?: 'horizontal' | 'vertical';
+};
-const Divider = forwardRef((props: DividerProps, ref: Ref) => {
- return ;
+export type DividerHTMLProps = HTMLAttributes & RefAttributes;
+
+export type DividerProps = DividerOptions & DividerHTMLProps;
+
+export const Divider = forwardRef((props: DividerProps, ref: Ref) => {
+ const ruleOrientation = props.orientation || 'horizontal';
+
+ return ;
});
-export default Divider;
+Divider.displayName = 'Divider';
diff --git a/packages/design-system/src/components/Divider/__snapshots__/Divider.test.tsx.snap b/packages/design-system/src/components/Divider/__snapshots__/Divider.test.tsx.snap
new file mode 100644
index 00000000000..c8e87f167b3
--- /dev/null
+++ b/packages/design-system/src/components/Divider/__snapshots__/Divider.test.tsx.snap
@@ -0,0 +1,31 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`Divider should render a11y html 1`] = `
+
+
+
+ test
+
+
+
+ lorem ipsum
+
+
+
+
+
+ Side
+
+
+ lorem ipsum content
+
+
+
+`;
diff --git a/packages/design-system/src/components/Divider/index.ts b/packages/design-system/src/components/Divider/index.ts
index c8ee123153b..1f84888dc70 100644
--- a/packages/design-system/src/components/Divider/index.ts
+++ b/packages/design-system/src/components/Divider/index.ts
@@ -1,3 +1 @@
-import Divider from './Divider';
-
-export default Divider;
+export * from './Divider';
diff --git a/packages/design-system/src/components/Drawer/Drawer.test.tsx b/packages/design-system/src/components/Drawer/Drawer.test.tsx
new file mode 100644
index 00000000000..4219cb4522c
--- /dev/null
+++ b/packages/design-system/src/components/Drawer/Drawer.test.tsx
@@ -0,0 +1,22 @@
+import { describe, it, expect } from '@jest/globals';
+import { axe } from 'jest-axe';
+import { render } from '@testing-library/react';
+import { FloatingDrawer } from './';
+
+describe('FloatingDrawer', () => {
+ it('should render a11y html', async () => {
+ const { container } = render(
+
+
+ test
+
+ content of the drawer
+
+
+ ,
+ );
+ expect(container.firstChild).toMatchSnapshot();
+ const results = await axe(document.body);
+ expect(results).toHaveNoViolations();
+ });
+});
diff --git a/packages/design-system/src/components/WIP/Drawer/Primitive/PrimitiveDrawer.module.scss b/packages/design-system/src/components/Drawer/Primitive/PrimitiveDrawer.module.scss
similarity index 90%
rename from packages/design-system/src/components/WIP/Drawer/Primitive/PrimitiveDrawer.module.scss
rename to packages/design-system/src/components/Drawer/Primitive/PrimitiveDrawer.module.scss
index 2916b7943fa..e18430463d6 100644
--- a/packages/design-system/src/components/WIP/Drawer/Primitive/PrimitiveDrawer.module.scss
+++ b/packages/design-system/src/components/Drawer/Primitive/PrimitiveDrawer.module.scss
@@ -1,12 +1,12 @@
@use '~@talend/design-tokens/lib/tokens';
-.primitive-drawer {
+.drawer {
width: tokens.$coral-sizing-maximal;
height: 100%;
background: tokens.$coral-color-neutral-background;
display: flex;
- &__header {
+ .header {
flex-grow: 0;
flex-shrink: 0;
flex-basis: tokens.$coral-sizing-l;
@@ -15,7 +15,7 @@
display: flex;
}
- &__body {
+ .body {
flex-grow: 1;
flex-shrink: 1;
flex-basis: 0;
@@ -24,7 +24,7 @@
display: flex;
}
- &__footer {
+ .footer {
flex-grow: 0;
flex-shrink: 0;
flex-basis: calc(tokens.$coral-sizing-l + 2 * tokens.$coral-spacing-xs);
diff --git a/packages/design-system/src/components/WIP/Drawer/Primitive/PrimitiveDrawer.tsx b/packages/design-system/src/components/Drawer/Primitive/PrimitiveDrawer.tsx
similarity index 59%
rename from packages/design-system/src/components/WIP/Drawer/Primitive/PrimitiveDrawer.tsx
rename to packages/design-system/src/components/Drawer/Primitive/PrimitiveDrawer.tsx
index 94d46eb0d20..3d85e2affde 100644
--- a/packages/design-system/src/components/WIP/Drawer/Primitive/PrimitiveDrawer.tsx
+++ b/packages/design-system/src/components/Drawer/Primitive/PrimitiveDrawer.tsx
@@ -1,6 +1,6 @@
import { forwardRef } from 'react';
import type { Ref, ReactNode } from 'react';
-import { StackVertical } from '../../../Stack';
+import { StackVertical } from '../../Stack';
import theme from './PrimitiveDrawer.module.scss';
@@ -12,11 +12,11 @@ export type DrawerProps = {
export const PrimitiveDrawer = forwardRef(
({ header, children, footer }: DrawerProps, ref: Ref) => (
-
+
- {header && {header}
}
- {children}
- {footer && {footer}
}
+ {header && {header}
}
+ {children}
+ {footer && {footer}
}
),
diff --git a/packages/design-system/src/components/Drawer/__snapshots__/Drawer.test.tsx.snap b/packages/design-system/src/components/Drawer/__snapshots__/Drawer.test.tsx.snap
new file mode 100644
index 00000000000..5a47831a2d7
--- /dev/null
+++ b/packages/design-system/src/components/Drawer/__snapshots__/Drawer.test.tsx.snap
@@ -0,0 +1,38 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`FloatingDrawer should render a11y html 1`] = `
+
+
+
+ test
+
+
+
+
+
+
+ content of the drawer
+
+
+
+
+
+
+
+`;
diff --git a/packages/design-system/src/components/WIP/Drawer/index.ts b/packages/design-system/src/components/Drawer/index.ts
similarity index 100%
rename from packages/design-system/src/components/WIP/Drawer/index.ts
rename to packages/design-system/src/components/Drawer/index.ts
diff --git a/packages/design-system/src/components/WIP/Drawer/variants/FloatingDrawer/FloatingDrawer.module.scss b/packages/design-system/src/components/Drawer/variants/FloatingDrawer/FloatingDrawer.module.scss
similarity index 72%
rename from packages/design-system/src/components/WIP/Drawer/variants/FloatingDrawer/FloatingDrawer.module.scss
rename to packages/design-system/src/components/Drawer/variants/FloatingDrawer/FloatingDrawer.module.scss
index fdb13afc15a..d9c9eaf78ca 100644
--- a/packages/design-system/src/components/WIP/Drawer/variants/FloatingDrawer/FloatingDrawer.module.scss
+++ b/packages/design-system/src/components/Drawer/variants/FloatingDrawer/FloatingDrawer.module.scss
@@ -1,6 +1,6 @@
@use '~@talend/design-tokens/lib/tokens';
-.floating-drawer {
+.drawer {
position: absolute;
top: 0;
right: 0;
@@ -8,9 +8,4 @@
box-shadow: tokens.$coral-elevation-shadow-neutral-m;
z-index: tokens.$coral-elevation-layer-standard-front;
transition: transform tokens.$coral-transition-fast;
- transform: translateX(100%);
-
- &[data-enter] {
- transform: translateX(0%);
- }
}
diff --git a/packages/design-system/src/components/Drawer/variants/FloatingDrawer/FloatingDrawer.tsx b/packages/design-system/src/components/Drawer/variants/FloatingDrawer/FloatingDrawer.tsx
new file mode 100644
index 00000000000..3157baf9cca
--- /dev/null
+++ b/packages/design-system/src/components/Drawer/variants/FloatingDrawer/FloatingDrawer.tsx
@@ -0,0 +1,129 @@
+import { useEffect, useState, cloneElement } from 'react';
+import type { CSSProperties, HTMLAttributes, ReactElement, ReactNode } from 'react';
+import { Transition } from 'react-transition-group';
+import { PrimitiveDrawer } from '../../Primitive/PrimitiveDrawer';
+
+import theme from './FloatingDrawer.module.scss';
+import { useId } from '../../../../useId';
+
+type WithDisclosure = {
+ disclosure: ReactElement;
+ visible?: never;
+};
+
+type Controlled = {
+ disclosure?: never;
+ visible: boolean;
+};
+
+type DialogFnProps = {
+ onClose: () => void;
+};
+
+export type FloatingDrawerProps = {
+ id?: string;
+ header?: ((dialog: DialogFnProps) => ReactNode) | ReactNode;
+ children: ((dialog: DialogFnProps) => ReactNode) | ReactNode;
+ footer?: ((dialog: DialogFnProps) => ReactNode) | ReactNode;
+ withTransition?: boolean;
+ onClose?: () => void;
+ ['aria-label']: string;
+} & (WithDisclosure | Controlled);
+
+// backward compatibility
+export type DrawerProps = FloatingDrawerProps;
+
+const STYLES = {
+ entering: { transform: 'translateX(100%)' },
+ entered: { transform: 'translateX(0%)' },
+ exiting: { transform: 'translateX(100%)' },
+ exited: { transform: 'translateX(100%)' },
+ unmounted: { transform: 'translateX(100%)' },
+};
+
+export const FloatingDrawer = ({
+ id,
+ withTransition = true,
+ disclosure,
+ header,
+ children,
+ footer,
+ visible,
+ onClose,
+ ...props
+}: DrawerProps) => {
+ const uuid = useId(id, 'drawer');
+ const [isVisible, setVisible] = useState(visible === undefined ? false : visible);
+
+ // sync with the props
+ useEffect(() => {
+ if (visible !== undefined && visible !== isVisible) {
+ setVisible(visible);
+ }
+ }, [visible, isVisible]);
+ const onCloseHandler = () => {
+ if (onClose) {
+ onClose();
+ } else {
+ setVisible(false);
+ }
+ };
+
+ const disclosureProps = {
+ onClick: () => setVisible(!isVisible),
+ ['aria-expanded']: isVisible,
+ ['aria-controls']: uuid,
+ };
+ return (
+ <>
+ {disclosure &&
{cloneElement(disclosure, disclosureProps)}
}
+ {isVisible && (
+
+ {transitionState => {
+ const style = {
+ ...STYLES[transitionState],
+ };
+ const childrenProps = {
+ onClose: onCloseHandler,
+ };
+ return (
+
+
+ {typeof children === 'function' ? children(childrenProps) : children}
+
+
+ );
+ }}
+
+ )}
+ >
+ );
+};
+
+FloatingDrawer.displayName = 'FloatingDrawer';
+
+type ContainerPropTypes = HTMLAttributes
& {
+ children: ReactNode | ReactNode[];
+ style?: Omit;
+};
+
+const Container = ({ children, style, ...props }: ContainerPropTypes) => (
+
+ {children}
+
+);
+
+Container.displayName = 'FloatingDrawer.Container';
+
+FloatingDrawer.Container = Container;
diff --git a/packages/design-system/src/components/Dropdown/Dropdown.cy.tsx b/packages/design-system/src/components/Dropdown/Dropdown.cy.tsx
index 4738d3de3b6..346476c4e0d 100644
--- a/packages/design-system/src/components/Dropdown/Dropdown.cy.tsx
+++ b/packages/design-system/src/components/Dropdown/Dropdown.cy.tsx
@@ -1,7 +1,7 @@
/* eslint-disable testing-library/prefer-screen-queries */
/* eslint-disable testing-library/await-async-query */
/* eslint-disable no-console */
-import Dropdown from './';
+import { Dropdown } from './';
import { ButtonTertiary } from '../../components/Button';
import { IconsProvider } from '../../components/IconsProvider';
@@ -83,7 +83,7 @@ context(' ', () => {
it('should display menu', () => {
cy.mount( );
- cy.findByTestId('dropdown.button').click();
+ cy.findByTestId('dropdown.button').click({ force: true });
cy.findByTestId('dropdown.menu').should('be.visible');
cy.findByTestId('dropdown.menuitem.Link with icon-0').should('be.visible');
cy.findByTestId('dropdown.menuitem.Button with icon-1').should('be.visible');
@@ -96,7 +96,7 @@ context(' ', () => {
cy.mount( );
cy.findByTestId('dropdown.button').click();
cy.findByTestId('dropdown.menu').should('be.visible');
- cy.findByTestId('dropdown.menuitem.Button with icon-1').click();
+ cy.findByTestId('dropdown.menuitem.Button with icon-1').click({ force: true });
cy.findByTestId('dropdown.menu').should('not.be.visible');
});
diff --git a/packages/design-system/src/components/Dropdown/Dropdown.test.tsx b/packages/design-system/src/components/Dropdown/Dropdown.test.tsx
new file mode 100644
index 00000000000..ecf34846dd6
--- /dev/null
+++ b/packages/design-system/src/components/Dropdown/Dropdown.test.tsx
@@ -0,0 +1,56 @@
+import { describe, it, expect } from '@jest/globals';
+import { axe } from 'jest-axe';
+import { render } from '@testing-library/react';
+import { Dropdown } from './';
+import { ButtonTertiary } from '../Button';
+
+jest.mock('@talend/utils', () => {
+ let i = 0;
+ return {
+ // we need stable but different uuid (is fixed to 42 by current mock)
+ randomUUID: () => `mocked-uuid-${i++}`,
+ };
+});
+
+describe('Dropdown', () => {
+ it('should render a11y html', async () => {
+ const { container } = render(
+
+
+ {}}>
+ Dropdown
+
+
+ ,
+ );
+ expect(container.firstChild).toMatchSnapshot();
+ const results = await axe(document.body);
+ expect(results).toHaveNoViolations();
+ });
+});
diff --git a/packages/design-system/src/components/Dropdown/Dropdown.tsx b/packages/design-system/src/components/Dropdown/Dropdown.tsx
index c307ff8f2bc..590bc0c249d 100644
--- a/packages/design-system/src/components/Dropdown/Dropdown.tsx
+++ b/packages/design-system/src/components/Dropdown/Dropdown.tsx
@@ -1,16 +1,25 @@
-import { cloneElement, forwardRef, MouseEvent, ReactElement, Ref } from 'react';
-import { Menu, MenuButton, useMenuState } from 'reakit';
-// eslint-disable-next-line @talend/import-depth
-import DropdownButton from './Primitive/DropdownButton';
+import { cloneElement, MouseEvent, ReactElement, useEffect, useState } from 'react';
+
+import {
+ useDismiss,
+ useInteractions,
+ useFloating,
+ autoUpdate,
+ flip,
+ shift,
+} from '@floating-ui/react';
+
+import { DataAttributes, DeprecatedIconNames } from '../../types';
+import { Clickable, ClickableProps } from '../Clickable';
+import { LinkableType } from '../Linkable';
+import DropdownDivider from './Primitive/DropdownDivider';
import DropdownLink from './Primitive/DropdownLink';
import DropdownShell from './Primitive/DropdownShell';
import DropdownTitle from './Primitive/DropdownTitle';
-import DropdownDivider from './Primitive/DropdownDivider';
-import Clickable, { ClickableProps } from '../Clickable';
-import { LinkableType } from '../Linkable';
-import { DataAttributes, DeprecatedIconNames } from '../../types';
+import { DropdownButton } from './Primitive/DropdownButton';
+import { randomUUID } from '@talend/utils';
-type DropdownButtonType = Omit & {
+type DropdownButtonType = Omit & {
label: string;
onClick: () => void;
icon?: DeprecatedIconNames;
@@ -44,119 +53,134 @@ export type DropdownPropsType = {
'aria-label': string;
} & Partial;
-const Dropdown = forwardRef(
- (
- {
- children,
- 'data-test': dataTest,
- 'data-testid': dataTestId,
- items,
- ...rest
- }: DropdownPropsType,
- ref: Ref,
- ) => {
- const menu = useMenuState({
- animated: 250,
- gutter: 4,
- loop: true,
- });
-
- const menuButtonTestId = dataTestId ? `${dataTestId}.dropdown.button` : 'dropdown.button';
- const menuTestId = dataTestId ? `${dataTestId}.dropdown.menu` : 'dropdown.menu';
- const menuItemTestId = dataTestId ? `${dataTestId}.dropdown.menuitem` : 'dropdown.menuitem';
- const menuButtonTest = dataTest ? `${dataTest}.dropdown.button` : 'dropdown.button';
- const menuTest = dataTest ? `${dataTest}.dropdown.menu` : 'dropdown.menu';
- const menuItemTest = dataTest ? `${dataTest}.dropdown.menuitem` : 'dropdown.menuitem';
-
- return (
- <>
-
- {disclosureProps => cloneElement(children, disclosureProps)}
-
-
- {items.map((entry, index) => {
- if (entry.type === 'button') {
- const { label, ...entryRest } = entry;
- const id = `${label}-${index}`;
- return (
- | KeyboardEvent) => {
- menu.hide();
- entry.onClick(event);
- }}
- key={id}
- id={id}
- data-testid={`${menuItemTestId}.${id}`}
- data-test={`${menuItemTest}.${id}`}
- >
- {label}
-
- );
- }
-
- if (entry.type === 'title') {
- const { label } = entry;
- const id = `${label}-${index}`;
- return (
-
- {label}
-
- );
- }
-
- if (entry.type === 'divider') {
- const id = `divider-${index}`;
- return (
-
- );
- }
-
- const { label, as, type, ...entryRest } = entry;
+export const Dropdown = ({
+ children,
+ 'data-test': dataTest,
+ 'data-testid': dataTestId,
+ items,
+ ...rest
+}: DropdownPropsType) => {
+ const [isOpen, setIsOpen] = useState(false);
+ const [itemIds, setItemIds] = useState(items.map(() => randomUUID()));
+
+ useEffect(() => {
+ setItemIds(items.map(() => randomUUID()));
+ }, [items]);
+
+ const floating = useFloating({
+ placement: 'bottom-start',
+ open: isOpen,
+ onOpenChange: setIsOpen,
+ middleware: [flip(), shift()],
+ whileElementsMounted: autoUpdate,
+ });
+ const dismiss = useDismiss(floating.context, {
+ escapeKey: true,
+ outsidePressEvent: 'mousedown',
+ });
+ const { getReferenceProps, getFloatingProps } = useInteractions([dismiss]);
+ const menuButtonTestId = dataTestId ? `${dataTestId}.dropdown.button` : 'dropdown.button';
+ const menuTestId = dataTestId ? `${dataTestId}.dropdown.menu` : 'dropdown.menu';
+ const menuItemTestId = dataTestId ? `${dataTestId}.dropdown.menuitem` : 'dropdown.menuitem';
+ const menuButtonTest = dataTest ? `${dataTest}.dropdown.button` : 'dropdown.button';
+ const menuTest = dataTest ? `${dataTest}.dropdown.menu` : 'dropdown.menu';
+ const menuItemTest = dataTest ? `${dataTest}.dropdown.menuitem` : 'dropdown.menuitem';
+
+ return (
+ <>
+ {cloneElement(children as any, {
+ onClick: () => setIsOpen(!isOpen),
+ 'aria-pressed': `${isOpen}`,
+ 'data-testid': menuButtonTestId,
+ 'data-test': menuButtonTest,
+ 'aria-expanded': `${isOpen}`,
+ ref: floating.refs.setReference,
+ ...getReferenceProps(),
+ })}
+ setIsOpen(false)}
+ style={{ ...floating.floatingStyles, display: isOpen ? 'block' : 'none' }}
+ data-testid={menuTestId}
+ data-test={menuTest}
+ {...getFloatingProps()}
+ >
+ {items.map((entry, index) => {
+ const uuid = itemIds[index];
+ if (entry.type === 'button') {
+ const { label, ...entryRest } = entry;
const id = `${label}-${index}`;
return (
- ) => {
- menu.hide();
- if (entry.onClick) {
- entry.onClick(event);
- }
+ // {...menu}
+ onClick={(event: MouseEvent | KeyboardEvent) => {
+ entry.onClick(event);
+ setIsOpen(false);
}}
+ key={uuid}
+ tabIndex={0}
+ id={uuid}
data-testid={`${menuItemTestId}.${id}`}
data-test={`${menuItemTest}.${id}`}
>
{label}
-
+
);
- })}
-
- >
- );
- },
-);
+ }
-Dropdown.displayName = 'Dropdown';
+ if (entry.type === 'title') {
+ const { label } = entry;
+ const id = `${label}-${index}`;
+ return (
+
+ {label}
+
+ );
+ }
-export default Dropdown;
+ if (entry.type === 'divider') {
+ const id = `divider-${index}`;
+ return (
+
+ );
+ }
+
+ const { label, as, type, ...entryRest } = entry;
+ const id = `${label}-${index}`;
+ return (
+ ) => {
+ setIsOpen(false);
+ if (entry.onClick) {
+ entry.onClick(event);
+ }
+ }}
+ data-testid={`${menuItemTestId}.${id}`}
+ data-test={`${menuItemTest}.${id}`}
+ >
+ {label}
+
+ );
+ })}
+
+ >
+ );
+};
+
+Dropdown.displayName = 'Dropdown';
diff --git a/packages/design-system/src/components/Dropdown/Primitive/DropdownButton.tsx b/packages/design-system/src/components/Dropdown/Primitive/DropdownButton.tsx
index f0bbfbdd1f7..940bef4240c 100644
--- a/packages/design-system/src/components/Dropdown/Primitive/DropdownButton.tsx
+++ b/packages/design-system/src/components/Dropdown/Primitive/DropdownButton.tsx
@@ -1,52 +1,50 @@
import { forwardRef, Ref } from 'react';
-import classNames from 'classnames';
-import { MenuItem, MenuItemProps } from 'reakit';
+
// eslint-disable-next-line @talend/import-depth
import { IconNameWithSize } from '@talend/icons/dist/typeUtils';
import { DeprecatedIconNames } from '../../../types';
-import Clickable, { ClickableProps } from '../../Clickable';
+import { Clickable, ClickableProps } from '../../Clickable';
import { SizedIcon } from '../../Icon';
import { getIconWithDeprecatedSupport } from '../../Icon/DeprecatedIconHelper';
import styles from './DropdownEntry.module.scss';
-export type DropdownButtonType = Omit &
- MenuItemProps & { icon?: DeprecatedIconNames | IconNameWithSize<'M'>; checked?: boolean };
+/**
+ * This is in fact a Menu Item
+ */
+
+export type DropdownButtonType = ClickableProps & {
+ icon?: DeprecatedIconNames | IconNameWithSize<'M'>;
+};
-const DropdownButton = forwardRef(
+export const DropdownButton = forwardRef(
({ children, icon, checked, ...props }: DropdownButtonType, ref: Ref) => {
return (
-
- {icon && (
-
- {getIconWithDeprecatedSupport({
- iconSrc: icon,
- size: 'M',
- ['data-test']: 'button.icon.before',
- })}
-
- )}
- {children}
- {checked && (
-
- )}
-
+
+
+ {icon && (
+
+ {getIconWithDeprecatedSupport({
+ iconSrc: icon,
+ size: 'M',
+ ['data-test']: 'button.icon.before',
+ })}
+
+ )}
+ {children}
+ {checked && (
+
+ )}
+
+
);
},
);
DropdownButton.displayName = 'DropdownButton';
-
-export default DropdownButton;
diff --git a/packages/design-system/src/components/Dropdown/Primitive/DropdownDivider.tsx b/packages/design-system/src/components/Dropdown/Primitive/DropdownDivider.tsx
index 9cfb4854d47..62785bb0081 100644
--- a/packages/design-system/src/components/Dropdown/Primitive/DropdownDivider.tsx
+++ b/packages/design-system/src/components/Dropdown/Primitive/DropdownDivider.tsx
@@ -1,12 +1,13 @@
-import { forwardRef, Ref } from 'react';
-import { MenuItemProps, MenuSeparator } from 'reakit';
+import { forwardRef, Ref, HTMLAttributes } from 'react';
import styles from './DropdownDivider.module.scss';
-export type DropdownDividerType = MenuItemProps;
+export type DropdownDividerType = HTMLAttributes;
const DropdownDivider = forwardRef((props: DropdownDividerType, ref: Ref) => {
- return ;
+ return ;
});
+DropdownDivider.displayName = 'DropdownDivider';
+
export default DropdownDivider;
diff --git a/packages/design-system/src/components/Dropdown/Primitive/DropdownLink.tsx b/packages/design-system/src/components/Dropdown/Primitive/DropdownLink.tsx
index bb4ce9e81e2..2f1cffa3786 100644
--- a/packages/design-system/src/components/Dropdown/Primitive/DropdownLink.tsx
+++ b/packages/design-system/src/components/Dropdown/Primitive/DropdownLink.tsx
@@ -1,10 +1,9 @@
import { forwardRef, ReactElement, Ref } from 'react';
-import { MenuItem, MenuItemProps } from 'reakit';
-import Linkable, { LinkableType } from '../../Linkable';
+import { Linkable, LinkableType } from '../../Linkable';
import styles from './DropdownEntry.module.scss';
-export type DropdownLinkType = LinkableType & MenuItemProps;
+export type DropdownLinkType = LinkableType;
// Extend Linkable with props from Dropdown
// Since MenuItem and Linkable both have an `as` prop, this enables passing `as` to Linkable.
@@ -17,15 +16,16 @@ const LocalLink = forwardRef(
return ;
},
);
+LocalLink.displayName = 'LocalLink';
const DropdownLink = forwardRef(
({ children, as, ...props }: DropdownLinkType, ref: Ref) => {
return (
-
+
{children}
-
+
);
},
);
-
+DropdownLink.displayName = 'DropdownLink';
export default DropdownLink;
diff --git a/packages/design-system/src/components/Dropdown/Primitive/DropdownShell.module.scss b/packages/design-system/src/components/Dropdown/Primitive/DropdownShell.module.scss
index 87b0e5d6fdc..ce144eb6d44 100644
--- a/packages/design-system/src/components/Dropdown/Primitive/DropdownShell.module.scss
+++ b/packages/design-system/src/components/Dropdown/Primitive/DropdownShell.module.scss
@@ -2,6 +2,10 @@
.dropdownShell {
z-index: tokens.$coral-elevation-layer-interactive-front;
+ position: absolute;
+ width: max-content;
+ top: 0;
+ left: 0;
.animatedZone {
background: tokens.$coral-color-neutral-background;
@@ -12,14 +16,5 @@
min-width: calc(#{tokens.$coral-sizing-maximal} / 2);
max-width: tokens.$coral-sizing-maximal;
transition: tokens.$coral-transition-fast;
- opacity: 0;
- transform: translateY(-10%);
- }
-
- &[data-enter] {
- .animatedZone {
- opacity: 1;
- transform: translateY(0%);
- }
}
}
diff --git a/packages/design-system/src/components/Dropdown/Primitive/DropdownShell.tsx b/packages/design-system/src/components/Dropdown/Primitive/DropdownShell.tsx
index d881c1af2dc..23240ef5345 100644
--- a/packages/design-system/src/components/Dropdown/Primitive/DropdownShell.tsx
+++ b/packages/design-system/src/components/Dropdown/Primitive/DropdownShell.tsx
@@ -1,21 +1,35 @@
-import { forwardRef, Ref } from 'react';
-import { MenuProps } from 'reakit';
-
+import { forwardRef } from 'react';
+import type { HTMLAttributes, ReactNode } from 'react';
import styles from './DropdownShell.module.scss';
import { StackVertical } from '../../Stack';
-type ShellProps = MenuProps;
+type ShellProps = HTMLAttributes & {
+ onClick: () => void;
+ children: ReactNode;
+};
-const DropdownShell = forwardRef(({ children, ...rest }: ShellProps, ref: Ref) => {
- return (
-
-
-
- {children}
-
+const DropdownShell = forwardRef
(
+ ({ children, onClick, ...rest }, ref) => {
+ return (
+
-
- );
-});
+ );
+ },
+);
+
+DropdownShell.displayName = 'DropdownShell';
export default DropdownShell;
diff --git a/packages/design-system/src/components/Dropdown/Primitive/DropdownTitle.tsx b/packages/design-system/src/components/Dropdown/Primitive/DropdownTitle.tsx
index 7f37a516b04..c8edaf2b05b 100644
--- a/packages/design-system/src/components/Dropdown/Primitive/DropdownTitle.tsx
+++ b/packages/design-system/src/components/Dropdown/Primitive/DropdownTitle.tsx
@@ -14,4 +14,5 @@ const DropdownTitle = forwardRef(
},
);
+DropdownTitle.displayName = 'DropdownTitle';
export default DropdownTitle;
diff --git a/packages/design-system/src/components/Dropdown/__snapshots__/Dropdown.test.tsx.snap b/packages/design-system/src/components/Dropdown/__snapshots__/Dropdown.test.tsx.snap
new file mode 100644
index 00000000000..35241e8e71c
--- /dev/null
+++ b/packages/design-system/src/components/Dropdown/__snapshots__/Dropdown.test.tsx.snap
@@ -0,0 +1,121 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`Dropdown should render a11y html 1`] = `
+
+
+
+ Dropdown
+
+
+
+
+
+
+
+
+
+`;
diff --git a/packages/design-system/src/components/Dropdown/index.ts b/packages/design-system/src/components/Dropdown/index.ts
index 293f190383c..5bd11eca0bd 100644
--- a/packages/design-system/src/components/Dropdown/index.ts
+++ b/packages/design-system/src/components/Dropdown/index.ts
@@ -1,6 +1,2 @@
-import Dropdown from './Dropdown';
-import DropdownButton from './Primitive/DropdownButton';
-
-export default Dropdown;
-
-export { DropdownButton };
+export * from './Dropdown';
+export * from './Primitive/DropdownButton';
diff --git a/packages/design-system/src/components/EmptyState/EmptyState.test.tsx b/packages/design-system/src/components/EmptyState/EmptyState.test.tsx
new file mode 100644
index 00000000000..f24c60f457c
--- /dev/null
+++ b/packages/design-system/src/components/EmptyState/EmptyState.test.tsx
@@ -0,0 +1,40 @@
+import { describe, it, expect } from '@jest/globals';
+import { axe } from 'jest-axe';
+import { render } from '@testing-library/react';
+import { EmptyState } from './';
+
+describe('EmptyState', () => {
+ it('should render a11y html', async () => {
+ const { container } = render(
+
,
+ );
+ expect(container.firstChild).toMatchSnapshot();
+ const results = await axe(document.body);
+ expect(results).toHaveNoViolations();
+ });
+});
diff --git a/packages/design-system/src/components/EmptyState/EmptyState.tsx b/packages/design-system/src/components/EmptyState/EmptyState.tsx
index d8e31be3f09..811e28745fb 100644
--- a/packages/design-system/src/components/EmptyState/EmptyState.tsx
+++ b/packages/design-system/src/components/EmptyState/EmptyState.tsx
@@ -15,7 +15,7 @@ type Small = VariantType<'S', EmptyStateSmallProps>;
export type EmptyStateProps = Large | Medium | Small;
-const EmptyState = forwardRef((props: EmptyStateProps, ref: Ref
) => {
+export const EmptyState = forwardRef((props: EmptyStateProps, ref: Ref) => {
switch (props.variant) {
case 'L': {
const { variant, ...rest } = props;
@@ -39,5 +39,3 @@ const EmptyState = forwardRef((props: EmptyStateProps, ref: Ref) =>
});
EmptyState.displayName = 'EmptyState';
-
-export default EmptyState;
diff --git a/packages/design-system/src/components/EmptyState/__snapshots__/EmptyState.test.tsx.snap b/packages/design-system/src/components/EmptyState/__snapshots__/EmptyState.test.tsx.snap
new file mode 100644
index 00000000000..c9dde18e7b6
--- /dev/null
+++ b/packages/design-system/src/components/EmptyState/__snapshots__/EmptyState.test.tsx.snap
@@ -0,0 +1,434 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`EmptyState should render a11y html 1`] = `
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Side menu is not ready yet
+
+
+ Something went wrong
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ No preparations yet
+
+
+ Add a preparation to clean, format, and transform data prior to processing.
+
+
+
+
+
+
+
+
+
+ Reload
+
+
+
+
+ Learn more
+
+
+
+
+
+
+
+
+
+
+
+`;
diff --git a/packages/design-system/src/components/EmptyState/index.ts b/packages/design-system/src/components/EmptyState/index.ts
index b9cf05cfe80..0c2df29071e 100644
--- a/packages/design-system/src/components/EmptyState/index.ts
+++ b/packages/design-system/src/components/EmptyState/index.ts
@@ -1,7 +1,6 @@
-import EmptyState from './EmptyState';
+export * from './EmptyState';
import EmptyStateLarge from './variants/EmptyStateLarge';
import EmptyStateMedium from './variants/EmptyStateMedium';
import EmptyStateSmall from './variants/EmptyStateSmall';
-export default EmptyState;
export { EmptyStateLarge, EmptyStateMedium, EmptyStateSmall };
diff --git a/packages/design-system/src/components/EmptyState/primitive/EmptyStatePrimitive.tsx b/packages/design-system/src/components/EmptyState/primitive/EmptyStatePrimitive.tsx
index a65f7f4587d..d5baab619c4 100644
--- a/packages/design-system/src/components/EmptyState/primitive/EmptyStatePrimitive.tsx
+++ b/packages/design-system/src/components/EmptyState/primitive/EmptyStatePrimitive.tsx
@@ -2,7 +2,7 @@ import { forwardRef, HTMLAttributes, ReactElement, Ref } from 'react';
import { useTranslation } from 'react-i18next';
import { StackVertical } from '../../Stack';
-import Link from '../../Link';
+import { Link } from '../../Link';
import { ButtonPrimary } from '../../Button';
import { ButtonPrimaryAsLink } from '../../ButtonAsLink';
import { ButtonPrimaryPropsType } from '../../Button/variations/ButtonPrimary';
@@ -61,5 +61,5 @@ const EmptyStatePrimitive = forwardRef((props: EmptyStatePrimitiveProps, ref: Re
);
});
-
+EmptyStatePrimitive.displayName = 'EmptyStatePrimitive';
export default EmptyStatePrimitive;
diff --git a/packages/design-system/src/components/EmptyState/variants/EmptyStateLarge.tsx b/packages/design-system/src/components/EmptyState/variants/EmptyStateLarge.tsx
index b126eb6b68b..f783501c01a 100644
--- a/packages/design-system/src/components/EmptyState/variants/EmptyStateLarge.tsx
+++ b/packages/design-system/src/components/EmptyState/variants/EmptyStateLarge.tsx
@@ -1,5 +1,5 @@
import { forwardRef, Ref } from 'react';
-import Illustration from '../../illustrations';
+import { Illustration } from '../../illustrations';
import EmptyStatePrimitive, { EmptyStatePrimitiveProps } from '../primitive/EmptyStatePrimitive';
export type EmptyStateLargeProps = Omit & {
@@ -17,4 +17,5 @@ const EmptyStateLarge = forwardRef((props: EmptyStateLargeProps, ref: Ref ;
});
+EmptyStateSmall.displayName = 'EmptyStateSmall';
export default EmptyStateSmall;
diff --git a/packages/design-system/src/components/ErrorState/ErrorState.test.tsx b/packages/design-system/src/components/ErrorState/ErrorState.test.tsx
new file mode 100644
index 00000000000..b0d09adfc52
--- /dev/null
+++ b/packages/design-system/src/components/ErrorState/ErrorState.test.tsx
@@ -0,0 +1,17 @@
+import { describe, it, expect } from '@jest/globals';
+import { axe } from 'jest-axe';
+import { render } from '@testing-library/react';
+import { ErrorState } from './';
+
+describe('ErrorState', () => {
+ it('should render a11y html', async () => {
+ const { container } = render(
+
+
+ ,
+ );
+ expect(container.firstChild).toMatchSnapshot();
+ const results = await axe(document.body);
+ expect(results).toHaveNoViolations();
+ });
+});
diff --git a/packages/design-system/src/components/ErrorState/ErrorState.tsx b/packages/design-system/src/components/ErrorState/ErrorState.tsx
index fac2fd14083..655c47c3c33 100644
--- a/packages/design-system/src/components/ErrorState/ErrorState.tsx
+++ b/packages/design-system/src/components/ErrorState/ErrorState.tsx
@@ -2,7 +2,7 @@ import { ReactElement, isValidElement } from 'react';
import { ButtonPrimary } from '../Button';
import { ButtonPrimaryPropsType } from '../Button/variations/ButtonPrimary';
-import Link from '../Link';
+import { Link } from '../Link';
import { LinkProps } from '../Link/Link';
import { StackVertical } from '../Stack';
@@ -10,14 +10,14 @@ import ErrorIllustration from './illutstrations/ErrorIllustration';
import styles from './ErrorState.module.scss';
-type ErrorStatePropTypes = {
+export type ErrorStatePropTypes = {
title: string;
description: string;
action?: Omit, 'size'>;
link?: ReactElement | LinkProps;
};
-function ErrorState({ title, description, action, link }: ErrorStatePropTypes) {
+export function ErrorState({ title, description, action, link }: ErrorStatePropTypes) {
return (
@@ -35,5 +35,3 @@ function ErrorState({ title, description, action, link }: ErrorStatePropTypes) {
);
}
-
-export default ErrorState;
diff --git a/packages/design-system/src/components/ErrorState/__snapshots__/ErrorState.test.tsx.snap b/packages/design-system/src/components/ErrorState/__snapshots__/ErrorState.test.tsx.snap
new file mode 100644
index 00000000000..4eb0eba62d7
--- /dev/null
+++ b/packages/design-system/src/components/ErrorState/__snapshots__/ErrorState.test.tsx.snap
@@ -0,0 +1,65 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`ErrorState should render a11y html 1`] = `
+
+
+
+
+
+
+
+
+
+
+
+
+
+ small
+
+
+ what happens
+
+
+
+
+
+`;
diff --git a/packages/design-system/src/components/ErrorState/index.ts b/packages/design-system/src/components/ErrorState/index.ts
index b17df79da3f..c8a7981cdb2 100644
--- a/packages/design-system/src/components/ErrorState/index.ts
+++ b/packages/design-system/src/components/ErrorState/index.ts
@@ -1 +1 @@
-export { default } from './ErrorState';
+export * from './ErrorState';
diff --git a/packages/design-system/src/components/Form/Affix/variations/AffixButton.tsx b/packages/design-system/src/components/Form/Affix/variations/AffixButton.tsx
index b7ef75b4881..17efeee1ebd 100644
--- a/packages/design-system/src/components/Form/Affix/variations/AffixButton.tsx
+++ b/packages/design-system/src/components/Form/Affix/variations/AffixButton.tsx
@@ -5,13 +5,14 @@ import classnames from 'classnames';
import { IconNameWithSize } from '@talend/icons/dist/typeUtils';
import { DeprecatedIconNames } from '../../../../types';
-import Tooltip from '../../../Tooltip';
+import { Tooltip, TooltipChildrenFnProps, TooltipChildrenFnRef } from '../../../Tooltip';
import { StackHorizontal } from '../../../Stack';
-import Clickable, { ClickableProps } from '../../../Clickable';
+import { Clickable, ClickableProps } from '../../../Clickable';
import { getIconWithDeprecatedSupport } from '../../../Icon/DeprecatedIconHelper';
import { SizedIcon } from '../../../Icon';
import styles from '../AffixStyles.module.scss';
+import { mergeRefs } from '../../../../mergeRef';
type CommonAffixButtonPropsType = {
children: string;
@@ -34,7 +35,7 @@ export type AffixButtonPropsType = Omit(
(
{
children,
@@ -47,11 +48,12 @@ const AffixButton = forwardRef(
}: AffixButtonPropsType,
ref: Ref,
) => {
- const element = (
+ const element = (subProps: TooltipChildrenFnProps, subRef: TooltipChildrenFnRef) => (
@@ -74,12 +76,14 @@ const AffixButton = forwardRef(
if (hideText) {
return (
- {element}
+ {(triggerProps: TooltipChildrenFnProps, triggerRef: TooltipChildrenFnRef) =>
+ element(triggerProps, mergeRefs([triggerRef, ref]))
+ }
);
}
- return element;
+ return element({}, ref);
},
);
diff --git a/packages/design-system/src/components/Form/Affix/variations/AffixReadOnly.tsx b/packages/design-system/src/components/Form/Affix/variations/AffixReadOnly.tsx
index 0b577f34743..7ed1f178320 100644
--- a/packages/design-system/src/components/Form/Affix/variations/AffixReadOnly.tsx
+++ b/packages/design-system/src/components/Form/Affix/variations/AffixReadOnly.tsx
@@ -4,7 +4,7 @@ import { IconNameWithSize } from '@talend/icons';
import { DeprecatedIconNames } from '../../../../types';
import { StackHorizontal } from '../../../Stack';
-import VisuallyHidden from '../../../VisuallyHidden';
+import { VisuallyHidden } from '../../../VisuallyHidden';
import { getIconWithDeprecatedSupport } from '../../../Icon/DeprecatedIconHelper';
import styles from '../AffixStyles.module.scss';
diff --git a/packages/design-system/src/components/Form/Affix/variations/AffixSelect.tsx b/packages/design-system/src/components/Form/Affix/variations/AffixSelect.tsx
index ab2464fa676..f3e4542bfa0 100644
--- a/packages/design-system/src/components/Form/Affix/variations/AffixSelect.tsx
+++ b/packages/design-system/src/components/Form/Affix/variations/AffixSelect.tsx
@@ -1,7 +1,7 @@
import { forwardRef, Ref } from 'react';
-import { unstable_useId as useId } from 'reakit';
import FieldPrimitive, { FieldPropsPrimitive } from '../../Primitives/Field/Field';
import SelectNoWrapper, { SelectNoWrapperProps } from '../../Primitives/Select/SelectNoWrapper';
+import { useId } from '../../../../useId';
export type AffixSelectPropsType = Omit &
Omit & {
@@ -12,8 +12,7 @@ const AffixSelect = forwardRef((props: AffixSelectPropsType, ref: Ref,
diff --git a/packages/design-system/src/components/Form/Field/Datalist/Datalist.tsx b/packages/design-system/src/components/Form/Field/Datalist/Datalist.tsx
index 9e2bbc70084..24797eae43e 100644
--- a/packages/design-system/src/components/Form/Field/Datalist/Datalist.tsx
+++ b/packages/design-system/src/components/Form/Field/Datalist/Datalist.tsx
@@ -1,11 +1,11 @@
import { forwardRef, Key, Ref } from 'react';
-import { unstable_useId as useId } from 'reakit';
import {
FieldPrimitive,
FieldPropsPrimitive,
InputPrimitive,
InputPrimitiveProps,
-} from '../../Primitives/index';
+} from '../../Primitives';
+import { useId } from '../../../../useId';
export type DatalistProps = {
values: string[];
@@ -36,9 +36,8 @@ const Datalist = forwardRef(
}: DatalistProps,
ref: Ref | undefined,
) => {
- const { id: reakitId } = useId();
- const datalistId = id || `datalist--${reakitId}`;
- const datalistListId = `datalist__list--${reakitId}`;
+ const datalistId = useId(id, 'datalist-');
+ const datalistListId = useId(undefined, 'datalist__list-');
return (
<>
diff --git a/packages/design-system/src/components/Form/Field/Input/Input.Checkbox.tsx b/packages/design-system/src/components/Form/Field/Input/Input.Checkbox.tsx
index 9d784dfb3c4..4160e07a676 100644
--- a/packages/design-system/src/components/Form/Field/Input/Input.Checkbox.tsx
+++ b/packages/design-system/src/components/Form/Field/Input/Input.Checkbox.tsx
@@ -1,7 +1,12 @@
import { forwardRef, Ref } from 'react';
-import { CheckboxPrimitive, CheckboxPrimitiveType } from '../../Primitives/index';
-export type CheckboxProps = Omit;
+import { Mandatory } from '../../../../types';
+import { CheckboxPrimitive, CheckboxPrimitiveType } from '../../Primitives';
+
+export type CheckboxProps = Mandatory<
+ Omit,
+ 'onChange'
+>;
const Checkbox = forwardRef((props: CheckboxProps, ref: Ref) => {
return ;
diff --git a/packages/design-system/src/components/Form/Field/Input/Input.Copy.tsx b/packages/design-system/src/components/Form/Field/Input/Input.Copy.tsx
index 36cc4dac070..21e1242c2b7 100644
--- a/packages/design-system/src/components/Form/Field/Input/Input.Copy.tsx
+++ b/packages/design-system/src/components/Form/Field/Input/Input.Copy.tsx
@@ -7,7 +7,7 @@ import {
FieldPropsPrimitive,
InputPrimitive,
InputPrimitiveProps,
-} from '../../Primitives/index';
+} from '../../Primitives';
type InputCopyProps = Omit &
Omit;
diff --git a/packages/design-system/src/components/Form/Field/Input/Input.File.tsx b/packages/design-system/src/components/Form/Field/Input/Input.File.tsx
index 596ffd0c21b..318d1e1666a 100644
--- a/packages/design-system/src/components/Form/Field/Input/Input.File.tsx
+++ b/packages/design-system/src/components/Form/Field/Input/Input.File.tsx
@@ -1,20 +1,20 @@
import { forwardRef, Key, Ref, useEffect, useRef, useState } from 'react';
-import { unstable_useId as useId } from 'reakit';
import { Trans, useTranslation } from 'react-i18next';
import { TFunction } from 'i18next';
import classnames from 'classnames';
import { I18N_DOMAIN_DESIGN_SYSTEM } from '../../../constants';
import { ButtonIcon } from '../../../ButtonIcon';
import { SizedIcon } from '../../../Icon';
-import VisuallyHidden from '../../../VisuallyHidden';
+import { VisuallyHidden } from '../../../VisuallyHidden';
import {
FieldPrimitive,
FieldPropsPrimitive,
InputPrimitive,
InputPrimitiveProps,
-} from '../../Primitives/index';
+} from '../../Primitives';
import styles from './Input.File.module.scss';
+import { useId } from '../../../../useId';
function getFileSize(size: number, t: TFunction) {
if (size < 1024) {
@@ -93,8 +93,7 @@ const InputFile = forwardRef((props: FileProps, ref: Ref) => {
}
};
}, []);
- const { id: reakitId } = useId();
- const fileInfoId = `info--${reakitId}`;
+ const fileInfoId = useId(undefined, 'info-');
const filesValue = () => {
if (files) {
diff --git a/packages/design-system/src/components/Form/Field/Input/Input.Radio.tsx b/packages/design-system/src/components/Form/Field/Input/Input.Radio.tsx
index 2b5600f7617..27da5a606c2 100644
--- a/packages/design-system/src/components/Form/Field/Input/Input.Radio.tsx
+++ b/packages/design-system/src/components/Form/Field/Input/Input.Radio.tsx
@@ -1,5 +1,5 @@
import { forwardRef, Ref } from 'react';
-import { RadioPrimitive, RadioPrimitiveType } from '../../Primitives/index';
+import { RadioPrimitive, RadioPrimitiveType } from '../../Primitives';
export type RadioProps = Omit;
diff --git a/packages/design-system/src/components/Form/Field/Input/Input.ToggleSwitch.tsx b/packages/design-system/src/components/Form/Field/Input/Input.ToggleSwitch.tsx
index 7791a42a443..c6ee13c2bc1 100644
--- a/packages/design-system/src/components/Form/Field/Input/Input.ToggleSwitch.tsx
+++ b/packages/design-system/src/components/Form/Field/Input/Input.ToggleSwitch.tsx
@@ -1,17 +1,21 @@
import { forwardRef } from 'react';
import type { Ref } from 'react';
-import { Checkbox as ReakitCheckbox, unstable_useId as useId } from 'reakit';
import classnames from 'classnames';
-import useCheckboxState from './hooks/useCheckboxState';
-import { CheckboxProps } from './Input.Checkbox';
+import { useId } from '../../../../useId';
+import { CheckboxPrimitiveType } from '../../Primitives';
+import { useControl } from '../../../../useControl';
import styles from './Input.ToggleSwitch.module.scss';
-const ToggleSwitch = forwardRef(
- (
- {
+export type ToggleSwitchPropTypes = Omit & {
+ onChange?: (checked: boolean) => void;
+};
+
+export const ToggleSwitch = forwardRef(
+ (props: Omit, ref: Ref) => {
+ const {
id,
label,
defaultChecked,
@@ -21,34 +25,38 @@ const ToggleSwitch = forwardRef(
required,
children,
isInline,
+ onChange,
...rest
- }: Omit,
- ref: Ref,
- ) => {
- const { id: reakitId } = useId();
- const switchId = id || `switch--${reakitId}`;
- const checkbox = useCheckboxState({ state: defaultChecked || checked, readOnly });
+ } = props;
+ const switchId = useId(id, 'switch-');
+ const controlled = useControl(props, {
+ onChangeKey: 'onChange',
+ valueKey: 'checked',
+ defaultValueKey: 'defaultChecked',
+ selector: e => e.target.checked,
+ defaultValue: false,
+ });
return (
- {/*
- // ReakitCheckbox is not based on HTMLInputElement despite working like one
- // @ts-ignore */}
- controlled.onChange(e)}
{...rest}
- {...checkbox}
ref={ref}
/>
@@ -61,6 +69,4 @@ const ToggleSwitch = forwardRef(
},
);
-ToggleSwitch.displayName = 'ToggleSwitch';
-
-export default ToggleSwitch;
+ToggleSwitch.displayName = 'ToggleSwitchPrimitive';
diff --git a/packages/design-system/src/components/Form/Field/Input/Input.tsx b/packages/design-system/src/components/Form/Field/Input/Input.tsx
index d33530dacaa..19528ee5c24 100644
--- a/packages/design-system/src/components/Form/Field/Input/Input.tsx
+++ b/packages/design-system/src/components/Form/Field/Input/Input.tsx
@@ -4,7 +4,7 @@ import {
FieldPropsPrimitive,
InputPrimitive,
InputPrimitiveProps,
-} from '../../Primitives/index';
+} from '../../Primitives';
export type InputFieldProps = FieldPropsPrimitive &
Omit;
diff --git a/packages/design-system/src/components/Form/Field/Input/Password/Password.tsx b/packages/design-system/src/components/Form/Field/Input/Password/Password.tsx
index bc22da5e18e..2b714e5cc70 100644
--- a/packages/design-system/src/components/Form/Field/Input/Password/Password.tsx
+++ b/packages/design-system/src/components/Form/Field/Input/Password/Password.tsx
@@ -1,5 +1,5 @@
import { forwardRef, Ref } from 'react';
-import { FieldPropsPrimitive, InputPrimitiveProps } from '../../../Primitives/index';
+import { FieldPropsPrimitive, InputPrimitiveProps } from '../../../Primitives';
import Input from '../Input';
type InputPasswordProps = FieldPropsPrimitive &
diff --git a/packages/design-system/src/components/Form/Field/Input/hooks/useCheckboxState.tsx b/packages/design-system/src/components/Form/Field/Input/hooks/useCheckboxState.tsx
deleted file mode 100644
index 344adc3cca4..00000000000
--- a/packages/design-system/src/components/Form/Field/Input/hooks/useCheckboxState.tsx
+++ /dev/null
@@ -1,25 +0,0 @@
-import { useEffect } from 'react';
-import { CheckboxInitialState, useCheckboxState as useReakitCheckboxState } from 'reakit';
-
-import useReadOnly from './useReadOnly';
-
-type ChoiceState = CheckboxInitialState & {
- readOnly?: boolean;
-};
-
-export default function useCheckboxState({ readOnly, ...choiceState }: ChoiceState) {
- const checkboxState = useReakitCheckboxState(choiceState);
- const readOnlyState = useReadOnly(choiceState.state);
- const { setState } = checkboxState;
-
- useEffect(() => {
- if (choiceState.state !== undefined) {
- setState(choiceState.state);
- }
- }, [setState, choiceState.state]);
-
- if (readOnly) {
- return { ...checkboxState, ...readOnlyState, setState: () => {} };
- }
- return checkboxState;
-}
diff --git a/packages/design-system/src/components/Form/Field/Input/hooks/useRevealPassword.tsx b/packages/design-system/src/components/Form/Field/Input/hooks/useRevealPassword.tsx
index 3e1917d582a..8298704dfc9 100644
--- a/packages/design-system/src/components/Form/Field/Input/hooks/useRevealPassword.tsx
+++ b/packages/design-system/src/components/Form/Field/Input/hooks/useRevealPassword.tsx
@@ -3,10 +3,11 @@ import type { MouseEvent } from 'react';
import { useTranslation } from 'react-i18next';
import { I18N_DOMAIN_DESIGN_SYSTEM } from '../../../../constants';
-import Clickable from '../../../../Clickable';
-import Tooltip from '../../../../Tooltip';
+import { Clickable } from '../../../../Clickable';
+import { Tooltip } from '../../../../Tooltip';
import { SizedIcon } from '../../../../Icon';
import styles from './passwordButton.module.scss';
+import { TooltipChildrenFnProps, TooltipChildrenFnRef } from '../../../../Tooltip/Tooltip';
export default function useRevealPassword() {
const [revealed, setRevealed] = useState(false);
@@ -31,21 +32,25 @@ export default function useRevealPassword() {
const { onClick, disabled } = props;
return (
- {
- onReveal(e);
- if (onClick) {
- onClick(e);
- }
- }}
- tabIndex={-1}
- aria-hidden
- data-testid="form.password.reveal"
- disabled={disabled}
- >
-
-
+ {(triggerProps: TooltipChildrenFnProps, ref: TooltipChildrenFnRef) => (
+ {
+ onReveal(e);
+ if (onClick) {
+ onClick(e);
+ }
+ }}
+ tabIndex={-1}
+ aria-hidden
+ data-testid="form.password.reveal"
+ disabled={disabled}
+ >
+
+
+ )}
);
}
diff --git a/packages/design-system/src/components/Form/Field/Input/index.ts b/packages/design-system/src/components/Form/Field/Input/index.ts
index 2212eb54fac..e70e8eb1fc3 100644
--- a/packages/design-system/src/components/Form/Field/Input/index.ts
+++ b/packages/design-system/src/components/Form/Field/Input/index.ts
@@ -1,6 +1,6 @@
import Input from './Input';
-import InputCheckbox from './Input.Checkbox';
+import Checkbox from './Input.Checkbox';
import InputColor from './Input.Color';
import InputCopy from './Input.Copy';
import InputDate from './Input.Date';
@@ -10,18 +10,18 @@ import InputFile from './Input.File';
import InputHidden from './Input.Hidden';
import InputMonth from './Input.Month';
import InputNumber from './Input.Number';
-import InputPassword from './Password';
import InputRadio from './Input.Radio';
import InputSearch from './Input.Search';
-import InputToggleSwitch from './Input.ToggleSwitch';
+import { ToggleSwitch } from './Input.ToggleSwitch';
import InputTel from './Input.Tel';
import InputText from './Input.Text';
import InputTime from './Input.Time';
import InputUrl from './Input.Url';
import InputWeek from './Input.Week';
+import InputPassword from './Password';
const InputComponent = Input as typeof Input & {
- Checkbox: typeof InputCheckbox;
+ Checkbox: typeof Checkbox;
Color: typeof InputColor;
Copy: typeof InputCopy;
Date: typeof InputDate;
@@ -34,15 +34,15 @@ const InputComponent = Input as typeof Input & {
Password: typeof InputPassword;
Radio: typeof InputRadio;
Search: typeof InputSearch;
- ToggleSwitch: typeof InputToggleSwitch;
Tel: typeof InputTel;
Text: typeof InputText;
Time: typeof InputTime;
+ ToggleSwitch: typeof ToggleSwitch;
Url: typeof InputUrl;
Week: typeof InputWeek;
};
-InputComponent.Checkbox = InputCheckbox;
+InputComponent.Checkbox = Checkbox;
InputComponent.Color = InputColor;
InputComponent.Copy = InputCopy;
InputComponent.Date = InputDate;
@@ -55,10 +55,10 @@ InputComponent.Number = InputNumber;
InputComponent.Password = InputPassword;
InputComponent.Radio = InputRadio;
InputComponent.Search = InputSearch;
-InputComponent.ToggleSwitch = InputToggleSwitch;
InputComponent.Tel = InputTel;
InputComponent.Text = InputText;
InputComponent.Time = InputTime;
+InputComponent.ToggleSwitch = ToggleSwitch;
InputComponent.Url = InputUrl;
InputComponent.Week = InputWeek;
diff --git a/packages/design-system/src/components/Form/Field/Select/Select.tsx b/packages/design-system/src/components/Form/Field/Select/Select.tsx
index acd603023ec..4103f03ada3 100644
--- a/packages/design-system/src/components/Form/Field/Select/Select.tsx
+++ b/packages/design-system/src/components/Form/Field/Select/Select.tsx
@@ -7,7 +7,7 @@ import {
FieldPropsPrimitive,
SelectPrimitive,
SelectPrimitiveProps,
-} from '../../Primitives/index';
+} from '../../Primitives';
export type SelectProps = FieldPropsPrimitive &
Omit & { readOnly?: boolean };
diff --git a/packages/design-system/src/components/Form/Field/Textarea/Textarea.tsx b/packages/design-system/src/components/Form/Field/Textarea/Textarea.tsx
index 263c7b05f4b..15fa0b46989 100644
--- a/packages/design-system/src/components/Form/Field/Textarea/Textarea.tsx
+++ b/packages/design-system/src/components/Form/Field/Textarea/Textarea.tsx
@@ -4,7 +4,7 @@ import {
FieldPropsPrimitive,
TextareaPrimitive,
TextareaPrimitiveProps,
-} from '../../Primitives/index';
+} from '../../Primitives';
export type InputTextareaProps = FieldPropsPrimitive &
Omit & { children?: string };
diff --git a/packages/design-system/src/components/Form/Form.test.tsx b/packages/design-system/src/components/Form/Form.test.tsx
new file mode 100644
index 00000000000..f0d2e326860
--- /dev/null
+++ b/packages/design-system/src/components/Form/Form.test.tsx
@@ -0,0 +1,49 @@
+import { describe, it, expect } from '@jest/globals';
+import { axe } from 'jest-axe';
+import { render } from '@testing-library/react';
+import { Form } from './';
+import { ButtonPrimary, ButtonSecondary } from '../Button';
+
+jest.mock('@talend/utils', () => {
+ let i = 0;
+ return {
+ // we need stable but different uuid (is fixed to 42 by current mock)
+ randomUUID: () => `mocked-uuid-${i++}`,
+ };
+});
+
+describe('Form', () => {
+ it('should render a11y html', async () => {
+ const { container } = render(
+
+
+
+
+
+
+
+
+
+
+ Foo
+ Bar
+
+
+
+
+
+
+ Reset
+
+ Submit
+
+
+
+ ,
+ );
+ expect(container.firstChild).toMatchSnapshot();
+ const results = await axe(document.body);
+ expect(results).toHaveNoViolations();
+ });
+});
diff --git a/packages/design-system/src/components/Form/Form.tsx b/packages/design-system/src/components/Form/Form.tsx
index 95e984a689a..f42198c2a87 100644
--- a/packages/design-system/src/components/Form/Form.tsx
+++ b/packages/design-system/src/components/Form/Form.tsx
@@ -5,12 +5,12 @@ import { isElement } from 'react-is';
import styles from './Form.module.scss';
-type FormProps = FormHTMLAttributes & {
+export type FormProps = FormHTMLAttributes & {
disabled?: boolean;
readOnly?: boolean;
};
-const Form = forwardRef(
+export const Form = forwardRef(
({ disabled, readOnly, children, ...rest }: FormProps, ref: Ref) => {
const childrenProps: { disabled?: boolean; readOnly?: boolean } = {};
if (disabled) childrenProps.disabled = true;
@@ -27,5 +27,3 @@ const Form = forwardRef(
);
Form.displayName = 'Form';
-
-export default Form;
diff --git a/packages/design-system/src/components/Form/Label/Label.tsx b/packages/design-system/src/components/Form/Label/Label.tsx
index b06ef676f36..ae881e467da 100644
--- a/packages/design-system/src/components/Form/Label/Label.tsx
+++ b/packages/design-system/src/components/Form/Label/Label.tsx
@@ -1,5 +1,5 @@
import { forwardRef, Ref } from 'react';
-import { LabelPrimitive, LabelPrimitiveProps } from '../Primitives/index';
+import LabelPrimitive, { LabelPrimitiveProps } from '../Primitives/Label/Label';
const Label = forwardRef(
(props: Omit, ref: Ref) => {
diff --git a/packages/design-system/src/components/Form/Primitives/Checkbox/Checkbox.module.scss b/packages/design-system/src/components/Form/Primitives/Checkbox/Checkbox.module.scss
index 0f63eecc494..1ba49d45dfa 100644
--- a/packages/design-system/src/components/Form/Primitives/Checkbox/Checkbox.module.scss
+++ b/packages/design-system/src/components/Form/Primitives/Checkbox/Checkbox.module.scss
@@ -1,7 +1,7 @@
@use '~@talend/design-tokens/lib/tokens';
$checkmark-image: url('data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iOCIgaGVpZ2h0PSI4IiBmaWxsPSJub25lIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxwYXRoIGQ9Ik03LjA1IDEgMyA1LjEuOTUgMy4wNDk1IDAgNGwzIDMgNS01LjA1TDcuMDUgMVoiIGZpbGw9IiNmZmYiLz48bWFzayBpZD0iYSIgc3R5bGU9Im1hc2stdHlwZTphbHBoYSIgbWFza1VuaXRzPSJ1c2VyU3BhY2VPblVzZSIgeD0iMCIgeT0iMSIgd2lkdGg9IjgiIGhlaWdodD0iNiI+PHBhdGggZD0iTTcuMDUgMSAzIDUuMS45NSAzLjA0OTUgMCA0bDMgMyA1LTUuMDVMNy4wNSAxWiIgZmlsbD0iI2ZmZiIvPjwvbWFzaz48L3N2Zz4=');
-$checkmark-image-indeterminate: url('data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iOCIgaGVpZ2h0PSI4IiBmaWxsPSJub25lIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxwYXRoIGZpbGwtcnVsZT0iZXZlbm9kZCIgY2xpcC1ydWxlPSJldmVub2RkIiBkPSJNOCA1SDBWM2g4djJaIiBmaWxsPSIjZmZmIi8+PC9zdmc+');;
+$checkmark-image-indeterminate: url('data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iOCIgaGVpZ2h0PSI4IiBmaWxsPSJub25lIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxwYXRoIGZpbGwtcnVsZT0iZXZlbm9kZCIgY2xpcC1ydWxlPSJldmVub2RkIiBkPSJNOCA1SDBWM2g4djJaIiBmaWxsPSIjZmZmIi8+PC9zdmc+');
$checkbox-size: tokens.$coral-sizing-xxxs;
.checkbox {
@@ -128,7 +128,6 @@ $checkbox-size: tokens.$coral-sizing-xxxs;
}
}
-
// Disabled and read-only cases
input[type='checkbox']:disabled + label,
@@ -162,6 +161,7 @@ $checkbox-size: tokens.$coral-sizing-xxxs;
&_readOnly {
input[type='checkbox'] {
cursor: not-allowed;
+ pointer-events: none;
}
input[type='checkbox']:not(:checked) + label,
diff --git a/packages/design-system/src/components/Form/Primitives/Checkbox/Checkbox.tsx b/packages/design-system/src/components/Form/Primitives/Checkbox/Checkbox.tsx
index 0b3bcb06711..9195b8976a8 100644
--- a/packages/design-system/src/components/Form/Primitives/Checkbox/Checkbox.tsx
+++ b/packages/design-system/src/components/Form/Primitives/Checkbox/Checkbox.tsx
@@ -1,13 +1,25 @@
-import { forwardRef, ReactElement, Ref } from 'react';
-import { Checkbox as ReakitCheckbox, CheckboxProps, unstable_useId as useId } from 'reakit';
+import {
+ forwardRef,
+ ReactElement,
+ Ref,
+ InputHTMLAttributes,
+ useEffect,
+ useRef,
+ useImperativeHandle,
+} from 'react';
import { ReactI18NextChild } from 'react-i18next';
import classnames from 'classnames';
-import useCheckboxState from '../../../Form/Field/Input/hooks/useCheckboxState';
-import Label from '../Label/Label';
+import { useId } from '../../../../useId';
+import { useControl } from '../../../../useControl';
+import Label from '../../Label';
import styles from './Checkbox.module.scss';
+type CheckboxProps = InputHTMLAttributes & {
+ value?: string | number;
+};
+
export type CheckboxPrimitiveType = Omit & {
id?: string;
indeterminate?: boolean;
@@ -16,49 +28,67 @@ export type CheckboxPrimitiveType = Omit & {
name: string;
};
-const Checkbox = forwardRef((props: CheckboxPrimitiveType, ref: Ref) => {
- const {
- id,
- label,
- readOnly = false,
- disabled = false,
- isInline = false,
- checked,
- defaultChecked,
- indeterminate,
- ...rest
- } = props;
- const { id: reakitId } = useId();
- const checkboxId = id || `checkbox--${reakitId}`;
- const state = (indeterminate && 'indeterminate') || defaultChecked || checked;
- const checkboxState = useCheckboxState({
- state,
- readOnly: readOnly,
- });
+const CheckboxPrimitive = forwardRef(
+ (props: CheckboxPrimitiveType, ref: Ref) => {
+ const checkboxRef = useRef(null);
+ // Forward ref to parent ref
+ useImperativeHandle(ref, () => checkboxRef.current);
+
+ const {
+ id,
+ label,
+ readOnly = false,
+ disabled = false,
+ isInline = false,
+ checked,
+ indeterminate,
+ onChange,
+ ...rest
+ } = props;
+
+ const checkboxId = useId(id, 'checkbox-');
+
+ const controlled = useControl(props, {
+ onChangeKey: 'onChange',
+ valueKey: 'checked',
+ defaultValueKey: 'defaultChecked',
+ selector: e => e.target.checked,
+ defaultValue: false,
+ });
+
+ useEffect(() => {
+ // indeterminate is a controlled value only
+ if (checkboxRef?.current) {
+ checkboxRef.current.indeterminate = !!indeterminate;
+ }
+ }, [checkboxRef, indeterminate, controlled.value]);
- return (
-
-
-
- {label}
-
-
- );
-});
+ return (
+
+ controlled.onChange(e)}
+ {...rest}
+ />
+
+ {label}
+
+
+ );
+ },
+);
-Checkbox.displayName = 'Checkbox';
+CheckboxPrimitive.displayName = 'CheckboxPrimtive';
-export default Checkbox;
+export default CheckboxPrimitive;
diff --git a/packages/design-system/src/components/Form/Primitives/Field/Field.tsx b/packages/design-system/src/components/Form/Primitives/Field/Field.tsx
index df990c50fad..cfc678424a1 100644
--- a/packages/design-system/src/components/Form/Primitives/Field/Field.tsx
+++ b/packages/design-system/src/components/Form/Primitives/Field/Field.tsx
@@ -1,10 +1,11 @@
import { cloneElement, forwardRef, ReactElement, Ref } from 'react';
+
import Link, { LinkProps } from '../../../Link/Link';
import { StackVertical } from '../../../Stack';
import Label, { LabelPrimitiveProps } from '../Label/Label';
import { InlineMessageDestructive, InlineMessageInformation } from '../../../InlineMessage';
-import VisuallyHidden from '../../../VisuallyHidden';
-import { unstable_useId as useId } from 'reakit';
+import { VisuallyHidden } from '../../../VisuallyHidden';
+import { useId } from '../../../../useId';
export type FieldStatusProps =
| {
@@ -42,8 +43,7 @@ const Field = forwardRef(
...rest
} = props;
- const { id: reakitId } = useId();
- const fieldID = id || `field--${reakitId}`;
+ const fieldID = useId(id, 'field-');
const labelProps = typeof label === 'string' ? { children: label } : { ...label };
diff --git a/packages/design-system/src/components/Form/Primitives/Radio/Radio.module.scss b/packages/design-system/src/components/Form/Primitives/Radio/Radio.module.scss
index dd5181d543c..e0ae538d9f7 100644
--- a/packages/design-system/src/components/Form/Primitives/Radio/Radio.module.scss
+++ b/packages/design-system/src/components/Form/Primitives/Radio/Radio.module.scss
@@ -28,8 +28,6 @@ $mark-size: calc(#{tokens.$coral-sizing-xxs} / 2);
&::before,
&::after {
content: '';
- width: $radio-size;
- height: $radio-size;
border-radius: tokens.$coral-radius-round;
transition: tokens.$coral-transition-fast;
display: block;
@@ -42,6 +40,8 @@ $mark-size: calc(#{tokens.$coral-sizing-xxs} / 2);
// Radio
&::before {
z-index: 1;
+ width: $radio-size;
+ height: $radio-size;
border: tokens.$coral-border-s-solid tokens.$coral-color-neutral-border;
background: tokens.$coral-color-neutral-background-medium;
}
@@ -114,7 +114,6 @@ $mark-size: calc(#{tokens.$coral-sizing-xxs} / 2);
}
}
-
// Disabled and read-only cases
input[type='radio']:disabled + label,
diff --git a/packages/design-system/src/components/Form/Primitives/Radio/Radio.tsx b/packages/design-system/src/components/Form/Primitives/Radio/Radio.tsx
index 16a277cb7b1..6bda1da8eae 100644
--- a/packages/design-system/src/components/Form/Primitives/Radio/Radio.tsx
+++ b/packages/design-system/src/components/Form/Primitives/Radio/Radio.tsx
@@ -1,10 +1,11 @@
import { forwardRef, InputHTMLAttributes, Ref } from 'react';
-import { unstable_useId as useId } from 'reakit';
+
import classnames from 'classnames';
import Label from '../Label/Label';
import useReadOnly from '../../../Form/Field/Input/hooks/useReadOnly';
import styles from './Radio.module.scss';
+import { useId } from '../../../../useId';
export type RadioPrimitiveType = Omit, 'type' | 'prefix'> & {
label: string;
@@ -13,8 +14,7 @@ export type RadioPrimitiveType = Omit, 'ty
const Radio = forwardRef((props: RadioPrimitiveType, ref: Ref) => {
const { id, label, readOnly, disabled, defaultChecked, checked, ...rest } = props;
- const { id: reakitId } = useId();
- const radioId = id || `checkbox--${reakitId}`;
+ const radioId = useId(id, 'checkbox-');
const readOnlyRadioProps = useReadOnly(defaultChecked || checked);
return (
diff --git a/packages/design-system/src/components/Form/Row/Row.tsx b/packages/design-system/src/components/Form/Row/Row.tsx
index dad55a9fcd4..b963b2594d3 100644
--- a/packages/design-system/src/components/Form/Row/Row.tsx
+++ b/packages/design-system/src/components/Form/Row/Row.tsx
@@ -33,5 +33,5 @@ const Row = forwardRef(
);
},
);
-
+Row.displayName = 'Row';
export default Row;
diff --git a/packages/design-system/src/components/Form/__snapshots__/Form.test.tsx.snap b/packages/design-system/src/components/Form/__snapshots__/Form.test.tsx.snap
new file mode 100644
index 00000000000..7d8daa17e16
--- /dev/null
+++ b/packages/design-system/src/components/Form/__snapshots__/Form.test.tsx.snap
@@ -0,0 +1,373 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`Form should render a11y html 1`] = `
+
+
+
+`;
diff --git a/packages/design-system/src/components/Form/index.ts b/packages/design-system/src/components/Form/index.ts
index ed228666ad7..ca58df67445 100644
--- a/packages/design-system/src/components/Form/index.ts
+++ b/packages/design-system/src/components/Form/index.ts
@@ -1,14 +1,15 @@
-import Datalist from './Field/Datalist';
-import Fieldset from './Fieldset';
-import Form from './Form';
-import Row from './Row';
import Buttons from './Buttons';
+import Datalist from './Field/Datalist';
import Input from './Field/Input';
-import Label from './Label';
import Select from './Field/Select';
import Textarea from './Field/Textarea';
+import Fieldset from './Fieldset';
+import { Form as FormBase, FormProps } from './Form';
+import Label from './Label';
+import Row from './Row';
-export const FormComponent = Form as typeof Form & {
+export type { FormProps };
+export type FormType = typeof FormBase & {
Row: typeof Row;
Color: typeof Input.Color;
Checkbox: typeof Input.Checkbox;
@@ -27,7 +28,6 @@ export const FormComponent = Form as typeof Form & {
Radio: typeof Input.Radio;
Search: typeof Input.Search;
Select: typeof Select;
- ToggleSwitch: typeof Input.ToggleSwitch;
Tel: typeof Input.Tel;
Text: typeof Input.Text;
Textarea: typeof Textarea;
@@ -36,36 +36,35 @@ export const FormComponent = Form as typeof Form & {
Week: typeof Input.Week;
Buttons: typeof Buttons;
Input: typeof Input;
+ ToggleSwitch: typeof Input.ToggleSwitch;
};
-FormComponent.Row = Row;
-
-FormComponent.Color = Input.Color;
-FormComponent.Checkbox = Input.Checkbox;
-FormComponent.Datalist = Datalist;
-FormComponent.Date = Input.Date;
-FormComponent.DatetimeLocal = Input.DatetimeLocal;
-FormComponent.Email = Input.Email;
-FormComponent.Fieldset = Fieldset;
-FormComponent.File = Input.File;
-FormComponent.Hidden = Input.Hidden;
-FormComponent.Copy = Input.Copy;
-FormComponent.Label = Label;
-FormComponent.Month = Input.Month;
-FormComponent.Number = Input.Number;
-FormComponent.Password = Input.Password;
-FormComponent.Radio = Input.Radio;
-FormComponent.Search = Input.Search;
-FormComponent.Select = Select;
-FormComponent.ToggleSwitch = Input.ToggleSwitch;
-FormComponent.Tel = Input.Tel;
-FormComponent.Text = Input.Text;
-FormComponent.Textarea = Textarea;
-FormComponent.Time = Input.Time;
-FormComponent.Url = Input.Url;
-FormComponent.Week = Input.Week;
-FormComponent.Input = Input;
-
-FormComponent.Buttons = Buttons;
+export const Form = FormBase as FormType;
+Form.Row = Row;
+Form.Color = Input.Color;
+Form.Copy = Input.Copy;
+Form.Checkbox = Input.Checkbox;
+Form.Datalist = Datalist;
+Form.Date = Input.Date;
+Form.DatetimeLocal = Input.DatetimeLocal;
+Form.Email = Input.Email;
+Form.Fieldset = Fieldset;
+Form.File = Input.File;
+Form.Hidden = Input.Hidden;
+Form.Label = Label;
+Form.Month = Input.Month;
+Form.Number = Input.Number;
+Form.Password = Input.Password;
+Form.Radio = Input.Radio;
+Form.Search = Input.Search;
+Form.Select = Select;
+Form.ToggleSwitch = Input.ToggleSwitch;
+Form.Tel = Input.Tel;
+Form.Text = Input.Text;
+Form.Textarea = Textarea;
+Form.Time = Input.Time;
+Form.Url = Input.Url;
+Form.Week = Input.Week;
+Form.Input = Input;
-export default FormComponent;
+Form.Buttons = Buttons;
diff --git a/packages/design-system/src/components/Icon/Icon.test.tsx b/packages/design-system/src/components/Icon/Icon.test.tsx
new file mode 100644
index 00000000000..b0cf38ee051
--- /dev/null
+++ b/packages/design-system/src/components/Icon/Icon.test.tsx
@@ -0,0 +1,28 @@
+/* eslint-disable import/no-extraneous-dependencies */
+import { describe, it, expect } from '@jest/globals';
+import { axe } from 'jest-axe';
+import { render } from '@testing-library/react';
+import { Icon } from './';
+
+describe('Icon', () => {
+ it('should render a11y html', async () => {
+ global.self.fetch.mockResponse = {
+ status: 200,
+ ok: true,
+ text: () =>
+ new Promise(resolve => {
+ resolve(undefined);
+ }),
+ };
+ const { container } = render(
+
+
+
+
+ ,
+ );
+ expect(container.firstChild).toMatchSnapshot();
+ const results = await axe(document.body);
+ expect(results).toHaveNoViolations();
+ });
+});
diff --git a/packages/design-system/src/components/Icon/Icon.tsx b/packages/design-system/src/components/Icon/Icon.tsx
index bb98f417b11..eb3da6c0270 100644
--- a/packages/design-system/src/components/Icon/Icon.tsx
+++ b/packages/design-system/src/components/Icon/Icon.tsx
@@ -1,8 +1,7 @@
import { forwardRef, createRef, useState, useEffect, memo } from 'react';
-import type { PropsWithChildren, Ref } from 'react';
+import type { CSSProperties, Ref } from 'react';
import classnames from 'classnames';
// eslint-disable-next-line @talend/import-depth
-import { DeprecatedIconNames } from '../../types';
import { IconsProvider } from '../IconsProvider';
import style from './Icon.module.scss';
@@ -20,20 +19,22 @@ export enum SVG_TRANSFORMS {
FlipVertical = 'flip-vertical',
}
-export type IconProps = PropsWithChildren & {
- name: DeprecatedIconNames;
- transform: SVG_TRANSFORMS;
- preserveColor: boolean;
- border: boolean;
+export type IconProps = {
+ name: string;
+ className?: string;
+ id?: string;
+ style?: CSSProperties;
+ transform?: SVG_TRANSFORMS;
+ border?: boolean;
};
const accessibility = {
- focusable: 'false',
- 'aria-hidden': 'true',
+ focusable: false,
+ 'aria-hidden': true,
};
// eslint-disable-next-line react/display-name
-export const Icon = forwardRef(
+const IconBase = forwardRef(
(
{ className, name = 'talend-empty-space', transform, border, ...rest }: IconProps,
ref: Ref,
@@ -111,19 +112,25 @@ export const Icon = forwardRef(
const classname = classnames('tc-icon', style.svg, className, {
[`tc-icon-name-${name}`]: !(isImg || isRemote),
[style.border]: border,
- [style[transform]]: !!transform,
+ [style[transform || '']]: !!transform,
});
if (isImg) {
return (
-
+
);
}
if (isRemote && content && !isRemoteSVG) {
return (
+
+
+
+
+`;
diff --git a/packages/design-system/src/components/Icon/index.ts b/packages/design-system/src/components/Icon/index.ts
index 1de4aa069b4..ca5d80d8ae4 100644
--- a/packages/design-system/src/components/Icon/index.ts
+++ b/packages/design-system/src/components/Icon/index.ts
@@ -1,5 +1,2 @@
-import { IconMemo } from './Icon';
-
+export * from './Icon';
export * from './SizedIcon';
-
-export const Icon = IconMemo;
diff --git a/packages/design-system/src/components/IconsProvider/IconsProvider.tsx b/packages/design-system/src/components/IconsProvider/IconsProvider.tsx
index 3d57950a6b0..10afff180fa 100644
--- a/packages/design-system/src/components/IconsProvider/IconsProvider.tsx
+++ b/packages/design-system/src/components/IconsProvider/IconsProvider.tsx
@@ -1,4 +1,4 @@
-import VisuallyHidden from '../VisuallyHidden';
+import { VisuallyHidden } from '../VisuallyHidden';
import assetsAPI from '@talend/assets-api';
import { ReactElement, RefObject, useState, useEffect, useRef } from 'react';
diff --git a/packages/design-system/src/components/InlineEditing/InlineEditing.cy.tsx b/packages/design-system/src/components/InlineEditing/InlineEditing.cy.tsx
index 584b4adf141..ab5beeb2457 100644
--- a/packages/design-system/src/components/InlineEditing/InlineEditing.cy.tsx
+++ b/packages/design-system/src/components/InlineEditing/InlineEditing.cy.tsx
@@ -1,7 +1,7 @@
/* eslint-disable cypress/unsafe-to-chain-command */
/* eslint-disable testing-library/await-async-query */
/* eslint-disable testing-library/prefer-screen-queries */
-import InlineEditing from './';
+import { InlineEditing } from './';
context(' ', () => {
it('should go to edit mode when clicking on the button', () => {
@@ -24,7 +24,7 @@ context(' ', () => {
placeholder=""
/>,
);
- cy.findByTestId('inlineediting.button.edit').click();
+ cy.findByTestId('inlineediting.button.edit').click({ force: true });
cy.findByTestId('inlineediting.textarea').should('exist');
});
@@ -37,22 +37,20 @@ context(' ', () => {
/>,
);
cy.findByTestId('inlineediting.button.edit').click();
- cy.findByTestId('inlineediting.input')
- .focus()
- .type('{selectall}{del}blah')
- .should('have.value', 'blah')
- .type('{esc}');
+ cy.findByTestId('inlineediting.input').focus();
+ cy.findByTestId('inlineediting.input').type('{selectall}{del}blah');
+ cy.findByTestId('inlineediting.input').should('have.value', 'blah');
+ cy.findByTestId('inlineediting.input').type('{esc}');
cy.findByTestId('inlineediting').should('have.text', 'Lorem ipsum dolor sit amet');
});
it('should validate on Enter', () => {
cy.mount( );
cy.findByTestId('inlineediting.button.edit').click();
- cy.findByTestId('inlineediting.input')
- .focus()
- .type('{selectall}{del}blah')
- .should('have.value', 'blah')
- .type('{enter}');
+ cy.findByTestId('inlineediting.input').focus();
+ cy.findByTestId('inlineediting.input').type('{selectall}{del}blah');
+ cy.findByTestId('inlineediting.input').should('have.value', 'blah');
+ cy.findByTestId('inlineediting.input').type('{enter}');
cy.findByTestId('inlineediting').should('have.text', 'blah');
});
@@ -64,12 +62,11 @@ context(' ', () => {
placeholder=""
/>,
);
- cy.findByTestId('inlineediting.button.edit').click();
- cy.findByTestId('inlineediting.textarea')
- .focus()
- .type('{selectall}{del}blah')
- .should('have.value', 'blah')
- .type('{enter}');
+ cy.findByTestId('inlineediting.button.edit').click({ force: true });
+ cy.findByTestId('inlineediting.textarea').focus();
+ cy.findByTestId('inlineediting.textarea').type('{selectall}{del}blah');
+ cy.findByTestId('inlineediting.textarea').should('have.value', 'blah');
+ cy.findByTestId('inlineediting.textarea').type('{enter}');
cy.findByTestId('inlineediting.textarea').should('exist');
});
@@ -79,12 +76,11 @@ context(' ', () => {
cy.mount(
,
);
- cy.findByTestId('inlineediting.button.edit').click();
- cy.findByTestId('inlineediting.textarea')
- .focus()
- .type('{selectall}{del}blah')
- .should('have.value', 'blah');
- cy.findByTestId('inlineediting.button.cancel').focus().type('{enter}');
+ cy.findByTestId('inlineediting.button.edit').click({ force: true });
+ cy.findByTestId('inlineediting.textarea').focus();
+ cy.findByTestId('inlineediting.textarea').type('{selectall}{del}blah');
+ cy.findByTestId('inlineediting.textarea').should('have.value', 'blah');
+ cy.findByTestId('inlineediting.button.cancel').type('{enter}', { force: true });
cy.findByTestId('inlineediting').should('have.text', defaultValue);
});
});
diff --git a/packages/design-system/src/components/InlineEditing/InlineEditing.test.tsx b/packages/design-system/src/components/InlineEditing/InlineEditing.test.tsx
new file mode 100644
index 00000000000..0f9b9e4e3bc
--- /dev/null
+++ b/packages/design-system/src/components/InlineEditing/InlineEditing.test.tsx
@@ -0,0 +1,33 @@
+/* eslint-disable import/no-extraneous-dependencies */
+import { describe, it, expect } from '@jest/globals';
+import { axe } from 'jest-axe';
+import { render } from '@testing-library/react';
+import { InlineEditing } from './';
+
+jest.mock('@talend/utils', () => {
+ let i = 0;
+ return {
+ // we need stable but different uuid (is fixed to 42 by current mock)
+ randomUUID: () => `mocked-uuid-${i++}`,
+ };
+});
+
+describe('InlineEditing', () => {
+ it('should render a11y html', async () => {
+ const { container } = render(
+
+
+ ,
+ );
+ // eslint-disable-next-line testing-library/no-container
+ container.querySelector('button')?.click();
+ expect(container.firstChild).toMatchSnapshot();
+ const results = await axe(document.body);
+ expect(results).toHaveNoViolations();
+ });
+});
diff --git a/packages/design-system/src/components/InlineEditing/Primitives/InlineEditingPrimitive.module.scss b/packages/design-system/src/components/InlineEditing/Primitives/InlineEditingPrimitive.module.scss
index ddbf4199f3e..d538e81e143 100644
--- a/packages/design-system/src/components/InlineEditing/Primitives/InlineEditingPrimitive.module.scss
+++ b/packages/design-system/src/components/InlineEditing/Primitives/InlineEditingPrimitive.module.scss
@@ -71,6 +71,7 @@
}
&:hover,
+ &:focus-within,
&:active,
&:focus {
opacity: 1;
diff --git a/packages/design-system/src/components/InlineEditing/Primitives/InlineEditingPrimitive.tsx b/packages/design-system/src/components/InlineEditing/Primitives/InlineEditingPrimitive.tsx
index 72af8a0e640..148b91c3800 100644
--- a/packages/design-system/src/components/InlineEditing/Primitives/InlineEditingPrimitive.tsx
+++ b/packages/design-system/src/components/InlineEditing/Primitives/InlineEditingPrimitive.tsx
@@ -15,7 +15,7 @@ import classnames from 'classnames';
import keycode from 'keycode';
import { ButtonIcon } from '../../ButtonIcon';
-import Form from '../../Form';
+import { Form } from '../../Form';
import { StackHorizontal } from '../../Stack';
import { I18N_DOMAIN_DESIGN_SYSTEM } from '../../constants';
diff --git a/packages/design-system/src/components/InlineEditing/__snapshots__/InlineEditing.test.tsx.snap b/packages/design-system/src/components/InlineEditing/__snapshots__/InlineEditing.test.tsx.snap
new file mode 100644
index 00000000000..e1160214015
--- /dev/null
+++ b/packages/design-system/src/components/InlineEditing/__snapshots__/InlineEditing.test.tsx.snap
@@ -0,0 +1,103 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`InlineEditing should render a11y html 1`] = `
+
+
+
+
+
+
+ Edit the value
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+`;
diff --git a/packages/design-system/src/components/InlineEditing/index.ts b/packages/design-system/src/components/InlineEditing/index.ts
index bc47dc12865..ec8ebe01d76 100644
--- a/packages/design-system/src/components/InlineEditing/index.ts
+++ b/packages/design-system/src/components/InlineEditing/index.ts
@@ -1,12 +1,17 @@
-import InlineEditingText from './variations/InlineEditing.text';
-import InlineEditingTextarea from './variations/InlineEditing.textarea';
+import { InlineEditingText, InlineEditingTextProps } from './variations/InlineEditing.text';
+import {
+ InlineEditingTextarea,
+ InlineEditingTextareaProps,
+} from './variations/InlineEditing.textarea';
-const InlineEditingComponent = InlineEditingText as typeof InlineEditingText & {
+const InlineEditing = InlineEditingText as typeof InlineEditingText & {
Text: typeof InlineEditingText;
Textarea: typeof InlineEditingTextarea;
};
-InlineEditingComponent.Text = InlineEditingText;
-InlineEditingComponent.Textarea = InlineEditingTextarea;
+InlineEditing.Text = InlineEditingText;
+InlineEditing.Textarea = InlineEditingTextarea;
-export default InlineEditingComponent;
+export type InlineEditingProps = InlineEditingTextProps;
+export { InlineEditing };
+export type { InlineEditingTextProps, InlineEditingTextareaProps };
diff --git a/packages/design-system/src/components/InlineEditing/variations/InlineEditing.text.tsx b/packages/design-system/src/components/InlineEditing/variations/InlineEditing.text.tsx
index c9091e4862a..dc9254a0094 100644
--- a/packages/design-system/src/components/InlineEditing/variations/InlineEditing.text.tsx
+++ b/packages/design-system/src/components/InlineEditing/variations/InlineEditing.text.tsx
@@ -3,12 +3,12 @@ import InlineEditingPrimitive, {
InlineEditingPrimitiveProps,
} from '../Primitives/InlineEditingPrimitive';
-const InlineEditingText = forwardRef(
- (props: Omit, ref: Ref) => {
+export type InlineEditingTextProps = Omit;
+
+export const InlineEditingText = forwardRef(
+ (props: InlineEditingTextProps, ref: Ref) => {
return ;
},
);
InlineEditingText.displayName = 'InlineEditing.Text';
-
-export default InlineEditingText;
diff --git a/packages/design-system/src/components/InlineEditing/variations/InlineEditing.textarea.cy.tsx b/packages/design-system/src/components/InlineEditing/variations/InlineEditing.textarea.cy.tsx
index 21f0dacdbf2..feb1c988d35 100644
--- a/packages/design-system/src/components/InlineEditing/variations/InlineEditing.textarea.cy.tsx
+++ b/packages/design-system/src/components/InlineEditing/variations/InlineEditing.textarea.cy.tsx
@@ -1,4 +1,4 @@
-import InlineEditingMulti from './InlineEditing.textarea';
+import { InlineEditingTextarea } from './InlineEditing.textarea';
context(' ', () => {
const defaultProps = {
@@ -7,14 +7,14 @@ context(' ', () => {
};
it('should render with filled value', () => {
- cy.mount( );
+ cy.mount( );
cy.get('[data-testid="inlineediting.button.edit"]').should('exist');
cy.get('p').should('have.text', 'Some text');
});
it('should allow inline editing', () => {
- cy.mount( );
+ cy.mount( );
// Switch to edit mode
cy.get('[data-testid="inlineediting.button.edit"]').click();
@@ -30,7 +30,7 @@ context(' ', () => {
});
it('should allow to have some constraints', () => {
- cy.mount( );
+ cy.mount( );
cy.get('[data-testid="inlineediting.button.edit"]').click();
cy.get('[data-testid="inlineediting.textarea"]').should('have.attr', 'required');
diff --git a/packages/design-system/src/components/InlineEditing/variations/InlineEditing.textarea.tsx b/packages/design-system/src/components/InlineEditing/variations/InlineEditing.textarea.tsx
index f2b5554fa02..2b9f7019582 100644
--- a/packages/design-system/src/components/InlineEditing/variations/InlineEditing.textarea.tsx
+++ b/packages/design-system/src/components/InlineEditing/variations/InlineEditing.textarea.tsx
@@ -3,12 +3,12 @@ import InlineEditingPrimitive, {
InlineEditingPrimitiveProps,
} from '../Primitives/InlineEditingPrimitive';
-const InlineEditingMulti = forwardRef(
- (props: Omit, ref: Ref) => {
+export type InlineEditingTextareaProps = Omit;
+
+export const InlineEditingTextarea = forwardRef(
+ (props: InlineEditingTextareaProps, ref: Ref) => {
return ;
},
);
-InlineEditingMulti.displayName = 'InlineEditing.Multi';
-
-export default InlineEditingMulti;
+InlineEditingTextarea.displayName = 'InlineEditing.Multi';
diff --git a/packages/design-system/src/components/InlineMessage/InlineMessage.test.tsx b/packages/design-system/src/components/InlineMessage/InlineMessage.test.tsx
new file mode 100644
index 00000000000..1c161e14900
--- /dev/null
+++ b/packages/design-system/src/components/InlineMessage/InlineMessage.test.tsx
@@ -0,0 +1,24 @@
+/* eslint-disable import/no-extraneous-dependencies */
+import { describe, it, expect } from '@jest/globals';
+import { axe } from 'jest-axe';
+import { render } from '@testing-library/react';
+import { InlineMessage } from './';
+
+describe('InlineMessage', () => {
+ it('should render a11y html', async () => {
+ const { container } = render(
+
+
+
+
+
+
+ ,
+ );
+ // eslint-disable-next-line testing-library/no-container
+ container.querySelector('button')?.click();
+ expect(container.firstChild).toMatchSnapshot();
+ const results = await axe(document.body);
+ expect(results).toHaveNoViolations();
+ });
+});
diff --git a/packages/design-system/src/components/InlineMessage/InlineMessage.tsx b/packages/design-system/src/components/InlineMessage/InlineMessage.tsx
index 561750b9a3e..33281225b59 100644
--- a/packages/design-system/src/components/InlineMessage/InlineMessage.tsx
+++ b/packages/design-system/src/components/InlineMessage/InlineMessage.tsx
@@ -18,40 +18,40 @@ type Warning = InlineMessageVariantType<'warning', InlineMessageWarningProps>;
type Destructive = InlineMessageVariantType<'destructive', InlineMessageDestructiveProps>;
type Beta = InlineMessageVariantType<'beta', InlineMessageBetaProps>;
-export type InlineMessageType = Information | Success | Warning | Destructive | Beta;
-
-const InlineMessage = forwardRef((props: InlineMessageType, ref: Ref) => {
- switch (props.variant) {
- case 'information': {
- const { variant, ...rest } = props;
- return ;
- }
-
- case 'destructive': {
- const { variant, ...rest } = props;
- return ;
+export type InlineMessagePropTypes = Information | Success | Warning | Destructive | Beta;
+
+export const InlineMessage = forwardRef(
+ (props: InlineMessagePropTypes, ref: Ref) => {
+ switch (props.variant) {
+ case 'information': {
+ const { variant, ...rest } = props;
+ return ;
+ }
+
+ case 'destructive': {
+ const { variant, ...rest } = props;
+ return ;
+ }
+
+ case 'success': {
+ const { variant, ...rest } = props;
+ return ;
+ }
+
+ case 'warning': {
+ const { variant, ...rest } = props;
+ return ;
+ }
+
+ case 'beta': {
+ const { variant, ...rest } = props;
+ return ;
+ }
+
+ default:
+ throw new Error("InlineMessage needs a 'variant' prop");
}
-
- case 'success': {
- const { variant, ...rest } = props;
- return ;
- }
-
- case 'warning': {
- const { variant, ...rest } = props;
- return ;
- }
-
- case 'beta': {
- const { variant, ...rest } = props;
- return ;
- }
-
- default:
- throw new Error("InlineMessage needs a 'variant' prop");
- }
-});
+ },
+);
InlineMessage.displayName = 'InlineMessage';
-
-export default InlineMessage;
diff --git a/packages/design-system/src/components/InlineMessage/Primitive/InlineMessagePrimitive.tsx b/packages/design-system/src/components/InlineMessage/Primitive/InlineMessagePrimitive.tsx
index f7540757b9e..34c70fb2be8 100644
--- a/packages/design-system/src/components/InlineMessage/Primitive/InlineMessagePrimitive.tsx
+++ b/packages/design-system/src/components/InlineMessage/Primitive/InlineMessagePrimitive.tsx
@@ -64,4 +64,6 @@ const InlineMessagePrimitive = forwardRef(
},
);
+InlineMessagePrimitive.displayName = 'InlineMessagePrimitive';
+
export default InlineMessagePrimitive;
diff --git a/packages/design-system/src/components/InlineMessage/__snapshots__/InlineMessage.test.tsx.snap b/packages/design-system/src/components/InlineMessage/__snapshots__/InlineMessage.test.tsx.snap
new file mode 100644
index 00000000000..04f1e99df2a
--- /dev/null
+++ b/packages/design-system/src/components/InlineMessage/__snapshots__/InlineMessage.test.tsx.snap
@@ -0,0 +1,141 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`InlineMessage should render a11y html 1`] = `
+
+
+
+
+
+
+
+
+
+ The information message value
+
+
+
+
+
+
+
+
+
+
+
+ The destructive message value
+
+
+
+
+
+
+
+
+
+
+
+ The success message value
+
+
+
+
+
+
+
+
+
+
+
+ The warning message value
+
+
+
+
+
+
+
+
+
+
+
+ The beta message value
+
+
+
+
+`;
diff --git a/packages/design-system/src/components/InlineMessage/index.ts b/packages/design-system/src/components/InlineMessage/index.ts
index 2690f84edac..45acafd6847 100644
--- a/packages/design-system/src/components/InlineMessage/index.ts
+++ b/packages/design-system/src/components/InlineMessage/index.ts
@@ -1,10 +1,11 @@
-import InlineMessage from './InlineMessage';
+import { InlineMessage, InlineMessagePropTypes } from './InlineMessage';
import InlineMessageInformation from './variations/InlineMessageInformation';
import InlineMessageSuccess from './variations/InlineMessageSuccess';
import InlineMessageWarning from './variations/InlineMessageWarning';
import InlineMessageDestructive from './variations/InlineMessageDestructive';
import InlineMessageBeta from './variations/InlineMessageBeta';
+export type { InlineMessagePropTypes };
export {
InlineMessage,
InlineMessageInformation,
diff --git a/packages/design-system/src/components/Link/Link.cy.tsx b/packages/design-system/src/components/Link/Link.cy.tsx
index c57cb93cd36..c399c4e153a 100644
--- a/packages/design-system/src/components/Link/Link.cy.tsx
+++ b/packages/design-system/src/components/Link/Link.cy.tsx
@@ -1,6 +1,7 @@
/* eslint-disable testing-library/await-async-query */
/* eslint-disable testing-library/prefer-screen-queries */
-import Link from './';
+import { Link } from './';
+
context(' ', () => {
it('should render', () => {
cy.mount(
@@ -12,17 +13,25 @@ context(' ', () => {
});
it('should render icon before', () => {
- cy.mount( );
+ cy.mount(
+
+ Link example
+ ,
+ );
cy.findByTestId('link.icon.before').should('be.visible');
});
it('should render external', () => {
- cy.mount( );
+ cy.mount( Link example);
cy.findByTestId('link.icon.external').should('be.visible');
});
it('should render disabled', () => {
- cy.mount( );
+ cy.mount(
+
+ Link example
+ ,
+ );
cy.findByTestId('my.link').should('have.attr', 'aria-disabled');
});
diff --git a/packages/design-system/src/components/Link/Link.test.tsx b/packages/design-system/src/components/Link/Link.test.tsx
new file mode 100644
index 00000000000..e38a4d51883
--- /dev/null
+++ b/packages/design-system/src/components/Link/Link.test.tsx
@@ -0,0 +1,26 @@
+/* eslint-disable import/no-extraneous-dependencies */
+import { describe, it, expect } from '@jest/globals';
+import { axe } from 'jest-axe';
+import { render } from '@testing-library/react';
+import { Link } from './';
+
+describe('Link', () => {
+ it('should render a11y html', async () => {
+ const { container } = render(
+
+
+ Link example
+
+
+ Link example
+
+ Link example
+ ,
+ );
+ // eslint-disable-next-line testing-library/no-container
+ container.querySelector('button')?.click();
+ expect(container.firstChild).toMatchSnapshot();
+ const results = await axe(document.body);
+ expect(results).toHaveNoViolations();
+ });
+});
diff --git a/packages/design-system/src/components/Link/Link.tsx b/packages/design-system/src/components/Link/Link.tsx
index 3f8692d00be..2bb68c44fea 100644
--- a/packages/design-system/src/components/Link/Link.tsx
+++ b/packages/design-system/src/components/Link/Link.tsx
@@ -3,7 +3,7 @@ import { forwardRef, ReactElement, Ref, useCallback, useMemo } from 'react';
import classNames from 'classnames';
import { useTranslation } from 'react-i18next';
import { DeprecatedIconNames } from '../../types';
-import Linkable, { LinkableType, isBlank as targetCheck } from '../Linkable';
+import { Linkable, LinkableType, isBlank as targetCheck } from '../Linkable';
import style from './Link.module.scss';
import { I18N_DOMAIN_DESIGN_SYSTEM } from '../constants';
diff --git a/packages/design-system/src/components/Link/__snapshots__/Link.test.tsx.snap b/packages/design-system/src/components/Link/__snapshots__/Link.test.tsx.snap
new file mode 100644
index 00000000000..128bd74e73e
--- /dev/null
+++ b/packages/design-system/src/components/Link/__snapshots__/Link.test.tsx.snap
@@ -0,0 +1,69 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`Link should render a11y html 1`] = `
+
+
+
+ Link example
+
+
+
+
+
+
+
+
+
+ Link example
+
+
+
+
+ Link example
+
+
+
+
+
+
+
+
+`;
diff --git a/packages/design-system/src/components/Link/index.ts b/packages/design-system/src/components/Link/index.ts
index cf5136801b2..6e93ba12193 100644
--- a/packages/design-system/src/components/Link/index.ts
+++ b/packages/design-system/src/components/Link/index.ts
@@ -2,4 +2,3 @@ import Link, { LinkProps, LinkComponentProps } from './Link';
export type { LinkProps, LinkComponentProps };
export { Link };
-export default Link;
diff --git a/packages/design-system/src/components/LinkAsButton/LinkAsButton.test.tsx b/packages/design-system/src/components/LinkAsButton/LinkAsButton.test.tsx
new file mode 100644
index 00000000000..b7eebaae409
--- /dev/null
+++ b/packages/design-system/src/components/LinkAsButton/LinkAsButton.test.tsx
@@ -0,0 +1,22 @@
+/* eslint-disable import/no-extraneous-dependencies */
+import { describe, it, expect } from '@jest/globals';
+import { axe } from 'jest-axe';
+import { render } from '@testing-library/react';
+import { LinkAsButton } from './';
+
+describe('LinkAsButton', () => {
+ it('should render a11y html', async () => {
+ const { container } = render(
+
+ Link example
+ Link example
+ Link example
+ ,
+ );
+ // eslint-disable-next-line testing-library/no-container
+ container.querySelector('button')?.click();
+ expect(container.firstChild).toMatchSnapshot();
+ const results = await axe(document.body);
+ expect(results).toHaveNoViolations();
+ });
+});
diff --git a/packages/design-system/src/components/LinkAsButton/LinkAsButton.tsx b/packages/design-system/src/components/LinkAsButton/LinkAsButton.tsx
index e969275c10d..efc07cfdb19 100644
--- a/packages/design-system/src/components/LinkAsButton/LinkAsButton.tsx
+++ b/packages/design-system/src/components/LinkAsButton/LinkAsButton.tsx
@@ -1,7 +1,7 @@
import { cloneElement, forwardRef, Ref } from 'react';
import classnames from 'classnames';
import { useTranslation } from 'react-i18next';
-import Clickable, { ClickableProps } from '../Clickable';
+import { Clickable, ClickableProps } from '../Clickable';
import { Icon } from '../Icon/Icon';
import { LinkComponentProps } from '../Link';
@@ -10,10 +10,10 @@ import linkStyles from '../Link/Link.module.scss';
import { I18N_DOMAIN_DESIGN_SYSTEM } from '../constants';
import { SizedIcon } from '../Icon';
-type LinkAsButtonProps = Omit &
+export type LinkAsButtonProps = Omit &
Omit & { openInNewTab?: boolean };
-const LinkAsButton = forwardRef(
+export const LinkAsButton = forwardRef(
(
{ disabled, title, icon, children, openInNewTab, ...rest }: LinkAsButtonProps,
ref: Ref,
@@ -85,5 +85,3 @@ const LinkAsButton = forwardRef(
);
LinkAsButton.displayName = 'LinkAsButton';
-
-export default LinkAsButton;
diff --git a/packages/design-system/src/components/LinkAsButton/__snapshots__/LinkAsButton.test.tsx.snap b/packages/design-system/src/components/LinkAsButton/__snapshots__/LinkAsButton.test.tsx.snap
new file mode 100644
index 00000000000..43f9a93d4a5
--- /dev/null
+++ b/packages/design-system/src/components/LinkAsButton/__snapshots__/LinkAsButton.test.tsx.snap
@@ -0,0 +1,64 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`LinkAsButton should render a11y html 1`] = `
+
+
+
+ Link example
+
+
+
+
+
+ Link example
+
+
+
+
+ Link example
+
+
+
+
+
+
+
+
+`;
diff --git a/packages/design-system/src/components/LinkAsButton/index.ts b/packages/design-system/src/components/LinkAsButton/index.ts
index 2b66a8829d7..a385ce1412a 100644
--- a/packages/design-system/src/components/LinkAsButton/index.ts
+++ b/packages/design-system/src/components/LinkAsButton/index.ts
@@ -1,3 +1 @@
-import LinkAsButton from './LinkAsButton';
-
-export { LinkAsButton };
+export * from './LinkAsButton';
diff --git a/packages/design-system/src/components/Linkable/Linkable.test.tsx b/packages/design-system/src/components/Linkable/Linkable.test.tsx
new file mode 100644
index 00000000000..ca3e2a41930
--- /dev/null
+++ b/packages/design-system/src/components/Linkable/Linkable.test.tsx
@@ -0,0 +1,26 @@
+/* eslint-disable import/no-extraneous-dependencies */
+import { describe, it, expect } from '@jest/globals';
+import { axe } from 'jest-axe';
+import { render } from '@testing-library/react';
+import { Linkable } from './';
+
+describe('Linkable', () => {
+ it('should render a11y html', async () => {
+ const { container } = render(
+
+
+ Linkable example
+
+
+ Linkable example
+
+ Linkable example
+ ,
+ );
+ // eslint-disable-next-line testing-library/no-container
+ container.querySelector('button')?.click();
+ expect(container.firstChild).toMatchSnapshot();
+ const results = await axe(document.body);
+ expect(results).toHaveNoViolations();
+ });
+});
diff --git a/packages/design-system/src/components/Linkable/Linkable.tsx b/packages/design-system/src/components/Linkable/Linkable.tsx
index 5f9ea9ee317..c504341a944 100644
--- a/packages/design-system/src/components/Linkable/Linkable.tsx
+++ b/packages/design-system/src/components/Linkable/Linkable.tsx
@@ -29,7 +29,7 @@ export function isBlank(target: string | undefined): boolean {
return !!target && !['_self', '_parent', '_top'].includes(target.toLowerCase());
}
-const Linkable = forwardRef(
+export const Linkable = forwardRef(
(
{
as = 'a',
@@ -127,4 +127,3 @@ const Linkable = forwardRef(
);
Linkable.displayName = 'Linkable';
-export default Linkable;
diff --git a/packages/design-system/src/components/Linkable/__snapshots__/Linkable.test.tsx.snap b/packages/design-system/src/components/Linkable/__snapshots__/Linkable.test.tsx.snap
new file mode 100644
index 00000000000..384fdf7b668
--- /dev/null
+++ b/packages/design-system/src/components/Linkable/__snapshots__/Linkable.test.tsx.snap
@@ -0,0 +1,57 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`Linkable should render a11y html 1`] = `
+
+
+ Linkable example
+
+
+
+
+
+
+
+ Linkable example
+
+
+ Linkable example
+
+
+
+
+
+
+
+`;
diff --git a/packages/design-system/src/components/Linkable/index.ts b/packages/design-system/src/components/Linkable/index.ts
index 9cbfb5498fe..45dba29023e 100644
--- a/packages/design-system/src/components/Linkable/index.ts
+++ b/packages/design-system/src/components/Linkable/index.ts
@@ -1,5 +1 @@
-import Linkable, { LinkableType, isBlank } from './Linkable';
-
-export type { LinkableType };
-export { isBlank };
-export default Linkable;
+export * from './Linkable';
diff --git a/packages/design-system/src/components/Loading/Loading.test.tsx b/packages/design-system/src/components/Loading/Loading.test.tsx
new file mode 100644
index 00000000000..caea9aeb2f0
--- /dev/null
+++ b/packages/design-system/src/components/Loading/Loading.test.tsx
@@ -0,0 +1,20 @@
+/* eslint-disable import/no-extraneous-dependencies */
+import { describe, it, expect } from '@jest/globals';
+import { axe } from 'jest-axe';
+import { render } from '@testing-library/react';
+import { Loading } from './';
+
+describe('Loading', () => {
+ it('should render a11y html', async () => {
+ const { container } = render(
+
+
+ ,
+ );
+ // eslint-disable-next-line testing-library/no-container
+ container.querySelector('button')?.click();
+ expect(container.firstChild).toMatchSnapshot();
+ const results = await axe(document.body);
+ expect(results).toHaveNoViolations();
+ });
+});
diff --git a/packages/design-system/src/components/Loading/Loading.tsx b/packages/design-system/src/components/Loading/Loading.tsx
index 4923acdae3a..d1c83dfa84b 100644
--- a/packages/design-system/src/components/Loading/Loading.tsx
+++ b/packages/design-system/src/components/Loading/Loading.tsx
@@ -1,6 +1,8 @@
-import { forwardRef } from 'react';
+import { HTMLAttributes, forwardRef } from 'react';
-const Loading = forwardRef>((props, ref) => (
+export type LoadingProps = HTMLAttributes;
+
+export const Loading = forwardRef((props, ref) => (
>((pr
));
-export default Loading;
+Loading.displayName = 'Loading';
diff --git a/packages/design-system/src/components/Loading/__snapshots__/Loading.test.tsx.snap b/packages/design-system/src/components/Loading/__snapshots__/Loading.test.tsx.snap
new file mode 100644
index 00000000000..ab4ae3c36fb
--- /dev/null
+++ b/packages/design-system/src/components/Loading/__snapshots__/Loading.test.tsx.snap
@@ -0,0 +1,31 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`Loading should render a11y html 1`] = `
+
+
+
+
+
+
+
+
+
+
+`;
diff --git a/packages/design-system/src/components/Loading/index.ts b/packages/design-system/src/components/Loading/index.ts
index a57e39771eb..bc5115b2f8e 100644
--- a/packages/design-system/src/components/Loading/index.ts
+++ b/packages/design-system/src/components/Loading/index.ts
@@ -1,3 +1 @@
-import Loading from './Loading';
-
-export default Loading;
+export * from './Loading';
diff --git a/packages/design-system/src/components/Message/Message.test.tsx b/packages/design-system/src/components/Message/Message.test.tsx
new file mode 100644
index 00000000000..13eccadb08f
--- /dev/null
+++ b/packages/design-system/src/components/Message/Message.test.tsx
@@ -0,0 +1,51 @@
+/* eslint-disable import/no-extraneous-dependencies */
+import { describe, it, expect } from '@jest/globals';
+import { axe } from 'jest-axe';
+import { render } from '@testing-library/react';
+import { MessageSuccess, MessageDestructive, MessageWarning, MessageInformation } from './';
+
+describe('Message', () => {
+ it('should render a11y html', async () => {
+ const { container } = render(
+
+
+ Success
+
+
+ Destructive
+
+
+ Warning
+
+
+ Information
+
+ ,
+ );
+ // eslint-disable-next-line testing-library/no-container
+ container.querySelector('button')?.click();
+ expect(container.firstChild).toMatchSnapshot();
+ const results = await axe(document.body);
+ expect(results).toHaveNoViolations();
+ });
+});
diff --git a/packages/design-system/src/components/Message/Primitive/MessagePrimitive.tsx b/packages/design-system/src/components/Message/Primitive/MessagePrimitive.tsx
index 25635c65605..68802033632 100644
--- a/packages/design-system/src/components/Message/Primitive/MessagePrimitive.tsx
+++ b/packages/design-system/src/components/Message/Primitive/MessagePrimitive.tsx
@@ -11,9 +11,8 @@ import Link, { LinkProps } from '../../Link/Link';
import { StackHorizontal, StackVertical } from '../../Stack';
import { ButtonTertiaryPropsType } from '../../Button/variations/ButtonTertiary';
import { ButtonTertiary } from '../../Button';
-import Dropdown from '../../Dropdown';
+import { Dropdown, DropdownPropsType } from '../../Dropdown';
import { ButtonIcon } from '../../ButtonIcon';
-import { DropdownPropsType } from '../../Dropdown/Dropdown';
import { I18N_DOMAIN_DESIGN_SYSTEM } from '../../constants';
import styles from './MessageStyles.module.scss';
diff --git a/packages/design-system/src/components/Message/__snapshots__/Message.test.tsx.snap b/packages/design-system/src/components/Message/__snapshots__/Message.test.tsx.snap
new file mode 100644
index 00000000000..37779e46381
--- /dev/null
+++ b/packages/design-system/src/components/Message/__snapshots__/Message.test.tsx.snap
@@ -0,0 +1,270 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`Message should render a11y html 1`] = `
+
+
+
+
+
+
+ Something went wrong
+
+
+
+ There is an issue with the component configuration
+
+
+
+ Learn more
+
+
+
+
+
+
+
+ Destructive
+
+
+
+ See
+
+
+
+
+
+
+
+
+`;
diff --git a/packages/design-system/src/components/Modal/Modal.cy.tsx b/packages/design-system/src/components/Modal/Modal.cy.tsx
index c53aa5c1928..94068660160 100644
--- a/packages/design-system/src/components/Modal/Modal.cy.tsx
+++ b/packages/design-system/src/components/Modal/Modal.cy.tsx
@@ -4,7 +4,7 @@
/* eslint-disable testing-library/prefer-screen-queries */
import { useState } from 'react';
import { ButtonPrimary } from '../Button';
-import Modal, { ModalPropsType } from './Modal';
+import { Modal, ModalPropsType } from './Modal';
function ModalStory(props: Partial) {
const [modalOpen, setModalOpen] = useState(false);
@@ -63,12 +63,8 @@ context(' ', () => {
,
);
cy.findByTestId('open-modal').click();
- cy.findByTestId('modal.buttons.close')
- .click()
- .then(() => {
- // then
- cy.findByTestId('modal').should('not.exist');
- });
+ cy.findByTestId('modal.buttons.close').click();
+ cy.findByTestId('modal').should('not.exist');
});
it('should not have cancel/close action when preventEscaping is passed', () => {
@@ -90,12 +86,8 @@ context(' ', () => {
,
);
cy.findByTestId('open-modal').click();
- cy.findByTestId('modal')
- .type('{esc}')
- .then(() => {
- // then
- cy.findByTestId('modal').should('not.exist');
- });
+ cy.findByTestId('modal').type('{esc}');
+ cy.findByTestId('modal').should('not.exist');
});
it('should not close the modal on ESC key', () => {
@@ -108,11 +100,7 @@ context(' ', () => {
,
);
cy.findByTestId('open-modal').click();
- cy.findByTestId('modal')
- .type('{esc}')
- .then(() => {
- // then
- cy.findByTestId('modal').should('exist');
- });
+ cy.findByTestId('modal').type('{esc}');
+ cy.findByTestId('modal').should('exist');
});
});
diff --git a/packages/design-system/src/components/Modal/Modal.test.tsx b/packages/design-system/src/components/Modal/Modal.test.tsx
new file mode 100644
index 00000000000..34a083990b9
--- /dev/null
+++ b/packages/design-system/src/components/Modal/Modal.test.tsx
@@ -0,0 +1,21 @@
+/* eslint-disable import/no-extraneous-dependencies */
+import { describe, it, expect } from '@jest/globals';
+import { axe } from 'jest-axe';
+import { render } from '@testing-library/react';
+import { Modal } from './';
+
+describe('Message', () => {
+ xit('should render a11y html', async () => {
+ const { container } = render(
+
+ jest.fn()}>
+ Content
+
+ ,
+ );
+ // eslint-disable-next-line testing-library/no-container
+ expect(container.firstChild).toMatchSnapshot();
+ const results = await axe(document.body);
+ expect(results).toHaveNoViolations();
+ });
+});
diff --git a/packages/design-system/src/components/Modal/Modal.tsx b/packages/design-system/src/components/Modal/Modal.tsx
index 55380ff0810..9e0455fbc3b 100644
--- a/packages/design-system/src/components/Modal/Modal.tsx
+++ b/packages/design-system/src/components/Modal/Modal.tsx
@@ -1,14 +1,17 @@
-import { cloneElement, HTMLAttributes, ReactElement, ReactNode, useEffect, useRef } from 'react';
+import { useEffect, useRef, cloneElement } from 'react';
+import type { ReactNode, ReactElement, MouseEvent as ReactMouseEvent } from 'react';
import { useTranslation } from 'react-i18next';
-import { Dialog, DialogBackdrop, DialogDisclosure, useDialogState } from 'reakit/Dialog';
import { DeprecatedIconNames } from '../../types';
import { ButtonDestructive, ButtonPrimary, ButtonSecondary } from '../Button';
-import { Icon } from '../Icon';
-import { StackHorizontal, StackVertical } from '../Stack';
+import { ButtonDestructivePropsType } from '../Button/variations/ButtonDestructive';
import { ButtonPrimaryPropsType } from '../Button/variations/ButtonPrimary';
import { ButtonSecondaryPropsType } from '../Button/variations/ButtonSecondary';
-import { ButtonDestructivePropsType } from '../Button/variations/ButtonDestructive';
+import { Disclosure } from '../Disclosure/Disclosure';
+import { Icon } from '../Icon';
+import { StackHorizontal, StackVertical } from '../Stack';
+import { Dialog, DialogPropsType, useDialogState } from './Primitives/Dialog';
+import { DialogBackdrop } from './Primitives/DialogBackdrop';
import styles from './Modal.module.scss';
@@ -39,7 +42,7 @@ export type ModalPropsType = {
secondaryAction?: ButtonSecondaryPropsType<'M'>;
preventEscaping?: boolean;
children: ReactNode | ReactNode[];
-} & Omit, 'className' | 'style'>;
+} & DialogPropsType;
function PrimaryAction(props: PrimaryActionPropsType) {
if (!('destructive' in props) || !props.destructive) {
@@ -65,114 +68,123 @@ export function Modal(props: ModalPropsType): ReactElement {
const hasDisclosure = 'disclosure' in props;
const { t } = useTranslation('design-system');
const dialog = useDialogState({ visible: !hasDisclosure });
- const ref = useRef(null);
+
+ const backdropRef = useRef(null);
+ const dialogRef = useRef(null);
useEffect(() => {
- (ref.current as unknown as HTMLElement)?.focus();
- }, [dialog.visible]);
+ dialogRef.current?.focus();
+ }, [dialogRef]);
- const onCloseHandler = hasDisclosure
- ? () => dialog.setVisible(false)
- : () => onClose && onClose();
+ const onCloseHandler = hasDisclosure ? () => dialog.hide() : () => onClose && onClose();
+
+ const onClickBackdropHandler = (event: ReactMouseEvent) => {
+ if (event.target === backdropRef.current) {
+ onCloseHandler();
+ }
+ };
return (
<>
{disclosure && (
-
- {disclosureProps => cloneElement(disclosure, disclosureProps)}
-
+
+ {disclosureProps =>
+ cloneElement(disclosure, { ...disclosureProps, onClick: dialog.show })
+ }
+
)}
- {dialog.visible && (
-
+
-
-
onCloseHandler()}
- ref={ref}
- >
-
-
- {header.icon && (
-
- )}
-
+
undefined : () => onCloseHandler()}
+ ref={dialogRef}
+ >
+
+
+ {header.icon && (
+
+ )}
+
+
+ {header.title}
+
+ {header.description && (
- {header.title}
+ {header.description}
- {header.description && (
-
- {header.description}
-
- )}
-
+ )}
+
-
- {children}
-
+
+ {children}
+
-
-
- {!preventEscaping && (
-
- onCloseHandler()}
- data-test="modal.buttons.close"
- data-testid="modal.buttons.close"
- data-feature="modal.buttons.close"
- >
- {primaryAction || secondaryAction
- ? t('CANCEL', 'Cancel')
- : t('CLOSE', 'Close')}
-
-
- )}
-
- {secondaryAction && (
+
+
+ {!preventEscaping && (
+
- )}
-
- {primaryAction && (
-
- )}
-
-
-
-
-
-
- )}
+ onClick={() => onCloseHandler()}
+ data-test="modal.buttons.close"
+ data-testid="modal.buttons.close"
+ data-feature="modal.buttons.close"
+ >
+ {primaryAction || secondaryAction
+ ? t('CANCEL', 'Cancel')
+ : t('CLOSE', 'Close')}
+
+
+ )}
+
+ {secondaryAction && (
+
+ )}
+
+ {primaryAction && (
+
+ )}
+
+
+
+
+
+
>
);
}
-
-export default Modal;
diff --git a/packages/design-system/src/components/Modal/Primitives/Dialog.tsx b/packages/design-system/src/components/Modal/Primitives/Dialog.tsx
new file mode 100644
index 00000000000..48a35449d74
--- /dev/null
+++ b/packages/design-system/src/components/Modal/Primitives/Dialog.tsx
@@ -0,0 +1,51 @@
+/* eslint-disable jsx-a11y/no-noninteractive-element-interactions */
+import { useState, useCallback, forwardRef } from 'react';
+import type { Ref, KeyboardEvent } from 'react';
+import { DialogState, DialogAction } from './DialogState';
+
+type A11yDialogPropsType = {
+ onKeyDown?: (event: KeyboardEvent
) => void;
+};
+
+export type DialogPropsType = React.HTMLAttributes & {
+ 'aria-modal'?: boolean | 'true' | 'false';
+} & Partial &
+ Partial &
+ A11yDialogPropsType;
+
+function BaseDialog(props: DialogPropsType, ref: Ref) {
+ const { hide, visible, ...rest } = props;
+ if (!visible) {
+ return null;
+ }
+ const onKeyDown = (event: KeyboardEvent) => {
+ if (hide && event.key === 'Escape') {
+ hide();
+ }
+ if (props.onKeyDown) {
+ props.onKeyDown(event);
+ }
+ };
+ return (
+
+ );
+}
+
+export const Dialog = forwardRef(BaseDialog);
+Dialog.displayName = 'Dialog';
+
+export function useDialogState(opts: Partial): DialogState & DialogAction {
+ const [visible, setVisible] = useState(opts.visible || false);
+ const show = useCallback(() => setVisible(true), []);
+ const hide = useCallback(() => setVisible(false), []);
+ const toggle = useCallback(() => setVisible(v => !v), []);
+ return { visible, show, hide, toggle };
+}
diff --git a/packages/design-system/src/components/Modal/Primitives/DialogBackdrop.tsx b/packages/design-system/src/components/Modal/Primitives/DialogBackdrop.tsx
new file mode 100644
index 00000000000..2bd3afff10a
--- /dev/null
+++ b/packages/design-system/src/components/Modal/Primitives/DialogBackdrop.tsx
@@ -0,0 +1,16 @@
+import { DialogState } from './DialogState';
+import { Portal } from './Portal';
+
+export type DialogBackdropProps = React.HTMLAttributes & DialogState;
+
+export function DialogBackdrop(props: DialogBackdropProps) {
+ const { children, visible, ...rest } = props;
+ if (!visible) {
+ return null;
+ }
+ return (
+
+ {children}
;
+
+ );
+}
diff --git a/packages/design-system/src/components/Modal/Primitives/DialogState.ts b/packages/design-system/src/components/Modal/Primitives/DialogState.ts
new file mode 100644
index 00000000000..d7c40f79396
--- /dev/null
+++ b/packages/design-system/src/components/Modal/Primitives/DialogState.ts
@@ -0,0 +1,15 @@
+export type DialogState = {
+ visible: boolean;
+};
+
+export type DialogAction = {
+ show: () => void;
+ /**
+ * Changes the `visible` state to `false`
+ */
+ hide: () => void;
+ /**
+ * Toggles the `visible` state
+ */
+ toggle: () => void;
+};
diff --git a/packages/design-system/src/components/Modal/Primitives/Portal.tsx b/packages/design-system/src/components/Modal/Primitives/Portal.tsx
new file mode 100644
index 00000000000..29cb96f72fa
--- /dev/null
+++ b/packages/design-system/src/components/Modal/Primitives/Portal.tsx
@@ -0,0 +1,19 @@
+import { useState, useEffect, ReactNode } from 'react';
+import { createPortal } from 'react-dom';
+
+type PortalProps = {
+ children: ReactNode;
+};
+
+export function Portal({ children }: PortalProps) {
+ const [el] = useState(document.createElement('div'));
+
+ useEffect(() => {
+ document.body.appendChild(el);
+ return () => {
+ document.body.removeChild(el);
+ };
+ }, [el]);
+
+ return createPortal(children, el);
+}
diff --git a/packages/design-system/src/components/Modal/__snapshots__/Modal.test.tsx.snap b/packages/design-system/src/components/Modal/__snapshots__/Modal.test.tsx.snap
new file mode 100644
index 00000000000..73a7d8ac012
--- /dev/null
+++ b/packages/design-system/src/components/Modal/__snapshots__/Modal.test.tsx.snap
@@ -0,0 +1,3 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`Message should render a11y html 1`] = ` `;
diff --git a/packages/design-system/src/components/WIP/Popover/Popover.cy.tsx b/packages/design-system/src/components/Popover/Popover.cy.tsx
similarity index 81%
rename from packages/design-system/src/components/WIP/Popover/Popover.cy.tsx
rename to packages/design-system/src/components/Popover/Popover.cy.tsx
index 85e78b4b0a4..a56f8d2cc2c 100644
--- a/packages/design-system/src/components/WIP/Popover/Popover.cy.tsx
+++ b/packages/design-system/src/components/Popover/Popover.cy.tsx
@@ -1,16 +1,15 @@
/* eslint-disable testing-library/prefer-screen-queries */
+
/* eslint-disable testing-library/await-async-query */
-import Popover from './Popover';
-import { ButtonPrimary } from '../../Button';
-import { CollapsiblePanel } from '../Accordion';
+import { Popover, ButtonPrimary, CollapsiblePanel } from '../../';
context(' ', () => {
describe('default', () => {
it('should show a popover', () => {
cy.mount(
Open popover}
data-testid="my.popover"
+ disclosure={Open popover }
>
Popover content
,
@@ -22,19 +21,19 @@ context(' ', () => {
cy.findByTestId('my.popover').should('be.visible');
});
- it('should be able to override disclosure click', () => {
+ it('should prevent default', () => {
cy.mount(
event.stopPropagation()} data-testid="my.button">
+ {}} data-testid="my.button">
Open popover
}
- data-testid="my.popover"
>
Popover content
,
diff --git a/packages/design-system/src/components/Popover/Popover.module.scss b/packages/design-system/src/components/Popover/Popover.module.scss
new file mode 100644
index 00000000000..88344927157
--- /dev/null
+++ b/packages/design-system/src/components/Popover/Popover.module.scss
@@ -0,0 +1,13 @@
+@use '~@talend/design-tokens/lib/tokens';
+
+.popover {
+ background-color: tokens.$coral-color-neutral-background;
+ transition: opacity tokens.$coral-transition-fast;
+ box-shadow: tokens.$coral-elevation-shadow-neutral-m;
+ border-radius: tokens.$coral-radius-s;
+ opacity: 1;
+}
+
+.withPadding {
+ padding: tokens.$coral-spacing-m;
+}
diff --git a/packages/design-system/src/components/Popover/Popover.tsx b/packages/design-system/src/components/Popover/Popover.tsx
new file mode 100644
index 00000000000..d99117659ad
--- /dev/null
+++ b/packages/design-system/src/components/Popover/Popover.tsx
@@ -0,0 +1,79 @@
+import { useRef, Fragment } from 'react';
+import type { ReactNode, MouseEvent } from 'react';
+
+import { Placement, FloatingArrow, FloatingPortal } from '@floating-ui/react';
+import classNames from 'classnames';
+
+import tokens from '@talend/design-tokens';
+
+import { renderOrClone, ChildOrGenerator } from '../../renderOrClone';
+import { usePopover } from './usePopover';
+
+import theme from './Popover.module.scss';
+
+type PopoverOptions = {
+ initialOpen?: boolean;
+ placement?: Placement;
+ modal?: boolean;
+ open?: boolean;
+ isFixed?: boolean;
+ onOpenChange?: (open: boolean) => void;
+ hasPadding?: boolean;
+};
+
+export type PopoverProps = {
+ disclosure: ChildOrGenerator;
+ children: ReactNode | ((props: any) => ReactNode);
+} & PopoverOptions;
+
+export type PopoverStateReturn = {
+ hide: () => void;
+};
+
+export function Popover({
+ children,
+ modal = true,
+ isFixed = false,
+ disclosure,
+ hasPadding = true,
+ ...restOptions
+}: PopoverProps) {
+ // This can accept any props as options, e.g. `placement`,
+ // or other positioning options.
+ const arrowRef = useRef(null);
+ const popover = usePopover({ modal, arrowRef, ...restOptions });
+
+ const Wrapper = isFixed ? FloatingPortal : Fragment;
+ const onClick = (e: MouseEvent) => {
+ e.preventDefault();
+ e.stopPropagation();
+ };
+ const childrenProps = popover.getReferenceProps({ onClick });
+
+ return (
+ <>
+ {renderOrClone(disclosure, { ...childrenProps, ref: popover.refs.setReference })}
+
+
+
+ {typeof children === 'function'
+ ? children({ ...popover.getFloatingProps(), setOpen: popover.setOpen })
+ : children}
+
+
+ >
+ );
+}
diff --git a/packages/design-system/src/components/Popover/index.ts b/packages/design-system/src/components/Popover/index.ts
new file mode 100644
index 00000000000..73a707e14bb
--- /dev/null
+++ b/packages/design-system/src/components/Popover/index.ts
@@ -0,0 +1,2 @@
+export * from './Popover';
+export * from './usePopover';
diff --git a/packages/design-system/src/components/Popover/usePopover.tsx b/packages/design-system/src/components/Popover/usePopover.tsx
new file mode 100644
index 00000000000..db596ea3cbf
--- /dev/null
+++ b/packages/design-system/src/components/Popover/usePopover.tsx
@@ -0,0 +1,113 @@
+import { useState, useMemo } from 'react';
+import type { MutableRefObject } from 'react';
+import {
+ useFloating,
+ arrow,
+ autoUpdate,
+ offset,
+ flip,
+ shift,
+ useClick,
+ useDismiss,
+ useRole,
+ useInteractions,
+ Placement,
+} from '@floating-ui/react';
+
+const ARROW_HEIGHT = 7;
+const GAP = 2;
+
+type PopoverOptions = {
+ initialOpen?: boolean;
+ arrowRef?: MutableRefObject;
+ placement?: Placement;
+ modal?: boolean;
+ open?: boolean;
+ onOpenChange?: (open: boolean) => void;
+};
+
+export type PopoverTriggerProps = {
+ onClick?: (event: any) => void;
+ ref: any;
+};
+
+export type UsePopoverType = {
+ // floating-ui types
+ refs: any;
+ getFloatingProps: (props?: any) => any;
+ floatingStyles: any;
+ context: any;
+ getReferenceProps: (props?: any) => any;
+ // local state
+ open: boolean;
+ setOpen: (open: boolean) => void;
+ modal?: boolean;
+ labelId?: string;
+ descriptionId?: string;
+ setLabelId: (id: string) => void;
+ setDescriptionId: (id: string) => void;
+};
+
+export function usePopover({
+ initialOpen = false,
+ placement = 'bottom',
+ arrowRef,
+ modal,
+ open: controlledOpen,
+ onOpenChange: setControlledOpen,
+}: PopoverOptions = {}): UsePopoverType {
+ const [uncontrolledOpen, setUncontrolledOpen] = useState(initialOpen);
+ const [labelId, setLabelId] = useState();
+ const [descriptionId, setDescriptionId] = useState();
+
+ const open = controlledOpen ?? uncontrolledOpen;
+ const setOpen = setControlledOpen ?? setUncontrolledOpen;
+ const middleware = [
+ offset(ARROW_HEIGHT + GAP),
+ flip({
+ crossAxis: placement.includes('-'),
+ fallbackAxisSideDirection: 'end',
+ padding: 0,
+ }),
+ shift({ padding: 0 }),
+ ];
+ if (arrowRef && arrowRef.current) {
+ middleware.push(
+ arrow({
+ element: arrowRef,
+ }),
+ );
+ }
+ const data = useFloating({
+ placement,
+ open,
+ onOpenChange: setOpen,
+ whileElementsMounted: autoUpdate,
+ middleware,
+ });
+
+ const context = data.context;
+
+ const click = useClick(context, {
+ enabled: controlledOpen == null,
+ });
+ const dismiss = useDismiss(context);
+ const role = useRole(context);
+
+ const interactions = useInteractions([click, dismiss, role]);
+
+ return useMemo(
+ () => ({
+ open,
+ setOpen,
+ ...interactions,
+ ...data,
+ modal,
+ labelId,
+ descriptionId,
+ setLabelId,
+ setDescriptionId,
+ }),
+ [open, setOpen, interactions, data, modal, labelId, descriptionId],
+ );
+}
diff --git a/packages/design-system/src/components/RichRadioButton/RichRadioButton.component.tsx b/packages/design-system/src/components/RichRadioButton/RichRadioButton.component.tsx
index da0b1894d78..159bbaebc73 100644
--- a/packages/design-system/src/components/RichRadioButton/RichRadioButton.component.tsx
+++ b/packages/design-system/src/components/RichRadioButton/RichRadioButton.component.tsx
@@ -36,8 +36,8 @@ function RichRadioButtonIcon({ asset }: { asset?: LogoAsset | IllustrationAsset
return null;
}
-
-const RichRadioButton = ({
+export type { RichRadioButtonProps };
+export const RichRadioButton = ({
dataFeature,
description,
asset,
diff --git a/packages/design-system/src/components/RichRadioButton/index.ts b/packages/design-system/src/components/RichRadioButton/index.ts
index 7607a73dcc4..f9b48830d0e 100644
--- a/packages/design-system/src/components/RichRadioButton/index.ts
+++ b/packages/design-system/src/components/RichRadioButton/index.ts
@@ -1,3 +1 @@
-import RichRadioButton from './RichRadioButton.component';
-
-export default RichRadioButton;
+export * from './RichRadioButton.component';
diff --git a/packages/design-system/src/components/Skeleton/Primitive/Skeleton.Primitive.tsx b/packages/design-system/src/components/Skeleton/Primitive/Skeleton.Primitive.tsx
index 2a60505c6fc..c9b5d48aa4b 100644
--- a/packages/design-system/src/components/Skeleton/Primitive/Skeleton.Primitive.tsx
+++ b/packages/design-system/src/components/Skeleton/Primitive/Skeleton.Primitive.tsx
@@ -19,5 +19,5 @@ const SkeletonPrimitive = forwardRef(
);
},
);
-
+SkeletonPrimitive.displayName = 'SkeletonPrimitive';
export default SkeletonPrimitive;
diff --git a/packages/design-system/src/components/Skeleton/Skeleton.tsx b/packages/design-system/src/components/Skeleton/Skeleton.tsx
index ee0dd239bbe..00d755017f8 100644
--- a/packages/design-system/src/components/Skeleton/Skeleton.tsx
+++ b/packages/design-system/src/components/Skeleton/Skeleton.tsx
@@ -6,14 +6,14 @@ import SkeletonHeading, { SkeletonHeadingProps } from './variations/SkeletonHead
import SkeletonInput, { SkeletonInputProps } from './variations/SkeletonInput';
import SkeletonParagraph, { SkeletonParagraphProps } from './variations/SkeletonParagraph';
-type SkeletonProps =
+export type SkeletonProps =
| ({ variant: 'button' } & SkeletonButtonProps)
| ({ variant: 'buttonIcon' } & SkeletonButtonIconProps)
| ({ variant: 'heading' } & SkeletonHeadingProps)
| ({ variant: 'paragraph' } & SkeletonParagraphProps)
| ({ variant: 'input' } & SkeletonInputProps);
-const Skeleton = forwardRef((props: SkeletonProps, ref: Ref) => {
+export const Skeleton = forwardRef((props: SkeletonProps, ref: Ref) => {
switch (props.variant) {
case 'button': {
const { variant, ...rest } = props;
@@ -45,5 +45,4 @@ const Skeleton = forwardRef((props: SkeletonProps, ref: Ref) =>
}
}
});
-
-export default Skeleton;
+Skeleton.displayName = 'Skeleton';
diff --git a/packages/design-system/src/components/Skeleton/index.ts b/packages/design-system/src/components/Skeleton/index.ts
index 1f14c389d36..29dafed2d48 100644
--- a/packages/design-system/src/components/Skeleton/index.ts
+++ b/packages/design-system/src/components/Skeleton/index.ts
@@ -1,9 +1,8 @@
-import Skeleton from './Skeleton';
+export * from './Skeleton';
import SkeletonButton from './variations/SkeletonButton';
import SkeletonButtonIcon from './variations/SkeletonButtonIcon';
import SkeletonHeading from './variations/SkeletonHeading';
import SkeletonInput from './variations/SkeletonInput';
import SkeletonParagraph from './variations/SkeletonParagraph';
-export default Skeleton;
export { SkeletonHeading, SkeletonButtonIcon, SkeletonButton, SkeletonParagraph, SkeletonInput };
diff --git a/packages/design-system/src/components/Skeleton/variations/SkeletonButton.tsx b/packages/design-system/src/components/Skeleton/variations/SkeletonButton.tsx
index 26c08ea6483..98f7891933a 100644
--- a/packages/design-system/src/components/Skeleton/variations/SkeletonButton.tsx
+++ b/packages/design-system/src/components/Skeleton/variations/SkeletonButton.tsx
@@ -24,5 +24,5 @@ const SkeletonButton = forwardRef(
);
},
);
-
+SkeletonButton.displayName = 'SkeletonButton';
export default SkeletonButton;
diff --git a/packages/design-system/src/components/Skeleton/variations/SkeletonButtonIcon.tsx b/packages/design-system/src/components/Skeleton/variations/SkeletonButtonIcon.tsx
index 67827b34fe1..aec03dc29c7 100644
--- a/packages/design-system/src/components/Skeleton/variations/SkeletonButtonIcon.tsx
+++ b/packages/design-system/src/components/Skeleton/variations/SkeletonButtonIcon.tsx
@@ -26,5 +26,5 @@ const SkeletonButtonIcon = forwardRef(
);
},
);
-
+SkeletonButtonIcon.displayName = 'SkeletonButtonIcon';
export default SkeletonButtonIcon;
diff --git a/packages/design-system/src/components/Skeleton/variations/SkeletonHeading.tsx b/packages/design-system/src/components/Skeleton/variations/SkeletonHeading.tsx
index c86d9ecc3df..6ac6fe6a40f 100644
--- a/packages/design-system/src/components/Skeleton/variations/SkeletonHeading.tsx
+++ b/packages/design-system/src/components/Skeleton/variations/SkeletonHeading.tsx
@@ -24,5 +24,5 @@ const SkeletonHeading = forwardRef(
);
},
);
-
+SkeletonHeading.displayName = 'SkeletonHeading';
export default SkeletonHeading;
diff --git a/packages/design-system/src/components/Skeleton/variations/SkeletonParagraph.tsx b/packages/design-system/src/components/Skeleton/variations/SkeletonParagraph.tsx
index 26484b41d33..4dffee63a69 100644
--- a/packages/design-system/src/components/Skeleton/variations/SkeletonParagraph.tsx
+++ b/packages/design-system/src/components/Skeleton/variations/SkeletonParagraph.tsx
@@ -23,5 +23,6 @@ const SkeletonParagraph = forwardRef(
);
},
);
+SkeletonParagraph.displayName = 'SkeletonParagraph';
export default SkeletonParagraph;
diff --git a/packages/design-system/src/components/Status/Primitive/StatusPrimitive.tsx b/packages/design-system/src/components/Status/Primitive/StatusPrimitive.tsx
index fd29dd7ccb9..60f06c0d18b 100644
--- a/packages/design-system/src/components/Status/Primitive/StatusPrimitive.tsx
+++ b/packages/design-system/src/components/Status/Primitive/StatusPrimitive.tsx
@@ -4,12 +4,13 @@ import classnames from 'classnames';
// eslint-disable-next-line @talend/import-depth
import { IconNameWithSize } from '@talend/icons/dist/typeUtils';
-import Tooltip from '../../Tooltip';
-import Loading from '../../Loading';
+import { Tooltip, TooltipChildrenFnProps, TooltipChildrenFnRef } from '../../Tooltip';
+import { Loading } from '../../Loading';
import { SizedIcon } from '../../Icon';
import { StackHorizontal } from '../../Stack';
import styles from './Status.module.scss';
+import { mergeRefs } from '../../../mergeRef';
export const variants = {
successful: 'successful',
@@ -32,9 +33,13 @@ const Status = forwardRef(
{ children, icon, inProgress, hideText, variant, ...rest }: StatusProps,
ref: Ref,
) => {
- const text = {children} ;
+ const text = (
+
+ {children}
+
+ );
const picto = (
-
+
{inProgress ? : icon ? : null}
);
@@ -42,7 +47,22 @@ const Status = forwardRef(
return (
- {hideText ? {picto} : [picto, text]}
+ {hideText ? (
+
+ {(triggerProps: TooltipChildrenFnProps, triggerRef: TooltipChildrenFnRef) => (
+
+ {inProgress ? : icon ? : null}
+
+ )}
+
+ ) : (
+ [picto, text]
+ )}
);
diff --git a/packages/design-system/src/components/Stepper/Progress/Primitive/Progress.tsx b/packages/design-system/src/components/Stepper/Progress/Primitive/Progress.tsx
index 2c7b6beafd1..542e2c86083 100644
--- a/packages/design-system/src/components/Stepper/Progress/Primitive/Progress.tsx
+++ b/packages/design-system/src/components/Stepper/Progress/Primitive/Progress.tsx
@@ -2,7 +2,7 @@ import { forwardRef, HTMLAttributes, ReactElement, Ref } from 'react';
import { useTranslation } from 'react-i18next';
import classnames from 'classnames';
-import VisuallyHidden from '../../../VisuallyHidden';
+import { VisuallyHidden } from '../../../VisuallyHidden';
import { I18N_DOMAIN_DESIGN_SYSTEM } from '../../../constants';
import styles from './Progress.module.scss';
@@ -39,5 +39,6 @@ const Progress = forwardRef((props: ProgressProps, ref: Ref) =>
);
});
+Progress.displayName = 'Progress';
export default Progress;
diff --git a/packages/design-system/src/components/Stepper/Step/Primitive/Step.tsx b/packages/design-system/src/components/Stepper/Step/Primitive/Step.tsx
index 8e9db428681..0e6bc079e6c 100644
--- a/packages/design-system/src/components/Stepper/Step/Primitive/Step.tsx
+++ b/packages/design-system/src/components/Stepper/Step/Primitive/Step.tsx
@@ -1,6 +1,6 @@
import { forwardRef, ReactElement, Ref } from 'react';
import classnames from 'classnames';
-import Tooltip from '../../../Tooltip';
+import { Tooltip, TooltipChildrenFnProps, TooltipChildrenFnRef } from '../../../Tooltip';
import styles from './Step.module.scss';
@@ -22,9 +22,11 @@ const Step = forwardRef(
{ title, tooltip, children, status, orientation = 'horizontal', ...rest }: StepPrimitiveProps,
ref: Ref,
) => {
- const step = (
+ const step = (triggerProps?: TooltipChildrenFnProps, triggerRef?: TooltipChildrenFnRef) => (
- {tooltip ?
{step} : step}
+ {tooltip ?
{step} : step({})}
);
},
diff --git a/packages/design-system/src/components/Stepper/Step/Step.cy.tsx b/packages/design-system/src/components/Stepper/Step/Step.cy.tsx
index 791b6572c46..0473b99c3c1 100644
--- a/packages/design-system/src/components/Stepper/Step/Step.cy.tsx
+++ b/packages/design-system/src/components/Stepper/Step/Step.cy.tsx
@@ -7,7 +7,7 @@ context('
', () => {
it('should show a tooltip', () => {
cy.mount(
);
- cy.findByTestId('step').trigger('mouseover');
+ cy.findByTestId('step').trigger('mouseenter');
cy.findByRole('tooltip').should('be.visible').should('have.text', 'Here is why');
});
diff --git a/packages/design-system/src/components/Stepper/Step/variations/Step.skeleton.tsx b/packages/design-system/src/components/Stepper/Step/variations/Step.skeleton.tsx
index ea6dc1aeaec..df0195f06ef 100644
--- a/packages/design-system/src/components/Stepper/Step/variations/Step.skeleton.tsx
+++ b/packages/design-system/src/components/Stepper/Step/variations/Step.skeleton.tsx
@@ -1,5 +1,5 @@
import { memo, forwardRef, Ref } from 'react';
-import Skeleton from '../../../Skeleton';
+import { Skeleton } from '../../../Skeleton';
import classnames from 'classnames';
import stepStyles from '../Primitive/Step.module.scss';
@@ -21,5 +21,6 @@ const StepSkeleton = forwardRef((props: SkeletonProps, ref: Ref
)
);
});
+StepSkeleton.displayName = 'StepSkeleton';
export default memo(StepSkeleton);
diff --git a/packages/design-system/src/components/Stepper/Stepper.cy.tsx b/packages/design-system/src/components/Stepper/Stepper.cy.tsx
index a9bb698aaa6..763fac2f6fa 100644
--- a/packages/design-system/src/components/Stepper/Stepper.cy.tsx
+++ b/packages/design-system/src/components/Stepper/Stepper.cy.tsx
@@ -1,6 +1,6 @@
/* eslint-disable testing-library/prefer-screen-queries */
/* eslint-disable testing-library/await-async-query */
-import Stepper from '.';
+import { Stepper } from '.';
context(' ', () => {
it('should render first step as current step', () => {
diff --git a/packages/design-system/src/components/Stepper/index.ts b/packages/design-system/src/components/Stepper/index.ts
index ed5274f0892..525bf9c7d32 100644
--- a/packages/design-system/src/components/Stepper/index.ts
+++ b/packages/design-system/src/components/Stepper/index.ts
@@ -1,16 +1,17 @@
-import StepperVertical from './variations/Stepper.vertical';
-import StepperHorizontal from './variations/Stepper.horizontal';
+import { StepperVertical, StepperVerticalProps } from './variations/Stepper.vertical';
+import { StepperHorizontal, StepperHorizontalProps } from './variations/Stepper.horizontal';
import Step from './Step';
-const StepperComponent = StepperVertical as typeof StepperVertical & {
+const Stepper = StepperVertical as typeof StepperVertical & {
Vertical: typeof StepperVertical;
Horizontal: typeof StepperHorizontal;
Step: typeof Step;
};
-StepperComponent.Vertical = StepperVertical;
-StepperComponent.Horizontal = StepperHorizontal;
+Stepper.Vertical = StepperVertical;
+Stepper.Horizontal = StepperHorizontal;
-StepperComponent.Step = Step;
+Stepper.Step = Step;
-export default StepperComponent;
+export type { StepperVerticalProps, StepperHorizontalProps };
+export { Stepper };
diff --git a/packages/design-system/src/components/Stepper/variations/Stepper.horizontal.tsx b/packages/design-system/src/components/Stepper/variations/Stepper.horizontal.tsx
index bfd9041c4ed..fb3ff250cd3 100644
--- a/packages/design-system/src/components/Stepper/variations/Stepper.horizontal.tsx
+++ b/packages/design-system/src/components/Stepper/variations/Stepper.horizontal.tsx
@@ -1,12 +1,12 @@
import { forwardRef, Ref } from 'react';
import Stepper, { StepperProps } from '../Stepper';
-type StepperHorizontalProps = Omit;
+export type StepperHorizontalProps = Omit;
-const StepperHorizontal = forwardRef((props: StepperHorizontalProps, ref: Ref) => (
-
-));
+export const StepperHorizontal = forwardRef(
+ (props: StepperHorizontalProps, ref: Ref) => (
+
+ ),
+);
StepperHorizontal.displayName = 'StepperHorizontal';
-
-export default StepperHorizontal;
diff --git a/packages/design-system/src/components/Stepper/variations/Stepper.vertical.tsx b/packages/design-system/src/components/Stepper/variations/Stepper.vertical.tsx
index 25749cc4b32..7787cb4ed81 100644
--- a/packages/design-system/src/components/Stepper/variations/Stepper.vertical.tsx
+++ b/packages/design-system/src/components/Stepper/variations/Stepper.vertical.tsx
@@ -1,12 +1,12 @@
import { forwardRef, Ref } from 'react';
import Stepper, { StepperProps } from '../Stepper';
-type StepperVerticalProps = Omit;
+export type StepperVerticalProps = Omit;
-const StepperVertical = forwardRef((props: StepperVerticalProps, ref: Ref) => (
-
-));
+export const StepperVertical = forwardRef(
+ (props: StepperVerticalProps, ref: Ref) => (
+
+ ),
+);
StepperVertical.displayName = 'StepperVertical';
-
-export default StepperVertical;
diff --git a/packages/design-system/src/components/Switch/Switch.module.scss b/packages/design-system/src/components/Switch/Switch.module.scss
index 14de156eea6..ee0086abe94 100644
--- a/packages/design-system/src/components/Switch/Switch.module.scss
+++ b/packages/design-system/src/components/Switch/Switch.module.scss
@@ -1,30 +1,47 @@
@use '~@talend/design-tokens/lib/tokens' as tokens;
.switch {
- div {
+ .container {
position: relative;
display: inline-flex;
background: tokens.$coral-color-neutral-background-strong;
border-radius: 10rem;
box-shadow: inset 0 0.1rem 0.3rem 0 rgba(0, 0, 0, 0.25);
overflow: hidden;
- }
- button {
- position: relative;
- display: flex;
- align-items: center;
- justify-content: space-around;
- margin: 0;
- padding: 0 1rem;
- color: tokens.$coral-color-neutral-text;
- font: tokens.$coral-paragraph-s;
- opacity: tokens.$coral-opacity-m;
- user-select: none;
- cursor: pointer;
- background: none;
- border: none;
- z-index: tokens.$coral-elevation-layer-interactive-front;
+ &:hover .switchIndicator em {
+ background-color: tokens.$coral-color-accent-background-active;
+ }
+
+ > .btn {
+ position: relative;
+ display: flex;
+ align-items: center;
+ justify-content: space-around;
+ margin: 0;
+ padding: 0 1rem;
+ color: tokens.$coral-color-neutral-text;
+ font: tokens.$coral-paragraph-s;
+ opacity: tokens.$coral-opacity-m;
+ user-select: none;
+ cursor: pointer;
+ background: none;
+ border: none;
+ z-index: tokens.$coral-elevation-layer-interactive-front;
+
+ &[aria-checked='true'] {
+ color: tokens.$coral-color-accent-text-weak;
+ opacity: 1;
+ }
+
+ &[aria-checked] ~ .switchIndicator {
+ visibility: hidden;
+ }
+
+ &[aria-checked='true'] ~ .switchIndicator {
+ visibility: visible;
+ }
+ }
}
.switchIndicator {
@@ -51,27 +68,10 @@
border-radius: 100px;
}
- &.readOnly div:hover .switchIndicator em {
- background-color: tokens.$coral-color-accent-background-active;
- }
-
- [aria-selected] {
+ &.readOnly div .btn[aria-checked] {
transition: color tokens.$coral-transition-normal;
}
- [aria-selected='true'] {
- color: tokens.$coral-color-accent-text-weak;
- opacity: 1;
- }
-
- [aria-selected] ~ .switchIndicator {
- visibility: hidden;
- }
-
- [aria-selected='true'] ~ .switchIndicator {
- visibility: visible;
- }
-
&.disabled div {
opacity: tokens.$coral-opacity-m;
}
diff --git a/packages/design-system/src/components/Switch/Switch.test.tsx b/packages/design-system/src/components/Switch/Switch.test.tsx
new file mode 100644
index 00000000000..18c92f86dfc
--- /dev/null
+++ b/packages/design-system/src/components/Switch/Switch.test.tsx
@@ -0,0 +1,31 @@
+import { describe, it, expect } from '@jest/globals';
+import { axe } from 'jest-axe';
+import { render } from '@testing-library/react';
+import { Switch } from './';
+
+jest.mock('@talend/utils', () => {
+ let i = 0;
+ return {
+ // we need stable but different uuid (is fixed to 42 by current mock)
+ randomUUID: () => `mocked-uuid-${i++}`,
+ };
+});
+
+describe('Switch', () => {
+ it('should render accessible html', async () => {
+ // note we need to add the aria-label to be accessible
+ // TODO: make it required
+ const { container } = render(
+
+
+ ,
+ ,
+ );
+ expect(container.firstChild).toMatchSnapshot();
+ const results = await axe(document.body);
+ expect(results).toHaveNoViolations();
+ });
+});
diff --git a/packages/design-system/src/components/Switch/Switch.tsx b/packages/design-system/src/components/Switch/Switch.tsx
index 2ee6d09883a..6d5468f2b08 100644
--- a/packages/design-system/src/components/Switch/Switch.tsx
+++ b/packages/design-system/src/components/Switch/Switch.tsx
@@ -1,61 +1,69 @@
-import { useLayoutEffect, useRef } from 'react';
-import type { PropsWithChildren, MouseEvent } from 'react';
-import { Radio, RadioGroup, useRadioState } from 'reakit';
+import { MouseEvent, useLayoutEffect, useRef, useState, useEffect } from 'react';
+import type { PropsWithChildren, HTMLAttributes } from 'react';
+
import classnames from 'classnames';
+
+import { randomUUID } from '@talend/utils';
+
import theme from './Switch.module.scss';
-export type SwitchProps = PropsWithChildren & {
- label: string;
+const emptyValues: string[] = [];
+
+export type SwitchProps = PropsWithChildren, 'onChange'>> & {
+ label?: string;
value?: string;
defaultValue?: string;
- values?: any[];
- checked: boolean;
- disabled: boolean;
- readOnly: boolean;
+ values?: string[];
+ checked?: boolean;
+ disabled?: boolean;
+ readOnly?: boolean;
+ // Redefine onChange prop
+ onChange?: (event: MouseEvent, selectedValue: string) => void;
};
-const Switch = ({
+export const Switch = ({
label,
value,
defaultValue,
- values,
+ values = emptyValues,
checked,
disabled,
readOnly,
onChange,
...rest
}: SwitchProps) => {
- const radio = useRadioState({
- state: value || defaultValue || (values && values[0]),
- loop: false,
- unstable_virtual: true,
- });
-
- const containerRef = useRef>();
- const switchIndicator = useRef>();
+ const [radio, setRadio] = useState(value || defaultValue || (values && values[0]));
+ const switchIndicator = useRef>(null);
+ const containerRef = useRef>(null);
+ const [valueIds, setValueIds] = useState(values.map(() => `id-${randomUUID()}`));
+ useEffect(() => {
+ setValueIds(values.map(() => `id-${randomUUID()}`));
+ }, [values]);
useLayoutEffect(() => {
const radioGroup = containerRef?.current;
if (!radioGroup) {
return;
}
- const radioGroupChildren = Array.prototype.slice.call(radioGroup.children);
- const checkedElement = radioGroup.querySelector(`#${radio.currentId}`);
+ const checkedRadioIndex = values.indexOf(radio);
+ const currentId = valueIds[checkedRadioIndex];
+
+ const checkedElement = radioGroup.querySelector(`#${currentId}`);
+ const items = Array.from(radioGroup.querySelectorAll(`.${theme.btn}`));
if (!checkedElement) {
return;
}
- const checkedRadioIndex = radioGroupChildren.indexOf(checkedElement);
const checkedRadioSpanWidth = checkedElement.scrollWidth;
const switchIndicatorRef = switchIndicator?.current;
if (switchIndicatorRef) {
switchIndicatorRef.style.width = `${checkedRadioSpanWidth}px`;
- const radioWidths = radio.items.map(item => item.ref.current?.scrollWidth || 0);
+ const radioWidths = items.map(item => item?.scrollWidth || 0);
switchIndicatorRef.style.transform = `translateX(${radioWidths
?.slice(0, checkedRadioIndex)
.reduce((accumulator, currentValue) => accumulator + currentValue, 0)}px)`;
- switchIndicatorRef.dataset.animated = true;
+ switchIndicatorRef.dataset.animated = 'true';
}
- }, [radio, defaultValue, radio.items]);
+ }, [radio, defaultValue, value, values, valueIds]);
return (
-
+
{values.map((v: string, i: number) => {
- const isChecked = radio.state === v;
+ const isChecked = radio === v;
return (
- ) => onChange && onChange(event, v)}
- {...radio}
+ ) => {
+ setRadio(v);
+ onChange?.(e, v);
+ }}
value={v}
- as="button"
key={i}
data-checked={isChecked}
>
{v}
-
+
);
})}
-
+
);
};
-export default Switch;
+Switch.displayName = 'Switch';
diff --git a/packages/design-system/src/components/Switch/__snapshots__/Switch.test.tsx.snap b/packages/design-system/src/components/Switch/__snapshots__/Switch.test.tsx.snap
new file mode 100644
index 00000000000..12b3d1392dd
--- /dev/null
+++ b/packages/design-system/src/components/Switch/__snapshots__/Switch.test.tsx.snap
@@ -0,0 +1,92 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`Switch should render accessible html 1`] = `
+
+
+
+
+ value a
+
+
+ value b
+
+
+ value c
+
+
+ value d
+
+
+ value e
+
+
+ value f
+
+
+
+
+
+
+ ,
+
+`;
diff --git a/packages/design-system/src/components/Switch/index.ts b/packages/design-system/src/components/Switch/index.ts
index 3c587b7aae1..1b19c1d39c3 100644
--- a/packages/design-system/src/components/Switch/index.ts
+++ b/packages/design-system/src/components/Switch/index.ts
@@ -1,3 +1 @@
-import Switch from './Switch';
-
-export default Switch;
+export * from './Switch';
diff --git a/packages/design-system/src/components/Tabs/Primitive/TabPanel.tsx b/packages/design-system/src/components/Tabs/Primitive/TabPanel.tsx
new file mode 100644
index 00000000000..b9e8ff73d25
--- /dev/null
+++ b/packages/design-system/src/components/Tabs/Primitive/TabPanel.tsx
@@ -0,0 +1,28 @@
+import { useContext } from 'react';
+import { TabsInternalContext } from './TabsProvider';
+
+type TabPanelPropTypes = {
+ id: string;
+ children: React.ReactNode | React.ReactNode[];
+ renderIf?: boolean;
+};
+
+export function TabPanel({ children, id, renderIf }: TabPanelPropTypes): JSX.Element {
+ const context = useContext(TabsInternalContext);
+ const style = {
+ display: '',
+ };
+ if (id !== context?.value) {
+ if (renderIf) {
+ return <>>;
+ }
+ style.display = 'none';
+ }
+ return (
+
+ {children}
+
+ );
+}
+
+TabPanel.displayName = 'TabPanel';
diff --git a/packages/design-system/src/components/WIP/Tabs/Primitive/TabStyles.module.scss b/packages/design-system/src/components/Tabs/Primitive/TabStyles.module.scss
similarity index 85%
rename from packages/design-system/src/components/WIP/Tabs/Primitive/TabStyles.module.scss
rename to packages/design-system/src/components/Tabs/Primitive/TabStyles.module.scss
index 62478db08e9..386c1d69c78 100644
--- a/packages/design-system/src/components/WIP/Tabs/Primitive/TabStyles.module.scss
+++ b/packages/design-system/src/components/Tabs/Primitive/TabStyles.module.scss
@@ -1,5 +1,18 @@
@use '~@talend/design-tokens/lib/tokens';
+.tablist {
+ list-style-type: none;
+ margin: 0;
+ padding: 0;
+ display: flex;
+ flex-flow: row;
+ flex-wrap: nowrap;
+ align-items: flex-start;
+ justify-content: flex-start;
+ row-gap: var(--coral-spacing-m, 1.6rem);
+ column-gap: var(--coral-spacing-m, 1.6rem);
+}
+
.tab {
font: tokens.$coral-heading-s;
height: tokens.$coral-sizing-xs;
@@ -30,7 +43,6 @@
color: tokens.$coral-color-neutral-icon-weak;
}
-
&::after {
content: '';
position: absolute;
@@ -73,7 +85,6 @@
}
}
-
&_large {
font: tokens.$coral-heading-l;
height: tokens.$coral-sizing-s;
diff --git a/packages/design-system/src/components/Tabs/Primitive/Tabs.tsx b/packages/design-system/src/components/Tabs/Primitive/Tabs.tsx
new file mode 100644
index 00000000000..2efcab63ffe
--- /dev/null
+++ b/packages/design-system/src/components/Tabs/Primitive/Tabs.tsx
@@ -0,0 +1,56 @@
+import { useContext } from 'react';
+import { SizedIcon } from '../../Icon';
+import { TagDefault } from '../../Tag';
+import { StackHorizontal } from '../../Stack';
+import { TabsInternalContext } from './TabsProvider';
+import { Tooltip } from '../../Tooltip';
+import style from './TabStyles.module.scss';
+import { IconNameWithSize } from '@talend/icons';
+import classNames from 'classnames';
+
+export type TabsPropTypes = {
+ children: React.ReactNode[];
+};
+
+export function Tabs({ children }: TabsPropTypes) {
+ return (
+
+ {children}
+
+ );
+}
+Tabs.displayName = 'Tabs';
+
+export type TabPropTypes = {
+ ['aria-controls']: string;
+ title: string;
+ disabled?: boolean;
+ icon?: IconNameWithSize<'S'>;
+ tag?: string | number;
+ tooltip?: string;
+};
+
+export function Tab(props: TabPropTypes) {
+ const context = useContext(TabsInternalContext);
+ let content = (
+ context?.onChange(e, props['aria-controls'])}
+ disabled={props.disabled}
+ type="button"
+ >
+
+ {props.icon && }
+ {props.title}
+ {props.tag && {props.tag} }
+
+
+ );
+ if (props.tooltip) {
+ content = {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
new file mode 100644
index 00000000000..ff899fc2a46
--- /dev/null
+++ b/packages/design-system/src/components/Tabs/Primitive/TabsProvider.tsx
@@ -0,0 +1,40 @@
+/* eslint-disable react/no-unused-prop-types */
+import { useControl, UseControlReturns } from '../../../useControl';
+import { StackVertical } from '../../Stack';
+import { createContext } from 'react';
+
+export type TabsProviderPropTypes = {
+ defaultActiveKey?: string;
+ activeKey?: string;
+ onSelect?: (event: any, key: string) => void;
+ size?: string;
+};
+
+type WithChildren = {
+ children: React.ReactNode[];
+};
+
+export const TabsInternalContext = createContext<
+ (UseControlReturns & { size?: string }) | null
+>(null);
+
+export function TabsProvider(props: TabsProviderPropTypes & WithChildren) {
+ const controlled = useControl(props, {
+ valueKey: 'activeKey',
+ defaultValueKey: 'defaultActiveKey',
+ onChangeKey: 'onSelect',
+ defaultValue: '',
+ selector: (e: any, id: string) => {
+ return id;
+ },
+ });
+ 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..5b390ec67b3
--- /dev/null
+++ b/packages/design-system/src/components/Tabs/Tabs.test.tsx
@@ -0,0 +1,26 @@
+import { describe, it, expect } from '@jest/globals';
+import { axe } from 'jest-axe';
+import { render } from '@testing-library/react';
+import { Tabs, TabPanel, Tab, TabsProvider } from './';
+
+describe('Tabs', () => {
+ it('should render accessible html', 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..e1d7fe398f7
--- /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[`Tabs should render accessible html 1`] = `
+
+
+
+
+
+
+ Home
+
+
+
+
+
+
+ Profile
+
+
+
+
+
+
+ Contact
+
+
+
+
+
+ Tab content for Home
+
+
+ Tab content for Profile
+
+
+ Tab content for Contact
+
+
+
+`;
diff --git a/packages/design-system/src/components/Tabs/index.ts b/packages/design-system/src/components/Tabs/index.ts
new file mode 100644
index 00000000000..8177fe1c79b
--- /dev/null
+++ b/packages/design-system/src/components/Tabs/index.ts
@@ -0,0 +1,3 @@
+export * from './Primitive/TabsProvider';
+export * from './Primitive/Tabs';
+export * from './Primitive/TabPanel';
diff --git a/packages/design-system/src/components/Tag/Tag.tsx b/packages/design-system/src/components/Tag/Tag.tsx
index fccd6f79625..40c2aac8916 100644
--- a/packages/design-system/src/components/Tag/Tag.tsx
+++ b/packages/design-system/src/components/Tag/Tag.tsx
@@ -22,14 +22,14 @@ export enum TagVariant {
// This const allows JS component to type props with restricted list of tag variations
export const TagVariantsNames = Object.values(TagVariant);
-type TagProps = Omit & {
+export type TagProps = Omit & {
/**
* Tag variation depending on its semantic
*/
variant?: keyof typeof TagVariant;
};
-const Tag = forwardRef(({ variant, ...rest }: TagProps, ref: Ref) => {
+export const Tag = forwardRef(({ variant, ...rest }: TagProps, ref: Ref) => {
switch (variant) {
case TagVariant.information:
return ;
@@ -45,5 +45,4 @@ const Tag = forwardRef(({ variant, ...rest }: TagProps, ref: Ref ;
}
});
-
-export default Tag;
+Tag.displayName = 'Tag';
diff --git a/packages/design-system/src/components/Tag/index.ts b/packages/design-system/src/components/Tag/index.ts
index e23086a37e7..e024babd09c 100644
--- a/packages/design-system/src/components/Tag/index.ts
+++ b/packages/design-system/src/components/Tag/index.ts
@@ -1,4 +1,4 @@
-import Tag, { TagVariantsNames } from './Tag';
+import { Tag, TagVariantsNames, TagProps, TagVariant } from './Tag';
import {
TagDefault,
@@ -19,3 +19,5 @@ export {
TagBeta,
TagVariantsNames,
};
+
+export type { TagProps, TagVariant };
diff --git a/packages/design-system/src/components/Tag/primitive/TagPrimitive.tsx b/packages/design-system/src/components/Tag/primitive/TagPrimitive.tsx
index e0ace2d5513..da4eb17b10e 100644
--- a/packages/design-system/src/components/Tag/primitive/TagPrimitive.tsx
+++ b/packages/design-system/src/components/Tag/primitive/TagPrimitive.tsx
@@ -7,5 +7,6 @@ export type TagProps = Omit, 'style'>;
const Tag = forwardRef((props: TagProps, ref: Ref) => {
return ;
});
+Tag.displayName = 'Tag';
export default Tag;
diff --git a/packages/design-system/src/components/Tag/variations/TagBeta.tsx b/packages/design-system/src/components/Tag/variations/TagBeta.tsx
index 937d4c4b846..4f3d235805e 100644
--- a/packages/design-system/src/components/Tag/variations/TagBeta.tsx
+++ b/packages/design-system/src/components/Tag/variations/TagBeta.tsx
@@ -9,5 +9,6 @@ type TagProps = Omit;
const TagBeta = forwardRef((props: TagProps, ref: Ref) => (
));
+TagBeta.displayName = 'TagBeta';
export default TagBeta;
diff --git a/packages/design-system/src/components/Tag/variations/TagDefault.tsx b/packages/design-system/src/components/Tag/variations/TagDefault.tsx
index d065913fef6..0833195a1cc 100644
--- a/packages/design-system/src/components/Tag/variations/TagDefault.tsx
+++ b/packages/design-system/src/components/Tag/variations/TagDefault.tsx
@@ -9,5 +9,6 @@ type TagProps = Omit;
const TagDefault = forwardRef((props: TagProps, ref: Ref) => (
));
+TagDefault.displayName = 'TagDefault';
export default TagDefault;
diff --git a/packages/design-system/src/components/Tag/variations/TagDestructive.tsx b/packages/design-system/src/components/Tag/variations/TagDestructive.tsx
index 8d62e9ad191..cea2e124e7b 100644
--- a/packages/design-system/src/components/Tag/variations/TagDestructive.tsx
+++ b/packages/design-system/src/components/Tag/variations/TagDestructive.tsx
@@ -9,5 +9,6 @@ type TagProps = Omit;
const TagDestructive = forwardRef((props: TagProps, ref: Ref) => (
));
+TagDestructive.displayName = 'TagDestructive';
export default TagDestructive;
diff --git a/packages/design-system/src/components/Tag/variations/TagInformation.tsx b/packages/design-system/src/components/Tag/variations/TagInformation.tsx
index 01a17949719..efbb51ab700 100644
--- a/packages/design-system/src/components/Tag/variations/TagInformation.tsx
+++ b/packages/design-system/src/components/Tag/variations/TagInformation.tsx
@@ -9,5 +9,6 @@ type TagProps = Omit;
const TagInformation = forwardRef((props: TagProps, ref: Ref) => (
));
+TagInformation.displayName = 'TagInformation';
export default TagInformation;
diff --git a/packages/design-system/src/components/Tag/variations/TagSuccess.tsx b/packages/design-system/src/components/Tag/variations/TagSuccess.tsx
index a4904993464..0620485ce0e 100644
--- a/packages/design-system/src/components/Tag/variations/TagSuccess.tsx
+++ b/packages/design-system/src/components/Tag/variations/TagSuccess.tsx
@@ -9,5 +9,6 @@ type TagProps = Omit;
const TagSuccess = forwardRef((props: TagProps, ref: Ref) => (
));
+TagSuccess.displayName = 'TagSuccess';
export default TagSuccess;
diff --git a/packages/design-system/src/components/Tag/variations/TagWarning.tsx b/packages/design-system/src/components/Tag/variations/TagWarning.tsx
index 08dc8da29c0..d0a000e2a90 100644
--- a/packages/design-system/src/components/Tag/variations/TagWarning.tsx
+++ b/packages/design-system/src/components/Tag/variations/TagWarning.tsx
@@ -9,5 +9,6 @@ type TagProps = Omit;
const TagWarning = forwardRef((props: TagProps, ref: Ref) => (
));
+TagWarning.displayName = 'TagWarning';
export default TagWarning;
diff --git a/packages/design-system/src/components/ThemeProvider/ThemeProvider.tsx b/packages/design-system/src/components/ThemeProvider/ThemeProvider.tsx
index adaa3b37139..2b4da30c99e 100644
--- a/packages/design-system/src/components/ThemeProvider/ThemeProvider.tsx
+++ b/packages/design-system/src/components/ThemeProvider/ThemeProvider.tsx
@@ -12,7 +12,7 @@ export type ThemeProviderProps = PropsWithChildren<{
theme?: string;
}>;
-const ThemeProvider = ({ theme = 'light', children }: ThemeProviderProps) => {
+export const ThemeProvider = ({ theme = 'light', children }: ThemeProviderProps) => {
const [selectedTheme, setSelectedTheme] = useState(theme);
// Handle nested Providers: parent Provider doesn't have context, child does
const context = useContext(ThemeContext);
@@ -32,5 +32,3 @@ const ThemeProvider = ({ theme = 'light', children }: ThemeProviderProps) => {
);
};
-
-export default ThemeProvider;
diff --git a/packages/design-system/src/components/ThemeProvider/index.ts b/packages/design-system/src/components/ThemeProvider/index.ts
index 7c0edb7223c..65083eb4fdc 100644
--- a/packages/design-system/src/components/ThemeProvider/index.ts
+++ b/packages/design-system/src/components/ThemeProvider/index.ts
@@ -1,10 +1,10 @@
import ThemeSwitcher from './ThemeSwitcher';
-import ThemeProvider from './ThemeProvider';
+import { ThemeProvider as BaseThemeProvider, ThemeProviderProps } from './ThemeProvider';
-const TalendThemeProvider = ThemeProvider as typeof ThemeProvider & {
+export const ThemeProvider = BaseThemeProvider as typeof BaseThemeProvider & {
ThemeSwitcher: typeof ThemeSwitcher;
};
-TalendThemeProvider.ThemeSwitcher = ThemeSwitcher;
+ThemeProvider.ThemeSwitcher = ThemeSwitcher;
-export default TalendThemeProvider;
+export type { ThemeProviderProps };
diff --git a/packages/design-system/src/components/Tooltip/Tooltip.cy.tsx b/packages/design-system/src/components/Tooltip/Tooltip.cy.tsx
index 02c3f0d582e..b642e740fb7 100644
--- a/packages/design-system/src/components/Tooltip/Tooltip.cy.tsx
+++ b/packages/design-system/src/components/Tooltip/Tooltip.cy.tsx
@@ -1,6 +1,6 @@
/* eslint-disable testing-library/prefer-screen-queries */
/* eslint-disable testing-library/await-async-query */
-import Tooltip from './Tooltip';
+import { Tooltip } from './Tooltip';
context(' ', () => {
describe('default', () => {
@@ -19,7 +19,7 @@ context(' ', () => {
it('Should be able to override baseId', () => {
const tooltipBaseId = 'base-id';
cy.mount(
-
+
button
,
);
diff --git a/packages/design-system/src/components/Tooltip/Tooltip.module.scss b/packages/design-system/src/components/Tooltip/Tooltip.module.scss
index 7391aca3823..f3659df1388 100644
--- a/packages/design-system/src/components/Tooltip/Tooltip.module.scss
+++ b/packages/design-system/src/components/Tooltip/Tooltip.module.scss
@@ -1,28 +1,20 @@
@use '~@talend/design-tokens/lib/tokens';
-.tooltip {
- z-index: tokens.$coral-elevation-layer-overlay;
-
- .container {
- padding: tokens.$coral-spacing-xxs tokens.$coral-spacing-xs;
- max-width: tokens.$coral-sizing-maximal;
- font: tokens.$coral-paragraph-s;
- color: tokens.$coral-color-assistive-text;
- background: tokens.$coral-color-assistive-background;
- border-radius: tokens.$coral-radius-s;
- transition: opacity tokens.$coral-transition-fast;
- opacity: 0;
- }
-
- &[data-enter] .container {
- opacity: 1;
- }
+.container {
+ padding: tokens.$coral-spacing-xxs tokens.$coral-spacing-xs;
+ max-width: tokens.$coral-sizing-maximal;
+ font: tokens.$coral-paragraph-s;
+ color: tokens.$coral-color-assistive-text;
+ background: tokens.$coral-color-assistive-background;
+ border-radius: tokens.$coral-radius-s;
+ transition: opacity tokens.$coral-transition-fast;
+ opacity: 1;
+}
- .arrow {
- fill: tokens.$coral-color-assistive-background;
+.arrow {
+ fill: tokens.$coral-color-assistive-background;
- :global(.stroke) {
- display: none;
- }
+ :global(.stroke) {
+ display: none;
}
}
diff --git a/packages/design-system/src/components/Tooltip/Tooltip.tsx b/packages/design-system/src/components/Tooltip/Tooltip.tsx
index 1ef5efc00e4..bf8348fe8e6 100644
--- a/packages/design-system/src/components/Tooltip/Tooltip.tsx
+++ b/packages/design-system/src/components/Tooltip/Tooltip.tsx
@@ -1,21 +1,29 @@
-import { cloneElement } from 'react';
-import type { PropsWithChildren, FC } from 'react';
+/* eslint-disable @typescript-eslint/no-explicit-any */
+import { useState, useRef } from 'react';
+import type { MutableRefObject, RefCallback, ReactElement, ReactNode } from 'react';
+
import {
- Tooltip as ReakitTooltip,
- TooltipArrow as ReakitTooltipArrow,
- TooltipProps as ReakitTooltipProps,
- TooltipReference as ReakitTooltipReference,
- useTooltipState as useReakitTooltipState,
-} from 'reakit/Tooltip';
+ arrow,
+ FloatingArrow,
+ FloatingPortal,
+ useFloating,
+ useHover,
+ useFocus,
+ useDismiss,
+ useRole,
+ useInteractions,
+ autoUpdate,
+ flip,
+ offset,
+ shift,
+} from '@floating-ui/react';
-import { unstable_useId as useId } from 'reakit/Id';
+import { ChildOrGenerator, renderOrClone } from '../../renderOrClone';
+import { useId } from '../../useId';
import styles from './Tooltip.module.scss';
export type Placement =
- | 'auto-start'
- | 'auto'
- | 'auto-end'
| 'top-start'
| 'top'
| 'top-end'
@@ -29,37 +37,77 @@ export type Placement =
| 'left'
| 'left-start';
-export type TooltipProps = PropsWithChildren &
- ReakitTooltipProps & {
- title?: string;
- };
+export type TooltipPlacement = Placement;
+export type TooltipChildrenFnProps = {
+ onHover?: (event: any) => void;
+ onFocus?: (event: any) => void;
+ onBlur?: (event: any) => void;
+ 'aria-describedby'?: string;
+};
+
+export type TooltipChildrenFnRef =
+ | any
+ | MutableRefObject
+ | RefCallback;
-const Tooltip: FC = ({ children, title, baseId, ...rest }: TooltipProps) => {
- const { id: reakitId } = useId();
- const tooltipState = useReakitTooltipState({
- ...rest,
- animated: 250,
- gutter: 15,
- unstable_flip: true,
- unstable_preventOverflow: true,
- baseId: baseId || reakitId,
+export type TooltipProps = {
+ title?: ReactNode;
+ placement?: Placement;
+ id?: string;
+ children: ChildOrGenerator;
+};
+
+export const Tooltip = ({ id, children, title, placement = 'top', ...rest }: TooltipProps) => {
+ const safeId = useId(id);
+ const [isOpen, setIsOpen] = useState(false);
+ const arrowRef = useRef(null);
+ const floating = useFloating({
+ placement: placement || 'top',
+ open: isOpen,
+ onOpenChange: setIsOpen,
+ middleware: [
+ arrow({
+ element: arrowRef,
+ }),
+ offset(10),
+ flip({
+ crossAxis: placement.includes('-'),
+ fallbackAxisSideDirection: 'start',
+ padding: 5,
+ }),
+ shift({ padding: 4 }),
+ ],
+ whileElementsMounted: autoUpdate,
});
+ const hover = useHover(floating.context, { move: false });
+ const focus = useFocus(floating.context);
+ const dismiss = useDismiss(floating.context);
+ const role = useRole(floating.context, { role: 'tooltip' });
+ const { getReferenceProps, getFloatingProps } = useInteractions([hover, focus, dismiss, role]);
return (
<>
-
- {referenceProps => cloneElement(children, referenceProps)}
-
- {title && (
-
-
-
- {title}
-
-
+ {renderOrClone(
+ children,
+ {
+ ...getReferenceProps(),
+ 'aria-describedby': safeId,
+ },
+ floating.refs.setReference,
)}
+
+
+
+ {title}
+
+
>
);
};
-
-export default Tooltip;
diff --git a/packages/design-system/src/components/Tooltip/index.ts b/packages/design-system/src/components/Tooltip/index.ts
index 8a72f966f24..7594a8f06c1 100644
--- a/packages/design-system/src/components/Tooltip/index.ts
+++ b/packages/design-system/src/components/Tooltip/index.ts
@@ -1,4 +1 @@
-import Tooltip, { Placement } from './Tooltip';
-
-export type { Placement as TooltipPlacement };
-export default Tooltip;
+export * from './Tooltip';
diff --git a/packages/design-system/src/components/VisuallyHidden/VisuallyHidden.module.scss b/packages/design-system/src/components/VisuallyHidden/VisuallyHidden.module.scss
new file mode 100644
index 00000000000..1566bc4c79b
--- /dev/null
+++ b/packages/design-system/src/components/VisuallyHidden/VisuallyHidden.module.scss
@@ -0,0 +1,11 @@
+.hidden {
+ border: 0;
+ clip: rect(0, 0, 0, 0);
+ height: 1px;
+ margin: -1px;
+ overflow: hidden;
+ padding: 0;
+ position: absolute;
+ white-space: nowrap;
+ width: 1px;
+}
diff --git a/packages/design-system/src/components/VisuallyHidden/VisuallyHidden.tsx b/packages/design-system/src/components/VisuallyHidden/VisuallyHidden.tsx
new file mode 100644
index 00000000000..6378f084cd1
--- /dev/null
+++ b/packages/design-system/src/components/VisuallyHidden/VisuallyHidden.tsx
@@ -0,0 +1,8 @@
+import { HTMLAttributes } from 'react';
+import style from './VisuallyHidden.module.scss';
+
+export type VisuallyHiddenProps = Omit, 'className'>;
+
+export function VisuallyHidden(props: VisuallyHiddenProps) {
+ return ;
+}
diff --git a/packages/design-system/src/components/VisuallyHidden/index.ts b/packages/design-system/src/components/VisuallyHidden/index.ts
index e231f3f26eb..f11f3f421b2 100644
--- a/packages/design-system/src/components/VisuallyHidden/index.ts
+++ b/packages/design-system/src/components/VisuallyHidden/index.ts
@@ -1,3 +1 @@
-import { VisuallyHidden } from 'reakit';
-
-export default VisuallyHidden;
+export * from './VisuallyHidden';
diff --git a/packages/design-system/src/components/WIP/Accordion/index.ts b/packages/design-system/src/components/WIP/Accordion/index.ts
deleted file mode 100644
index ca3ebf25b32..00000000000
--- a/packages/design-system/src/components/WIP/Accordion/index.ts
+++ /dev/null
@@ -1,6 +0,0 @@
-import Accordion from './Accordion';
-import CollapsiblePanel from './Primitive/CollapsiblePanel';
-
-export { Accordion, CollapsiblePanel };
-
-export default Accordion;
diff --git a/packages/design-system/src/components/WIP/Card/index.ts b/packages/design-system/src/components/WIP/Card/index.ts
deleted file mode 100644
index c68311df80b..00000000000
--- a/packages/design-system/src/components/WIP/Card/index.ts
+++ /dev/null
@@ -1 +0,0 @@
-export { default } from './Card';
diff --git a/packages/design-system/src/components/WIP/Combobox/Combobox.tsx b/packages/design-system/src/components/WIP/Combobox/Combobox.tsx
deleted file mode 100644
index 7cabfc50075..00000000000
--- a/packages/design-system/src/components/WIP/Combobox/Combobox.tsx
+++ /dev/null
@@ -1,41 +0,0 @@
-import { I18N_DOMAIN_DESIGN_SYSTEM } from '../../constants';
-import styles from './Combobox.module.scss';
-import { useTranslation } from 'react-i18next';
-import {
- unstable_useComboboxState as useReakitComboboxState,
- unstable_Combobox as ReakitCombobox,
- unstable_ComboboxPopover as ReakitComboboxPopover,
- unstable_ComboboxOption as ReakitComboboxOption,
-} from 'reakit';
-
-export type ComboboxProps = {
- values?: string[];
- initialValue?: string;
-};
-
-const Combobox = ({ values, ...rest }: ComboboxProps) => {
- const { t } = useTranslation(I18N_DOMAIN_DESIGN_SYSTEM);
-
- const combobox = useReakitComboboxState({
- autoSelect: true,
- inline: true,
- list: true,
- gutter: 8,
- values,
- });
-
- return (
-
-
-
- {combobox.matches.length
- ? combobox.matches.map(match => (
-
- ))
- : t('COMBOBOX_NOT_RESULT', 'No results found')}
-
-
- );
-};
-
-export default Combobox;
diff --git a/packages/design-system/src/components/WIP/Combobox/index.ts b/packages/design-system/src/components/WIP/Combobox/index.ts
deleted file mode 100644
index 3ed5124aae7..00000000000
--- a/packages/design-system/src/components/WIP/Combobox/index.ts
+++ /dev/null
@@ -1,3 +0,0 @@
-import Combobox from './Combobox';
-export * from './Combobox';
-export default Combobox;
diff --git a/packages/design-system/src/components/WIP/Drawer/variants/FloatingDrawer/FloatingDrawer.tsx b/packages/design-system/src/components/WIP/Drawer/variants/FloatingDrawer/FloatingDrawer.tsx
deleted file mode 100644
index 077dd24349b..00000000000
--- a/packages/design-system/src/components/WIP/Drawer/variants/FloatingDrawer/FloatingDrawer.tsx
+++ /dev/null
@@ -1,71 +0,0 @@
-import { useEffect, useRef, cloneElement } from 'react';
-import type { ReactElement, ReactNode } from 'react';
-import { Dialog, DialogDisclosure, DialogStateReturn, useDialogState } from 'reakit';
-import { PrimitiveDrawer } from '../../Primitive/PrimitiveDrawer';
-
-import theme from './FloatingDrawer.module.scss';
-
-type WithDisclosure = {
- disclosure: ReactElement;
- visible?: never;
-};
-type Controlled = {
- disclosure?: never;
- visible: boolean;
-};
-
-export type FloatingDrawerProps = {
- header?: ((dialog: DialogStateReturn) => ReactNode) | ReactNode;
- children: ((dialog: DialogStateReturn) => ReactNode) | ReactNode;
- footer?: ((dialog: DialogStateReturn) => ReactNode) | ReactNode;
- onClose?: () => void;
-} & (WithDisclosure | Controlled);
-// backward compatibility
-export type DrawerProps = FloatingDrawerProps;
-
-export const FloatingDrawer = ({
- disclosure,
- header,
- children,
- footer,
- visible: visibleProps,
- onClose,
-}: DrawerProps) => {
- const ref = useRef(null);
- const dialog = useDialogState({ modal: false, visible: visibleProps ?? false, animated: true });
-
- useEffect(() => {
- if (visibleProps !== undefined) {
- dialog.setVisible(visibleProps);
- }
- }, [visibleProps, dialog]);
-
- const onCloseHandler = disclosure ? () => dialog.setVisible(false) : () => onClose && onClose();
-
- return (
- <>
- {disclosure && (
-
- {disclosureProps => cloneElement(disclosure, disclosureProps)}
-
- )}
-
-
- {typeof children === 'function' ? children(dialog) : children}
-
-
- >
- );
-};
-
-FloatingDrawer.displayName = 'FloatingDrawer';
diff --git a/packages/design-system/src/components/WIP/Popover/Popover.module.scss b/packages/design-system/src/components/WIP/Popover/Popover.module.scss
deleted file mode 100644
index f55b122bed0..00000000000
--- a/packages/design-system/src/components/WIP/Popover/Popover.module.scss
+++ /dev/null
@@ -1,29 +0,0 @@
-@use '~@talend/design-tokens/lib/tokens';
-
-.popover {
- &__arrow {
- [class='fill'] {
- fill: tokens.$coral-color-neutral-background;
- }
-
- [class='stroke'] {
- fill: tokens.$coral-color-illustration-shadow;
- }
- }
-
- &__animated {
- background-color: tokens.$coral-color-neutral-background;
- transition: opacity tokens.$coral-transition-fast;
- box-shadow: tokens.$coral-elevation-shadow-neutral-m;
- border-radius: tokens.$coral-radius-s;
- opacity: 0;
-
- &_withPadding {
- padding: tokens.$coral-spacing-m;
- }
-
- [data-enter] & {
- opacity: 1;
- }
- }
-}
diff --git a/packages/design-system/src/components/WIP/Popover/Popover.tsx b/packages/design-system/src/components/WIP/Popover/Popover.tsx
deleted file mode 100644
index 33e65449182..00000000000
--- a/packages/design-system/src/components/WIP/Popover/Popover.tsx
+++ /dev/null
@@ -1,95 +0,0 @@
-import { cloneElement, HTMLAttributes, ReactElement, ReactNode, RefObject } from 'react';
-import classnames from 'classnames';
-import tokens from '@talend/design-tokens';
-import {
- Popover as ReakitPopover,
- PopoverArrow as ReakitPopoverArrow,
- PopoverDisclosure as ReakitPopoverDisclosure,
- PopoverDisclosureHTMLProps,
- PopoverProps,
- PopoverStateReturn,
- usePopoverState,
-} from 'reakit';
-import Clickable from '../../Clickable';
-import { Placement } from '../../Tooltip/Tooltip';
-import { DataAttributes } from '../../../types';
-
-import style from './Popover.module.scss';
-
-const ANIMATION_DURATION = 150; // Sync with @talend/design-token animations duration
-
-export type PopoverChildren = ReactNode | ((popover: PopoverStateReturn) => ReactNode);
-export type PopoverDisclosure =
- | ReactElement
- | ((disclosureProps: PopoverDisclosureHTMLProps) => ReactNode);
-
-export type PopoverPropsType = HTMLAttributes & {
- disclosure: PopoverDisclosure;
- children: PopoverChildren | PopoverChildren[];
- position?: Placement;
- zIndex?: string | number;
- isFixed?: boolean;
- hasPadding?: boolean;
- focusOnDisclosure?: boolean;
-} & DataAttributes;
-
-function Popover({
- disclosure,
- position = 'auto',
- zIndex = tokens.coralElevationLayerStandardFront,
- isFixed = false,
- hasPadding = true,
- focusOnDisclosure = false,
- ...props
-}: PopoverPropsType) {
- const popover = usePopoverState({
- animated: ANIMATION_DURATION,
- placement: position,
- unstable_fixed: isFixed,
- });
- const children = Array.isArray(props.children) ? props.children : [props.children];
- const disclosureElementProps = typeof disclosure !== 'function' ? disclosure.props : {};
- const basePopoverProps: Partial = {};
- if (focusOnDisclosure) {
- basePopoverProps.unstable_initialFocusRef =
- popover.unstable_referenceRef as RefObject;
- basePopoverProps.unstable_finalFocusRef = popover.unstable_popoverRef as RefObject;
- }
-
- return (
- <>
-
- {disclosureProps => {
- if (typeof disclosure === 'function') {
- return disclosure(disclosureProps);
- }
- return cloneElement(disclosure, disclosureProps);
- }}
-
-
-
-
- {children.map((child, index) => {
- if (typeof child === 'function') {
- return
{child(popover)}
;
- }
- return
{child}
;
- })}
-
-
- >
- );
-}
-
-export default Popover;
diff --git a/packages/design-system/src/components/WIP/Popover/index.ts b/packages/design-system/src/components/WIP/Popover/index.ts
deleted file mode 100644
index 497a8666bb9..00000000000
--- a/packages/design-system/src/components/WIP/Popover/index.ts
+++ /dev/null
@@ -1,3 +0,0 @@
-import Popover from './Popover';
-
-export default Popover;
diff --git a/packages/design-system/src/components/WIP/Tabs/Primitive/Tab.tsx b/packages/design-system/src/components/WIP/Tabs/Primitive/Tab.tsx
deleted file mode 100644
index 420f62982d6..00000000000
--- a/packages/design-system/src/components/WIP/Tabs/Primitive/Tab.tsx
+++ /dev/null
@@ -1,82 +0,0 @@
-import { forwardRef, HTMLAttributes, Ref } from 'react';
-import { Tab as ReakitTab, TabState } from 'reakit';
-import classnames from 'classnames';
-// eslint-disable-next-line @talend/import-depth
-import { IconNameWithSize } from '@talend/icons/dist/typeUtils';
-import { DataAttributes } from '../../../../types';
-import { StackHorizontal } from '../../../Stack';
-import { SizedIcon } from '../../../Icon';
-
-import styles from './TabStyles.module.scss';
-import Tooltip from '../../../Tooltip';
-import { TagDefault } from '../../../Tag';
-
-type TabChildren =
- | {
- title: string;
- icon?: IconNameWithSize<'S'>;
- tag?: string | number;
- }
- | { children: string };
-
-type TabSize = {
- size?: 'M' | 'L';
-};
-
-type TabTooltip = {
- tooltip?: string;
-};
-
-export type TabPropsTypesWithoutState = DataAttributes &
- Omit, 'className' | 'style' | 'type'> &
- TabSize &
- TabTooltip &
- TabChildren;
-
-type TabPropsTypes = TabPropsTypesWithoutState & TabState;
-
-const Tab = forwardRef((props: TabPropsTypes, ref: Ref) => {
- const { tooltip, ...otherProps } = props;
-
- const component = () => {
- if ('children' in otherProps) {
- const { children, size, ...rest } = otherProps;
- return (
-
- {children}
-
- );
- }
-
- const { icon, title, tag, size, ...rest } = otherProps;
- return (
-
-
- {icon && }
- {title}
- {tag && {tag} }
-
-
- );
- };
-
- if (tooltip) {
- return {component()} ;
- }
-
- return component();
-});
-
-Tab.displayName = 'Tab';
-
-export default Tab;
diff --git a/packages/design-system/src/components/WIP/Tabs/Primitive/TabAsLink.tsx b/packages/design-system/src/components/WIP/Tabs/Primitive/TabAsLink.tsx
deleted file mode 100644
index a92d8d0da07..00000000000
--- a/packages/design-system/src/components/WIP/Tabs/Primitive/TabAsLink.tsx
+++ /dev/null
@@ -1,61 +0,0 @@
-import { forwardRef, ReactElement, Ref } from 'react';
-import classnames from 'classnames';
-// eslint-disable-next-line @talend/import-depth
-import { IconNameWithSize } from '@talend/icons/dist/typeUtils';
-import { DataAttributes } from '../../../../types';
-import { StackHorizontal } from '../../../Stack';
-import { SizedIcon } from '../../../Icon';
-
-import styles from './TabStyles.module.scss';
-import Tooltip from '../../../Tooltip';
-import { TagDefault } from '../../../Tag';
-import Linkable, { LinkableType } from '../../../Linkable';
-
-type TabChildren = Omit & {
- title: string;
- icon?: IconNameWithSize<'S'>;
- tag?: string | number;
- size?: 'M' | 'L';
- isActive?: boolean;
-} & ({ tooltip?: string; as?: never } | { tooltip?: never; as?: ReactElement });
-
-export type TabAsLinkProps = DataAttributes & TabChildren;
-
-const TabComponent = forwardRef(
- (props: Omit, ref: Ref) => {
- const { icon, title, tag, size, isActive, as = 'a', ...rest } = props;
- return (
-
-
- {icon &&