diff --git a/.changeset/early-files-float.md b/.changeset/early-files-float.md new file mode 100644 index 0000000000..6abb6df4d6 --- /dev/null +++ b/.changeset/early-files-float.md @@ -0,0 +1,5 @@ +--- +'@talend/react-datagrid': minor +--- + +feat: use @talend/assets-api to load datagrid umds on demand diff --git a/.changeset/fifty-camels-call.md b/.changeset/fifty-camels-call.md new file mode 100644 index 0000000000..9b35b60fa4 --- /dev/null +++ b/.changeset/fifty-camels-call.md @@ -0,0 +1,5 @@ +--- +'@talend/scripts-config-react-webpack': minor +--- + +feat: add CDN_URL to /cdn by default diff --git a/.changeset/rich-swans-lie.md b/.changeset/rich-swans-lie.md new file mode 100644 index 0000000000..1e45a27434 --- /dev/null +++ b/.changeset/rich-swans-lie.md @@ -0,0 +1,6 @@ +--- +'@talend/design-system': minor +--- + +feat: use @talend/assets-api to load icons + diff --git a/.changeset/thirty-terms-return.md b/.changeset/thirty-terms-return.md new file mode 100644 index 0000000000..80bb564738 --- /dev/null +++ b/.changeset/thirty-terms-return.md @@ -0,0 +1,5 @@ +--- +'@talend/react-forms': minor +--- + +Use @talend/assets-api to load aceeditor diff --git a/.changeset/tiny-carrots-clean.md b/.changeset/tiny-carrots-clean.md new file mode 100644 index 0000000000..5aa2ee67f5 --- /dev/null +++ b/.changeset/tiny-carrots-clean.md @@ -0,0 +1,5 @@ +--- +'@talend/react-dataviz': minor +--- + +feat: Use @talend/assets-api to load topologies. diff --git a/.github/workflows/design-system-component-testing.yml b/.github/workflows/design-system-component-testing.yml index cf2ae14672..eadb19b96f 100644 --- a/.github/workflows/design-system-component-testing.yml +++ b/.github/workflows/design-system-component-testing.yml @@ -37,6 +37,7 @@ jobs: - name: Build @talend/design-tokens run: | + yarn workspace @talend/assets-api run build:lib yarn workspace @talend/design-tokens run build:lib - name: Cypress Component Testing diff --git a/.github/workflows/design-system-deploy.yml b/.github/workflows/design-system-deploy.yml index b42e55512e..8daec9eada 100644 --- a/.github/workflows/design-system-deploy.yml +++ b/.github/workflows/design-system-deploy.yml @@ -29,6 +29,7 @@ jobs: - name: Build Storybook of the design tokens run: | + yarn workspace @talend/assets-api run build:lib yarn workspace @talend/design-tokens run build:lib yarn workspace @talend/design-tokens run build-storybook diff --git a/.github/workflows/visual-testing.yml b/.github/workflows/visual-testing.yml index 1890a74fcc..8398017bae 100644 --- a/.github/workflows/visual-testing.yml +++ b/.github/workflows/visual-testing.yml @@ -62,6 +62,7 @@ jobs: - name: Build @talend/design-tokens if: matrix.package == 'design-system' run: | + yarn workspace @talend/assets-api run build:lib yarn workspace @talend/design-tokens run build:lib - name: Build @talend/* diff --git a/.prettierignore b/.prettierignore index e6846f6b70..da1cc32ff3 100644 --- a/.prettierignore +++ b/.prettierignore @@ -6,3 +6,4 @@ build dist node_modules +__fixtures__ diff --git a/packages/assets-api/README.md b/packages/assets-api/README.md new file mode 100644 index 0000000000..1407cb934c --- /dev/null +++ b/packages/assets-api/README.md @@ -0,0 +1,74 @@ +# @talend/assets-api + +Assets are files from a npm package which can be needed at some point by a web application. It can be a stylesheet, a SVG icon, a js script, a json file, etc... + +At Talend our web applications may rely on a CDN to load libraries like lodash, d3 but also @talend/design-system using UMD distribution format. An application rely on a CDN depending of the execution context; We are following 12 factors principals. So the same code base must work in both case so an asset url may change from `/cdn/my-package/1.2.3/dist/my-assets.svg` to `https://mycdn.talend.com/my-package/1.2.3/dist/my-assets.svg`. + +This is made possible using custom configuration of `@talend/dynamic-cdn-webpack-plugin`. Now we also need to be able to load translations, icons and more **assets** from the code. + +This package exposes a simple and friendly API to let developers access assets without the complexity of computing URL depending on the context (CDN or not). + +## Learn with examples + +First you need to ensure you have setup the [babel plugin](https://npmjs.com/package/@talend/babel-plugin-assets-api) or you use @talend/scripts-core to build your lib / project. + +Then we can start with this global example: + +```javascript +import assetsAPI from '@talend/assets-api'; + +// The lowest level API is the ability to get the URL of anything. Here, a CSS file. +const href = assetsApi.getURL('/dist/styles/ag-grid.css', 'ag-grid-community'); +// babel will add all missing arguments and at runtime you will have +console.log(href); +// -> 'https://unpkg.com/ag-grid-community@25.0.0/dist/styles/ag-grid.css'; + +// Higher level APIs enable users to get a JSON file for locales, timezones, etc... +async function getTopology(file) { + const locales = await assetsAPI.getJSON( + `/dist/assets/maps/${file}.topo.json`, + ); +} + + +// We can also lazy load a component from a UMD. +// This one is a bit more complex. You have to know that React.lazy wants a default esModule from a Promise. This is what getUMD + toDefaultModule give you. +const AgGridReact = React.lazy(() => + assetsApi + .getUMD('ag-grid-community') + .then(() => assetsApi.getUMD('ag-grid-react')) + .then(mod => assetsApi.toDefaultModule(mod.AgGridReact)) +); + + +// Finally, this is how we load styles from a lazy loaded component. +export default function DataGrid(props) { + //... + useEffect(() => { + const href = assetsApi.getURL('/dist/styles/ag-grid.css', 'ag-grid-community'); + assetsApi.addStyle({ href }); + }, []); + //... + return AgGridReact +} +``` + +## Requirements + +- devs do not have to write the version: it is injected at build time thanks to [babel plugin](https://npmjs.com/package/@talend/babel-plugin-assets-api) +- the assets' version is implicitly specified by the consumer webapp (`talend-scripts` adds global data that is then read by the API at runtime) +- the inject.js script will be able to control this version (it should update meta value) +- `sessionStorage` is used to let anyone override a version locally +- the API is compatible with `React.Suspense` / lazy React APIs +- for UMDs, the path is computed form `module-to-cdn` and injected at build time thanks to the babel plugin +- for relative paths in a package, the name of the package is optional + +## How to configure the CDN to use ? + +The assets API uses a global function to compute the URL: `window.Talend.getCDNUrl()`. By default the package will add it for you. This version will use a global `CDN_URL` with the following shape: + +```javascript +`${CDN_URL}/${info.name}/${info.version}${info.path}`; +``` + +but fallbacks to unpkg.com public CDN if no CDN_URL is provided. diff --git a/packages/assets-api/package.json b/packages/assets-api/package.json new file mode 100644 index 0000000000..a69e6febbe --- /dev/null +++ b/packages/assets-api/package.json @@ -0,0 +1,41 @@ +{ + "name": "@talend/assets-api", + "description": "A set of API designed to access assets using CDN", + "types": "lib/index.d.ts", + "main": "lib/index.js", + "mainSrc": "src/index.ts", + "license": "Apache-2.0", + "scripts": { + "pre-release": "echo no pre-release for assets-api", + "build:lib": "talend-scripts build:ts:lib", + "start": "echo nothing to start", + "test": "talend-scripts test", + "test:watch": "talend-scripts test --watch", + "test:cov": "talend-scripts test --coverage", + "test:demo": "echo nothing to demo", + "lint:es": "talend-scripts lint:es --format json -o eslint-report.json", + "lint": "talend-scripts lint:es --format json -o eslint-report.json" + }, + "keywords": [ + "talend" + ], + "author": "Talend Frontend ", + "homepage": "https://github.com/Talend/ui/tree/master/packages/assets-api#readme", + "bugs": { + "url": "https://github.com/Talend/ui/issues" + }, + "repository": { + "type": "git", + "url": "https://github.com/Talend/ui.git" + }, + "dependencies": {}, + "devDependencies": { + "@talend/scripts-core": "^11.6.0", + "@talend/scripts-preset-react-lib": "^11.0.2", + "read-pkg-up": "^7.0.1" + }, + "publishConfig": { + "access": "public" + }, + "version": "1.0.0" +} diff --git a/packages/assets-api/src/index.test.ts b/packages/assets-api/src/index.test.ts new file mode 100644 index 0000000000..725f37c13a --- /dev/null +++ b/packages/assets-api/src/index.test.ts @@ -0,0 +1,33 @@ +import readPackageUp from 'read-pkg-up'; +import assetsApi, { Asset } from '.'; + +const iconsInfo = readPackageUp.sync({ cwd: require.resolve('@talend/icons') }); +const currentInfo = readPackageUp.sync({ cwd: __dirname }); + +describe('assets-api', () => { + describe('getURL', () => { + it('should return unpkg url', () => { + const assetsPath = '/dist/svg-bundles/all.svg'; + const url = assetsApi.getURL(assetsPath, '@talend/icons', iconsInfo?.packageJson.version); + expect(url).toBe( + `https://unpkg.com/@talend/icons@${iconsInfo?.packageJson.version}${assetsPath}`, + ); + }); + it('should use global getCDNUrl', () => { + const assetsPath = '/package.json'; + const original = window.Talend.getCDNUrl; + window.Talend.getCDNUrl = jest.fn( + (info: Asset) => `https://mycdn.talend.com/${info.name}/${info.version}${info.path}`, + ); + const url = assetsApi.getURL( + assetsPath, + '@talend/assets-api', + currentInfo?.packageJson.version, + ); + expect(url).toBe( + `https://mycdn.talend.com/@talend/assets-api/${currentInfo?.packageJson.version}${assetsPath}`, + ); + window.Talend.getCDNUrl = original; + }); + }); +}); diff --git a/packages/assets-api/src/index.ts b/packages/assets-api/src/index.ts new file mode 100644 index 0000000000..6e10ce5c7e --- /dev/null +++ b/packages/assets-api/src/index.ts @@ -0,0 +1,205 @@ +/* eslint-disable no-console */ +declare global { + interface Window { + Talend: any; + } +} + +export interface Asset { + name: string; + version: string; + path: string; +} +export interface Script { + src: string; + integrity?: string; + onload?: () => void; + onerror?: (e: Error) => void; +} +export interface StyleAsset { + href: string; + integrity: string; +} + +export interface TypedResponse extends Response { + /** + * this will override `json` method from `Body` that is extended by `Response` + * interface Body { + * json(): Promise; + * } + */ + json

(): Promise

; +} + +/** + * + * Most of the parameters are optional but in fact they are not optional. + * They are optional for dev experience because injected by a babel plugin + */ + +function getPackageVersion(name?: string, version?: string): string | undefined { + if (name) { + const sessionVersion = sessionStorage.getItem(name); + if (sessionVersion) { + return sessionVersion; + } + + const metas = document.querySelectorAll(`meta[name="${name}"]`); + if (metas.length > 1) { + console.warn(`Package ${name} is installed multiple times`); + } + if (metas.length > 0) { + return metas[0].getAttribute('value') || version; + } + } + return version; +} + +function getURL(path: string, name?: string, version?: string) { + const overriddenVersion = getPackageVersion(name, version); + if (!overriddenVersion) { + throw new Error(`Version not found for ${name}`); + } + return window.Talend.getCDNUrl({ name, version: overriddenVersion, path }); +} + +const TIMEOUT = 10000; + +function addScript({ src, integrity, ...attr }: Script) { + const found = !!document.querySelector(`script[src="${src}"]`); + if (found) { + return; + } + const script = document.createElement('script'); + script.setAttribute('src', src); + script.setAttribute('type', 'text/javascript'); + script.setAttribute('crossorigin', 'anonymous'); + if (integrity) { + script.setAttribute('integrity', integrity); + } + script.async = false; + Object.assign(script, attr); + document.body.appendChild(script); +} + +function toDefaultModule(value: any) { + return Object.create(null, { + default: { + value, + enumerable: true, + }, + __esModule: { + value: true, + }, + [Symbol.toStringTag]: { + value: 'Module', + }, + }); +} + +function getUMD(name: string, version?: string, varName?: string, path?: string) { + const cache = { resolved: false }; + function loaded() { + if (!varName) { + return false; + } + return !!(window as any)[varName]; + } + if (loaded() && varName) { + return Promise.resolve((window as any)[varName]); + } + const src = getURL(path || '/undefined', name, version); + console.log('getUMD', src, varName); + + return new Promise((resolve, reject) => { + function onload() { + if (!varName) { + cache.resolved = true; + resolve(undefined); + } else { + console.log(`${varName} onload ok`); + } + } + function onerror(e: Error) { + console.error(e); + reject(e); + } + addScript({ src, onload, onerror }); + if (varName) { + const intervalId = setInterval(() => { + if (loaded()) { + cache.resolved = true; + clearInterval(intervalId); + resolve((window as any)[varName]); + } + }, 200); + setTimeout(() => { + if (!cache.resolved) { + clearInterval(intervalId); + reject(new Error(`UMD from ${src}, ${varName} not found in ${TIMEOUT}`)); + } + }, TIMEOUT); + } + }); +} + +async function getJSON(path: string, name?: string, version?: string) { + const url = getURL(path, name, version); + const response: TypedResponse = await fetch(url); + if (response.ok) { + return response.json(); + } + console.error(`Response not ok: ${response.status} ${response.statusText} from ${url}`); + + return undefined; +} + +function addStyle({ href, integrity, ...attr }: StyleAsset) { + const found = !!document.querySelector(`link[href="${href}"]`); + if (found) { + return; + } + const style = document.createElement('link'); + style.setAttribute('rel', 'stylesheet'); + style.setAttribute('media', 'print'); + style.setAttribute('onload', 'this.media="all"'); + if (integrity) { + style.setAttribute('integrity', integrity); + style.setAttribute('crossorigin', 'anonymous'); + } + style.setAttribute('href', href); + Object.assign(style, attr); + const title = document.getElementsByTagName('TITLE')[0]; + document.head.insertBefore(style, title); +} + +if (!window.Talend) { + window.Talend = {}; +} +if (!window.Talend.getCDNUrl) { + // eslint-disable-next-line no-console + console.log('assets.api add window.Talend.getCDNUrl'); + window.Talend.getCDNUrl = (info: Asset) => { + const CDN_URL = window.Talend.CDN_URL; + if (CDN_URL) { + if (CDN_URL.startsWith('/')) { + const baseTag = document.querySelector('base'); + if (baseTag) { + const root = baseTag.getAttribute('href') || ''; + return `${root}${CDN_URL}/${info.name}/${info.version}${info.path}`; + } + } + return `${CDN_URL}/${info.name}/${info.version}${info.path}`; + } + return `https://unpkg.com/${info.name}@${info.version}${info.path}`; + }; +} + +export default { + getURL, + getUMD, + getJSON, + addScript, + addStyle, + toDefaultModule, +}; diff --git a/packages/assets-api/talend-scripts.json b/packages/assets-api/talend-scripts.json new file mode 100644 index 0000000000..4d78bcf08f --- /dev/null +++ b/packages/assets-api/talend-scripts.json @@ -0,0 +1,3 @@ +{ + "preset": "@talend/scripts-preset-react-lib" +} diff --git a/packages/assets-api/tsconfig.json b/packages/assets-api/tsconfig.json new file mode 100644 index 0000000000..1c67d95f42 --- /dev/null +++ b/packages/assets-api/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "@talend/scripts-config-typescript/tsconfig.json", + "include": ["src/**/*"], + "compilerOptions": { + "declaration": true + } +} diff --git a/packages/components/src/FilterBar/FilterBar.test.js b/packages/components/src/FilterBar/FilterBar.test.js index 9053c3fa54..f3ea952658 100644 --- a/packages/components/src/FilterBar/FilterBar.test.js +++ b/packages/components/src/FilterBar/FilterBar.test.js @@ -250,7 +250,7 @@ describe('Filter', () => { setTimeout(() => { expect(onFilter).toBeCalledWith(expect.anything(), 'couc'); done(); - }, debounceTimeout); + }, debounceTimeout + 100); }); it('should clear filter on clear button click', () => { diff --git a/packages/datagrid/.babelrc.json b/packages/datagrid/.babelrc.json deleted file mode 100644 index 1d6655372a..0000000000 --- a/packages/datagrid/.babelrc.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "extends": "@talend/scripts-config-babel/.babelrc.json" -} diff --git a/packages/datagrid/package.json b/packages/datagrid/package.json index 4427de4f26..155cee6d50 100644 --- a/packages/datagrid/package.json +++ b/packages/datagrid/package.json @@ -12,8 +12,7 @@ "build:lib": "talend-scripts build:ts:lib", "build-storybook": "talend-scripts build-storybook", "start": "talend-scripts start-storybook -p 6010", - "test": "talend-scripts test --silent", - "test:noisy": "talend-scripts test", + "test": "talend-scripts test", "test:watch": "talend-scripts test --watch", "test:cov": "talend-scripts test --coverage", "test:demo": "talend-scripts build-storybook --quiet", @@ -37,6 +36,7 @@ "url": "https://github.com/Talend/ui.git" }, "dependencies": { + "@talend/assets-api": "^1.0.0", "@talend/icons": "^6.40.0", "@talend/react-cmf": "^7.0.2", "@talend/react-components": "^6.45.1", @@ -53,6 +53,8 @@ "@talend/react-cmf": "^7.0.2", "@talend/scripts-core": "^11.6.0", "@talend/scripts-preset-react-lib": "^11.0.2", + "@testing-library/react": "^12.1.5", + "@testing-library/jest-dom": "^5.16.4", "enzyme": "^3.11.0", "i18next": "^20.6.1", "immutable": "^3.8.2", diff --git a/packages/datagrid/src/components/DataGrid/DataGrid.component.js b/packages/datagrid/src/components/DataGrid/DataGrid.component.js index 14ced7fc85..7b053b4365 100644 --- a/packages/datagrid/src/components/DataGrid/DataGrid.component.js +++ b/packages/datagrid/src/components/DataGrid/DataGrid.component.js @@ -1,11 +1,10 @@ +/* eslint-disable react/sort-comp */ import React from 'react'; import classNames from 'classnames'; import keycode from 'keycode'; -import { AgGridReact } from 'ag-grid-react'; -import Inject from '@talend/react-components/lib/Inject'; +import assetsApi from '@talend/assets-api'; +import { Inject } from '@talend/react-components'; import { Icon } from '@talend/design-system'; -import 'ag-grid-community/dist/styles/ag-grid.css'; - import DefaultHeaderRenderer, { HEADER_RENDERER_COMPONENT } from '../DefaultHeaderRenderer'; import DefaultCellRenderer, { CELL_RENDERER_COMPONENT } from '../DefaultCellRenderer'; import DefaultPinHeaderRenderer, { @@ -17,6 +16,13 @@ import { NAMESPACE_INDEX } from '../../constants'; import serializer from '../DatasetSerializer'; import theme from './DataGrid.scss'; +const AgGridReact = React.lazy(() => + assetsApi + .getUMD('ag-grid-community') + .then(() => assetsApi.getUMD('ag-grid-react')) + .then(mod => assetsApi.toDefaultModule(mod.AgGridReact)), +); + export const AG_GRID = { CUSTOM_HEADER_KEY: 'headerComponent', CUSTOM_CELL_KEY: 'cellRenderer', @@ -94,6 +100,11 @@ export default class DataGrid extends React.Component { this.currentColId = null; } + componentDidMount() { + const href = assetsApi.getURL('/dist/styles/ag-grid.css', 'ag-grid-community'); + assetsApi.addStyle({ href }); + } + /** * componentDidUpdate - call forceRedrawRows after props changes to redraw or * not the grid @@ -292,7 +303,6 @@ export default class DataGrid extends React.Component { DefaultPinHeaderRenderer, ), }; - return agGridOptions; } @@ -336,7 +346,11 @@ export default class DataGrid extends React.Component { ); } else { - content = ; + content = ( + }> + + + ); } return ( diff --git a/packages/datagrid/src/components/DataGrid/DataGrid.test.js b/packages/datagrid/src/components/DataGrid/DataGrid.test.js index 821f1a11b9..316c8b83cb 100644 --- a/packages/datagrid/src/components/DataGrid/DataGrid.test.js +++ b/packages/datagrid/src/components/DataGrid/DataGrid.test.js @@ -1,17 +1,10 @@ import React from 'react'; -import keycode from 'keycode'; -import { shallow } from 'enzyme'; -import { JSDOM } from 'jsdom'; - -import { NAMESPACE_DATA, NAMESPACE_INDEX } from '../../constants'; - -import DataGrid, { injectHeaderRenderer, AG_GRID } from './DataGrid.component'; - -function PinHeaderRenderer() {} - -function getComponent() { - return PinHeaderRenderer; -} +import ReactDOM from 'react-dom'; +import { Grid as agGrid } from 'ag-grid-community'; +import { AgGridReact, AgGridColumn } from 'ag-grid-react'; +import { render, waitFor, screen } from '@testing-library/react'; +import '@testing-library/jest-dom'; +import DataGrid from './DataGrid.component'; const sample = { schema: { @@ -157,697 +150,25 @@ const sample = { }, }; -jest.mock('react-dom', () => ({ - findDOMNode(mockRef) { - return mockRef; - }, -})); +jest.mock('ally.js'); describe('#DataGrid', () => { - it('should render DataGrid', () => { - const wrapper = shallow(); - expect(wrapper.getElement()).toMatchSnapshot(); - }); - - it('should not call forceRedrawRows when the DataGrid is loading', () => { - const forceRedrawRows = jest.fn(() => true); - const wrapper = shallow( - , - ); - - wrapper.instance().componentDidUpdate({}); - expect(forceRedrawRows).not.toHaveBeenCalled(); - }); - - it('should not call forceRedrawRows when the DataGrid is not ready', () => { - const forceRedrawRows = jest.fn(() => true); - const wrapper = shallow( - , - ); - - wrapper.instance().componentDidUpdate({}); - expect(forceRedrawRows).not.toHaveBeenCalled(); - }); - - it('should call redrawRows when forceRedrawRows return true', () => { - const forceRedrawRows = jest.fn(() => true); - const redrawRows = jest.fn(); - const wrapper = shallow( - , - ); - - wrapper.instance().onGridReady({ - api: { - redrawRows, - }, - }); - wrapper.instance().componentDidUpdate({}); - - expect(forceRedrawRows).toHaveBeenCalled(); - expect(redrawRows).toHaveBeenCalled(); - }); - - it('should not call redrawRows when forceRedrawRows return false', () => { - const forceRedrawRows = jest.fn(() => false); - const redrawRows = jest.fn(); - const wrapper = shallow( - , - ); - - wrapper.instance().onGridReady({ - api: { - redrawRows, - }, - }); - wrapper.instance().componentDidUpdate({}); - - expect(forceRedrawRows).toHaveBeenCalled(); - expect(redrawRows).not.toHaveBeenCalled(); - }); - - it('should render DataGrid with columnsDefs and rowData', () => { - const wrapper = shallow(); - - expect(wrapper.getElement()).toMatchSnapshot(); - }); - - it('should render DataGrid with custom serializer', () => { - const getPinnedColumnDefsFn = jest.fn(); - const getColumnDefsFn = jest.fn(); - const getRowDataFn = jest.fn(); - const getCellValueFn = jest.fn(); - - const props = { - columnsConf: {}, - data: sample, - getComponent, - getCellValueFn, - getColumnDefsFn, - getPinnedColumnDefsFn, - getRowDataFn, - }; - - shallow(); - expect(getColumnDefsFn).toHaveBeenCalledWith(sample, props.columnsConf); - expect(getPinnedColumnDefsFn).toHaveBeenCalledWith(sample); - expect(getRowDataFn).toHaveBeenCalledWith(sample, 0); - }); - - it('should render one Skeleton is InProgress', () => { - const wrapper = shallow(); - - expect(wrapper.getElement()).toMatchSnapshot(); - }); -}); - -describe('#AgGrid API', () => { - it('should set the AgGrid API when the ag-grid is loaded', () => { - const api = {}; - const wrapper = shallow(); - - wrapper.find('AgGridReact').props().onGridReady({ api }); - - expect(wrapper.instance().gridAPI).toBe(api); - }); -}); - -describe('#Datagrid method', () => { - it('should set the current grid ref', () => { - const element = {}; - const wrapper = shallow(); - - wrapper.instance().setGridInstance(element); - - expect(wrapper.instance().gridInstance).toBe(element); - }); - - it('should set the current selected column', () => { - const currentColumn = 'colId'; - const wrapper = shallow(); - - wrapper.instance().setCurrentFocusedColumn(currentColumn); - - expect(wrapper.instance().currentColId).toBe(currentColumn); - }); - - it('should remove the focus on the column', () => { - const { document } = new JSDOM( - '

', - ).window; - const gridElement = document.querySelector('.grid-element'); - const wrapper = shallow(); - wrapper.instance().setGridInstance({ - [AG_GRID.ELEMENT]: gridElement, - }); - - wrapper.instance().removeFocusColumn(); - - expect(gridElement.outerHTML).toBe( - '
', - ); - }); - - it('should set the focus on the column', () => { - const { document } = new JSDOM( - `
`, - ).window; - const gridElement = document.querySelector('.grid-element'); - const wrapper = shallow(); - const instance = wrapper.instance(); - instance.setGridInstance({ - [AG_GRID.ELEMENT]: gridElement, - }); - instance.currentColId = `${NAMESPACE_DATA}colId`; - - wrapper.instance().updateStyleFocusColumn(); - - expect(gridElement.outerHTML).toBe( - `
`, - ); - }); - - it('should not set the focus on the column when any defined column', () => { - const { document } = new JSDOM( - `
`, - ).window; - const gridElement = document.querySelector('.grid-element'); - const wrapper = shallow(); - const instance = wrapper.instance(); - instance.setGridInstance({ - [AG_GRID.ELEMENT]: gridElement, - }); - - wrapper.instance().updateStyleFocusColumn(); - - expect(gridElement.outerHTML).toBe( - `
`, - ); - }); - - it('should not set the focus on the column when the selected column is an pinned column', () => { - const { document } = new JSDOM( - `
`, - ).window; - const gridElement = document.querySelector('.grid-element'); - const wrapper = shallow(); - const instance = wrapper.instance(); - instance.setGridInstance({ - [AG_GRID.ELEMENT]: gridElement, - }); - instance.currentColId = `${NAMESPACE_INDEX}index`; - - wrapper.instance().updateStyleFocusColumn(); - - expect(gridElement.outerHTML).toBe( - `
`, - ); - }); - - it('should manage the cells with keyboard', () => { - const setSelected = jest.fn(); - const api = { - getDisplayedRowAtIndex() { - return { - setSelected, - }; - }, - }; - const wrapper = shallow(); - const nextCellPosition = { - rowIndex: 1, - }; - - const previousCellPosition = { - rowIndex: 2, - }; - - wrapper.find('AgGridReact').props().onGridReady({ api }); - - const nextFocusedCell = wrapper.instance().handleKeyboard({ - nextCellPosition, - previousCellPosition, - }); - - expect(nextFocusedCell).toBe(nextCellPosition); - expect(setSelected).toHaveBeenCalledWith(true, true); - }); - - it('should not manage the cells with keyboard if any nextCellPosition', () => { - const setSelected = jest.fn(); - const api = { - getDisplayedRowAtIndex() { - return { - setSelected, - }; - }, - }; - const wrapper = shallow(); - const nextCellPosition = null; - - const previousCellPosition = { - rowIndex: 2, - }; - - wrapper.find('AgGridReact').props().onGridReady({ api }); - - const nextFocusedCell = wrapper.instance().handleKeyboard({ - nextCellPosition, - previousCellPosition, - }); - - expect(nextFocusedCell).toBe(null); - expect(setSelected).not.toHaveBeenCalled(); - }); - - it('should not manage the cells with keyboard if any api', () => { - const setSelected = jest.fn(); - const api = null; - const wrapper = shallow(); - const nextCellPosition = { - rowIndex: 1, - }; - - const previousCellPosition = { - rowIndex: 2, - }; - - wrapper.find('AgGridReact').props().onGridReady({ api }); - - const nextFocusedCell = wrapper.instance().handleKeyboard({ - nextCellPosition, - previousCellPosition, - }); - - expect(nextFocusedCell).toBe(nextCellPosition); - expect(setSelected).not.toHaveBeenCalled(); - }); - - it('should set the current selected column from props', () => { - const wrapper = shallow(); - wrapper.instance().onFocusedColumn = jest.fn(); - wrapper.instance().onGridReady({ api: {} }); - - expect(wrapper.instance().onFocusedColumn).toHaveBeenCalledWith('field3'); - }); - - it('should update current selected column from props', () => { - const wrapper = shallow(); - const deselectAll = jest.fn(); - const clearFocusedCell = jest.fn(); - const ensureColumnVisible = jest.fn(); - const api = { - deselectAll, - clearFocusedCell, - ensureColumnVisible, - }; - const instance = wrapper.instance(); - instance.onGridReady({ api }); - instance.removeFocusColumn = jest.fn(); - instance.updateStyleFocusColumn = jest.fn(); - - wrapper.setProps({ - focusedColumnId: 'field2', - }); - - expect(instance.currentColId).toEqual('field2'); - }); - - it('should not update local state on click when in controlled mode', () => { - const wrapper = shallow(); - const deselectAll = jest.fn(); - const clearFocusedCell = jest.fn(); - const ensureColumnVisible = jest.fn(); - const api = { - deselectAll, - clearFocusedCell, - ensureColumnVisible, - }; - const instance = wrapper.instance(); - instance.removeFocusColumn = jest.fn(); - instance.updateStyleFocusColumn = jest.fn(); - instance.onGridReady({ api }); - - instance.onFocusedColumn('field4'); - - expect(instance.currentColId).toEqual(null); - }); - - it('should scroll to focused column', () => { - const deselectAll = jest.fn(); - const clearFocusedCell = jest.fn(); - const ensureColumnVisible = jest.fn(); - const api = { - deselectAll, - clearFocusedCell, - ensureColumnVisible, - }; - const wrapper = shallow(); - const instance = wrapper.instance(); - instance.setCurrentFocusedColumn = jest.fn(); - instance.setCurrentFocusedColumn = jest.fn(); - instance.removeFocusColumn = jest.fn(); - instance.updateStyleFocusColumn = jest.fn(); - instance.onGridReady({ - api, - }); - - expect(api.ensureColumnVisible).toHaveBeenCalled(); - }); - - it('should focus a column', () => { - const deselectAll = jest.fn(); - const clearFocusedCell = jest.fn(); - const api = { - deselectAll, - clearFocusedCell, - }; - const currentColId = 'colId'; - const onFocusedColumn = jest.fn(); - const wrapper = shallow( - , - ); - const instance = wrapper.instance(); - - instance.setCurrentFocusedColumn = jest.fn(); - instance.removeFocusColumn = jest.fn(); - instance.updateStyleFocusColumn = jest.fn(); - - wrapper.find('AgGridReact').props().onGridReady({ api }); - - instance.onFocusedColumn(currentColId); - - expect(instance.setCurrentFocusedColumn).toHaveBeenCalledWith(currentColId); - expect(instance.updateStyleFocusColumn).toHaveBeenCalled(); - expect(instance.removeFocusColumn).toHaveBeenCalled(); - expect(deselectAll).toHaveBeenCalled(); - expect(clearFocusedCell).toHaveBeenCalled(); - expect(onFocusedColumn).toHaveBeenCalledWith({ colId: currentColId }); - }); - - it('should focus a column when an another column is focused', () => { - const currentColId = 'colId'; - const focusedColId = 'colId2'; - const column = { - colId: focusedColId, - pinned: false, - }; - const onFocusedCell = jest.fn(); - const wrapper = shallow(); - const instance = wrapper.instance(); - instance.setCurrentFocusedColumn(currentColId); - - instance.setCurrentFocusedColumn = jest.fn(); - instance.removeFocusColumn = jest.fn(); - instance.updateStyleFocusColumn = jest.fn(); - - instance.onFocusedCell({ - column, - }); - - expect(instance.setCurrentFocusedColumn).toHaveBeenCalledWith(focusedColId); - expect(instance.updateStyleFocusColumn).toHaveBeenCalled(); - expect(instance.removeFocusColumn).toHaveBeenCalled(); - expect(onFocusedCell).toHaveBeenCalledWith({ column }); - }); - - it('should focus a column when an another column is focused and not call onFocusedCell if undefined', () => { - const currentColId = 'colId'; - const focusedColId = 'colId2'; - const column = { - colId: focusedColId, - pinned: false, - }; - const onFocusedCell = jest.fn(); - const wrapper = shallow(); - const instance = wrapper.instance(); - instance.setCurrentFocusedColumn(currentColId); - - instance.setCurrentFocusedColumn = jest.fn(); - instance.removeFocusColumn = jest.fn(); - instance.updateStyleFocusColumn = jest.fn(); - - instance.onFocusedCell({ - column, - }); - - expect(instance.setCurrentFocusedColumn).toHaveBeenCalledWith(focusedColId); - expect(instance.updateStyleFocusColumn).toHaveBeenCalled(); - expect(instance.removeFocusColumn).toHaveBeenCalled(); - expect(onFocusedCell).not.toHaveBeenCalled(); - }); - - it('should focus a column when the same column is focused without remove the previous style', () => { - const currentColId = 'colId'; - const column = { - colId: currentColId, - pinned: false, - }; - const onFocusedCell = jest.fn(); - const wrapper = shallow(); - const instance = wrapper.instance(); - instance.setCurrentFocusedColumn(currentColId); - - instance.setCurrentFocusedColumn = jest.fn(); - instance.removeFocusColumn = jest.fn(); - instance.updateStyleFocusColumn = jest.fn(); - - instance.onFocusedCell({ - column, - }); - - expect(instance.setCurrentFocusedColumn).toHaveBeenCalledWith(currentColId); - expect(instance.updateStyleFocusColumn).toHaveBeenCalled(); - expect(instance.removeFocusColumn).not.toHaveBeenCalled(); - expect(onFocusedCell).toHaveBeenCalledWith({ column }); - }); - - it('should not focus a column when there is no column', () => { - const column = null; - const onFocusedCell = jest.fn(); - const wrapper = shallow(); - const instance = wrapper.instance(); - - instance.setCurrentFocusedColumn = jest.fn(); - instance.removeFocusColumn = jest.fn(); - instance.updateStyleFocusColumn = jest.fn(); - - instance.onFocusedCell({ - column, - }); - - expect(instance.setCurrentFocusedColumn).not.toHaveBeenCalled(); - expect(instance.updateStyleFocusColumn).not.toHaveBeenCalled(); - expect(instance.removeFocusColumn).not.toHaveBeenCalled(); - expect(onFocusedCell).not.toHaveBeenCalledWith({ column }); - }); - - it('should not focus a column when the selected column is pinned', () => { - const currentColId = 'colId'; - const column = { - colId: currentColId, - pinned: true, - }; - const onFocusedCell = jest.fn(); - const wrapper = shallow(); - const instance = wrapper.instance(); - - instance.setCurrentFocusedColumn = jest.fn(); - instance.removeFocusColumn = jest.fn(); - instance.updateStyleFocusColumn = jest.fn(); - - instance.onFocusedCell({ - column, - }); - - expect(instance.setCurrentFocusedColumn).toHaveBeenCalledWith(currentColId); - expect(instance.updateStyleFocusColumn).not.toHaveBeenCalled(); - expect(instance.removeFocusColumn).toHaveBeenCalled(); - expect(onFocusedCell).not.toHaveBeenCalledWith({ column }); - }); - - it('should focus on the first cell when DOWN is triggered', () => { - const preventDefault = jest.fn(); - const setFocusedCell = jest.fn(); - const ensureIndexVisible = jest.fn(); - const api = { - setFocusedCell, - ensureIndexVisible, - }; - const colId = 'colId'; - const wrapper = shallow(); - const instance = wrapper.instance(); - - wrapper.find('AgGridReact').props().onGridReady({ api }); - - instance.onKeyDownHeaderColumn( - { - keyCode: keycode('down'), - preventDefault, - }, - colId, - ); - - expect(setFocusedCell).toHaveBeenCalledWith(0, colId); - expect(ensureIndexVisible).toHaveBeenCalledWith(0); - }); - - it('should not focus on the first cell when other key is triggered', () => { - const preventDefault = jest.fn(); - const setFocusedCell = jest.fn(); - const ensureIndexVisible = jest.fn(); - const colId = 'colId'; - const wrapper = shallow(); - const instance = wrapper.instance(); - - wrapper.find('AgGridReact').props().onGridReady({ - setFocusedCell, - ensureIndexVisible, - }); - - instance.onKeyDownHeaderColumn( - { - keyCode: keycode('up'), - preventDefault, - }, - colId, - ); - - expect(setFocusedCell).not.toHaveBeenCalledWith(0, colId); - expect(ensureIndexVisible).not.toHaveBeenCalledWith(0); - }); - - it('should trigger an event when the grid scroll vertical', () => { - const firstIndex = 0; - const lastIndex = 20; - const event = { - direction: AG_GRID.SCROLL_VERTICAL_DIRECTION, - }; - const api = { - getFirstDisplayedRow: () => firstIndex, - getLastDisplayedRow: () => lastIndex, - }; - const onVerticalScroll = jest.fn(); - const wrapper = shallow( - , - ); - const instance = wrapper.instance(); - - wrapper.find('AgGridReact').props().onGridReady({ - api, - }); - - instance.onBodyScroll(event); - - expect(onVerticalScroll).toHaveBeenCalledWith(event, { - firstDisplayedRowIndex: firstIndex, - lastDisplayedRowIndex: lastIndex, - }); - }); - - it('should not trigger an event when the grid scroll horizontal', () => { - const event = { - direction: 'horizontal', - }; - const onVerticalScroll = jest.fn(); - const wrapper = shallow( - , - ); - const instance = wrapper.instance(); - - instance.onBodyScroll(event); - - expect(onVerticalScroll).not.toHaveBeenCalled(); - }); -}); - -describe('#injectHeaderRenderer', () => { - const Component = () => {}; - const onFocusedColumn = jest.fn(); - const onKeyDown = jest.fn(); - - it('should injected the header renderer', () => { - const getCellComponent = jest.fn(() => Component); - const componentId = 'header'; - const InjectedComponent = injectHeaderRenderer( - getCellComponent, - 'header', - onFocusedColumn, - onKeyDown, - ); - - const wrapper = shallow(); - wrapper.props().onFocusedColumn(); - wrapper.props().onKeyDown(); - - expect(wrapper.props().myProps).toBe('myProps'); - expect(getCellComponent).toHaveBeenCalledWith(componentId); - expect(onFocusedColumn).toHaveBeenCalled(); - expect(onKeyDown).toHaveBeenCalled(); - }); - - it('should injected the default header renderer', () => { - const InjectedComponent = injectHeaderRenderer(null, 'header', onFocusedColumn); - - const wrapper = shallow(); - - expect(wrapper.find('DefaultHeaderRenderer').length).toBe(1); - }); -}); - -describe('#forceRedraw', () => { - it('should not call forceRedrawRows when the DataGrid is loading', () => { - const forceRedrawRows = jest.fn(() => true); - const wrapper = shallow( - , - ); - - wrapper.instance().componentDidUpdate({}); - expect(forceRedrawRows).not.toHaveBeenCalled(); - }); - - it('should not call forceRedrawRows when the DataGrid is not ready', () => { - const forceRedrawRows = jest.fn(() => true); - const wrapper = shallow( - , - ); - - wrapper.instance().componentDidUpdate({}); - expect(forceRedrawRows).not.toHaveBeenCalled(); - }); - - it('should call redrawRows when forceRedrawRows return true', () => { - const forceRedrawRows = jest.fn(() => true); - const redrawRows = jest.fn(); - const wrapper = shallow( - , - ); - - wrapper.instance().onGridReady({ - api: { - redrawRows, - }, - }); - wrapper.instance().componentDidUpdate({}); - - expect(forceRedrawRows).toHaveBeenCalled(); - expect(redrawRows).toHaveBeenCalled(); - }); - - it('should not call redrawRows when forceRedrawRows return false', () => { - const forceRedrawRows = jest.fn(() => false); - const redrawRows = jest.fn(); - const wrapper = shallow( - , - ); - - wrapper.instance().onGridReady({ - api: { - redrawRows, - }, - }); - wrapper.instance().componentDidUpdate({}); - - expect(forceRedrawRows).toHaveBeenCalled(); - expect(redrawRows).not.toHaveBeenCalled(); + it('should render DataGrid', async () => { + // add globals + window.React = React; + window.ReactDOM = ReactDOM; + window.agGrid = agGrid; + window.AgGridReact = { AgGridReact, AgGridColumn }; + render(); + await waitFor( + () => + new Promise(resolve => { + setTimeout(() => { + resolve(true); + }, 30); + }), + ); + const column = await screen.findByText('Aéroport Charles de Gaulle 2 TGV'); + expect(column).toBeInTheDocument(); }); }); diff --git a/packages/datagrid/src/components/DataGrid/__snapshots__/DataGrid.test.js.snap b/packages/datagrid/src/components/DataGrid/__snapshots__/DataGrid.test.js.snap deleted file mode 100644 index 385f2d05f7..0000000000 --- a/packages/datagrid/src/components/DataGrid/__snapshots__/DataGrid.test.js.snap +++ /dev/null @@ -1,308 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`#DataGrid should render DataGrid 1`] = ` -
- -
-`; - -exports[`#DataGrid should render DataGrid with columnsDefs and rowData 1`] = ` -
- -
-`; - -exports[`#DataGrid should render one Skeleton is InProgress 1`] = ` -
-
- -
-
-`; diff --git a/packages/dataviz/src/components/GeoChart/maps/CA.topo.json b/packages/dataviz/assets/maps/CA.topo.json similarity index 100% rename from packages/dataviz/src/components/GeoChart/maps/CA.topo.json rename to packages/dataviz/assets/maps/CA.topo.json diff --git a/packages/dataviz/src/components/GeoChart/maps/FR.topo.json b/packages/dataviz/assets/maps/FR.topo.json similarity index 94% rename from packages/dataviz/src/components/GeoChart/maps/FR.topo.json rename to packages/dataviz/assets/maps/FR.topo.json index 441000a73a..3daa76bfca 100644 --- a/packages/dataviz/src/components/GeoChart/maps/FR.topo.json +++ b/packages/dataviz/assets/maps/FR.topo.json @@ -4083,35 +4083,8 @@ { "arcs": [ [ - 42, - -41, - -40, - -39, - -36, - 197, - -229, - -104, - 241, - 286, - 288, - -201, - -206, - -186, - -185, - -222, - -221, - 251, - 252, - 253, - 247, - 239, - 87, - 88, - 89, - 84, - 49, - 47, - 46 + 42, -41, -40, -39, -36, 197, -229, -104, 241, 286, 288, -201, -206, -186, -185, -222, + -221, 251, 252, 253, 247, 239, 87, 88, 89, 84, 49, 47, 46 ], [199] ], @@ -4122,32 +4095,8 @@ "arcs": [ [ [ - 50, - 111, - 115, - 119, - 120, - -129, - -262, - -261, - -208, - -215, - 181, - -214, - 219, - 220, - 221, - 184, - 185, - 186, - -205, - -204, - -190, - -176, - 114, - 52, - 53, - 54 + 50, 111, 115, 119, 120, -129, -262, -261, -208, -215, 181, -214, 219, 220, 221, 184, + 185, 186, -205, -204, -190, -176, 114, 52, 53, 54 ] ], [[124]] @@ -4158,31 +4107,8 @@ { "arcs": [ [ - 55, - 64, - -54, - -53, - -115, - 175, - 189, - 203, - 204, - -187, - 205, - 200, - -289, - -287, - -242, - 103, - 104, - -98, - 99, - 105, - 109, - 70, - 68, - 67, - 62 + 55, 64, -54, -53, -115, 175, 189, 203, 204, -187, 205, 200, -289, -287, -242, 103, + 104, -98, 99, 105, 109, 70, 68, 67, 62 ] ], "type": "Polygon", @@ -4191,26 +4117,8 @@ { "arcs": [ [ - 79, - -76, - 80, - 81, - 90, - -89, - -88, - -240, - -248, - -254, - -253, - -284, - -259, - -258, - 297, - -264, - -263, - -232, - -231, - -241 + 79, -76, 80, 81, 90, -89, -88, -240, -248, -254, -253, -284, -259, -258, 297, -264, + -263, -232, -231, -241 ] ], "type": "Polygon", @@ -4219,22 +4127,7 @@ { "arcs": [ [ - 125, - 129, - 130, - 131, - -145, - -144, - -149, - 223, - 225, - 226, - -216, - -210, - -209, - 260, - 261, - 128, + 125, 129, 130, 131, -145, -144, -149, 223, 225, 226, -216, -210, -209, 260, 261, 128, -121 ] ], @@ -4263,25 +4156,8 @@ { "arcs": [ [ - 207, - 208, - 209, - 215, - -227, - 284, - -158, - -281, - 285, - 255, - 256, - 257, - 258, - 283, - -252, - -220, - 213, - -182, - 214 + 207, 208, 209, 215, -227, 284, -158, -281, 285, 255, 256, 257, 258, 283, -252, -220, + 213, -182, 214 ] ], "type": "Polygon", @@ -4326,28 +4202,8 @@ { "arcs": [ [ - 42, - -41, - -40, - -39, - -36, - 197, - -229, - -104, - 241, - 242, - 244, - 245, - 246, - 247, - 239, - 87, - 88, - 89, - 84, - 49, - 47, - 46 + 42, -41, -40, -39, -36, 197, -229, -104, 241, 242, 244, 245, 246, 247, 239, 87, 88, + 89, 84, 49, 47, 46 ], [199] ], @@ -4364,28 +4220,8 @@ { "arcs": [ [ - 55, - 64, - -54, - -53, - -115, - 175, - 189, - 203, - 204, - -187, - 205, - 200, - 201, - -102, - -109, - -108, - -111, - 60, - 65, - 66, - 67, - 62 + 55, 64, -54, -53, -115, 175, 189, 203, 204, -187, 205, 200, 201, -102, -109, -108, + -111, 60, 65, 66, 67, 62 ] ], "type": "Polygon", @@ -4394,25 +4230,8 @@ { "arcs": [ [ - 68, - -67, - -66, - -61, - 110, - 107, - 108, - 101, - -202, - -289, - -287, - -242, - 103, - 104, - -98, - 99, - 105, - 109, - 70 + 68, -67, -66, -61, 110, 107, 108, 101, -202, -289, -287, -242, 103, 104, -98, 99, 105, + 109, 70 ] ], "type": "Polygon", @@ -4439,22 +4258,7 @@ { "arcs": [ [ - 125, - 129, - 130, - 131, - -145, - -144, - -149, - 223, - 225, - 226, - -216, - -210, - -209, - 260, - 261, - 128, + 125, 129, 130, 131, -145, -144, -149, 223, 225, 226, -216, -210, -209, 260, 261, 128, -121 ] ], @@ -4493,25 +4297,8 @@ { "arcs": [ [ - 207, - 208, - 209, - 215, - -227, - 284, - -158, - -281, - 285, - 255, - 256, - 257, - 258, - 283, - -252, - -220, - 213, - -182, - 214 + 207, 208, 209, 215, -227, 284, -158, -281, 285, 255, 256, 257, 258, 283, -252, -220, + 213, -182, 214 ] ], "type": "Polygon", diff --git a/packages/dataviz/src/components/GeoChart/maps/MX.topo.json b/packages/dataviz/assets/maps/MX.topo.json similarity index 100% rename from packages/dataviz/src/components/GeoChart/maps/MX.topo.json rename to packages/dataviz/assets/maps/MX.topo.json diff --git a/packages/dataviz/src/components/GeoChart/maps/US.topo.json b/packages/dataviz/assets/maps/US.topo.json similarity index 100% rename from packages/dataviz/src/components/GeoChart/maps/US.topo.json rename to packages/dataviz/assets/maps/US.topo.json diff --git a/packages/dataviz/src/components/GeoChart/maps/US_CA.topo.json b/packages/dataviz/assets/maps/US_CA.topo.json similarity index 100% rename from packages/dataviz/src/components/GeoChart/maps/US_CA.topo.json rename to packages/dataviz/assets/maps/US_CA.topo.json diff --git a/packages/dataviz/src/components/GeoChart/maps/continents.topo.json b/packages/dataviz/assets/maps/continents.topo.json similarity index 100% rename from packages/dataviz/src/components/GeoChart/maps/continents.topo.json rename to packages/dataviz/assets/maps/continents.topo.json diff --git a/packages/dataviz/src/components/GeoChart/maps/world.topo.json b/packages/dataviz/assets/maps/world.topo.json similarity index 99% rename from packages/dataviz/src/components/GeoChart/maps/world.topo.json rename to packages/dataviz/assets/maps/world.topo.json index cfbc53b7c7..a3a4478b8a 100644 --- a/packages/dataviz/src/components/GeoChart/maps/world.topo.json +++ b/packages/dataviz/assets/maps/world.topo.json @@ -10596,29 +10596,8 @@ "arcs": [ [ [ - -17, - -425, - 607, - -491, - 608, - -489, - -355, - -488, - 609, - -317, - 610, - -134, - -427, - -435, - -242, - -373, - -240, - -326, - 611, - 612, - 613, - 614, - 615 + -17, -425, 607, -491, 608, -489, -355, -488, 609, -317, 610, -134, -427, -435, -242, + -373, -240, -326, 611, 612, 613, 614, 615 ] ], [[616]] diff --git a/packages/dataviz/package.json b/packages/dataviz/package.json index c52053a1f9..f14d5d97a9 100644 --- a/packages/dataviz/package.json +++ b/packages/dataviz/package.json @@ -17,7 +17,7 @@ "lint:es": "talend-scripts lint:es --format json -o eslint-report.json", "lint:style": "talend-scripts lint:style -f json -o stylelint-report.json", "start": "talend-scripts start-storybook -p 6006", - "test": "cross-env TZ=Europe/Paris talend-scripts test --silent", + "test": "cross-env TZ=Europe/Paris talend-scripts test", "test:watch": "cross-env TZ=Europe/Paris talend-scripts test --watch", "test:cov": "cross-env TZ=Europe/Paris talend-scripts test --coverage", "test:demo": "talend-scripts build-storybook --quiet" @@ -37,6 +37,7 @@ "url": "https://github.com/Talend/ui.git" }, "dependencies": { + "@talend/assets-api": "^1.0.0", "@talend/react-components": "^6.44.14", "@talend/design-tokens": "^2.1.0", "classnames": "^2.3.1", diff --git a/packages/dataviz/src/components/GeoChart/GeoChart.test.js b/packages/dataviz/src/components/GeoChart/GeoChart.test.js index 250f0f3469..fbf702284f 100644 --- a/packages/dataviz/src/components/GeoChart/GeoChart.test.js +++ b/packages/dataviz/src/components/GeoChart/GeoChart.test.js @@ -4,10 +4,18 @@ import GeoChart from './GeoChart.component'; import { getGeoChartConfig } from './GeoChart.utils'; import styles from './GeoChart.scss'; +function getTopo(url) { + const last = url.split('/').pop(); + return require(`../../../assets/maps/${last}`); +} + describe('GeoChart component', () => { let defaultProps; - + let originalfetch; beforeEach(async () => { + jest.resetAllMocks(); + originalfetch = global.fetch; + global.fetch = jest.fn(url => ({ ok: true, json: () => Promise.resolve(getTopo(url)) })); defaultProps = { data: [ { key: 'Occitanie', value: 10 }, @@ -18,6 +26,9 @@ describe('GeoChart component', () => { onSelection: jest.fn(), }; }); + afterEach(() => { + global.fetch = originalfetch; + }); it('Should match data', () => { const component = mount().render(); @@ -47,6 +58,7 @@ describe('GeoChart component', () => { ]} />, ).render(); + expect(global.fetch).toHaveBeenCalled(); expect(component.find('[data-key="TX"]')).toHaveLength(1); expect(component.find('[data-key="New York"]')).toHaveLength(0); diff --git a/packages/dataviz/src/components/GeoChart/GeoChart.utils.ts b/packages/dataviz/src/components/GeoChart/GeoChart.utils.ts index 6b55eb7e3c..ba495df7c7 100644 --- a/packages/dataviz/src/components/GeoChart/GeoChart.utils.ts +++ b/packages/dataviz/src/components/GeoChart/GeoChart.utils.ts @@ -1,25 +1,9 @@ import { GeoChartConfig } from './GeoChart.component'; -import CA from './maps/CA.topo.json'; -import continents from './maps/continents.topo.json'; -import FR from './maps/FR.topo.json'; -import MX from './maps/MX.topo.json'; -import US_CA from './maps/US_CA.topo.json'; -import US from './maps/US.topo.json'; -import world from './maps/world.topo.json'; +import assetsAPI from '@talend/assets-api'; const DEFAULT_LABEL_PROPERTY = 'name'; const STATE_CODE_VALUE_PROPERTIES = ['iso_3166_2']; -const MAPS: { [key: string]: any } = { - CA, - continents, - FR, - MX, - US, - US_CA, - world, -}; - // Define file name only, will be used in a dynamic import() type SupportedGeoChart = Omit & { file: string }; const SUPPORTED_CHARTS: { [key: string]: SupportedGeoChart } = { @@ -97,11 +81,22 @@ export function getGeoChartSupportedDomains(): string[] { return Object.keys(SUPPORTED_CHARTS); } -export async function getGeoChartConfig(domain: string): Promise { +export async function getGeoChartConfig(domain: string): Promise { const { file, ...chartConfig } = SUPPORTED_CHARTS[domain]; - return { - ...chartConfig, - labelProperty: chartConfig.labelProperty || DEFAULT_LABEL_PROPERTY, - topology: MAPS[file], - }; + try { + const topology: GeoChartConfig['topology'] | undefined = await assetsAPI.getJSON( + `/dist/assets/maps/${file}.topo.json`, + ); + if (!topology) { + throw new Error('GeoChart topology undefined'); + } + return { + ...chartConfig, + labelProperty: chartConfig.labelProperty || DEFAULT_LABEL_PROPERTY, + topology, + }; + } catch (e) { + console.error(`can't get requested topology ${file}`, e); + } + return; } diff --git a/packages/dataviz/talend-scripts.json b/packages/dataviz/talend-scripts.json index 4d78bcf08f..a6810de062 100755 --- a/packages/dataviz/talend-scripts.json +++ b/packages/dataviz/talend-scripts.json @@ -1,3 +1,4 @@ { - "preset": "@talend/scripts-preset-react-lib" + "preset": "@talend/scripts-preset-react-lib", + "copy": [{ "from": "assets/**" }] } diff --git a/packages/design-system/README.md b/packages/design-system/README.md index 2ce7dedb1d..43e5743bee 100644 --- a/packages/design-system/README.md +++ b/packages/design-system/README.md @@ -63,6 +63,10 @@ Limit changes to styled-components scope. Visual non-regression testing will be covered by Chromatic. Use Cypress if you have to perform interaction tests, in real browsers. +To launch non the tests you first need to have cypress installed on your environment. Then you can just execute the corresponding npm scripts: + + yarn workspace @talend/design-system run test:cy + ## License [Apache 2.0](https://github.com/Talend/design-system/blob/master/LICENSE) diff --git a/packages/design-system/package.json b/packages/design-system/package.json index 7f956967e1..6cd2780ca3 100644 --- a/packages/design-system/package.json +++ b/packages/design-system/package.json @@ -33,6 +33,7 @@ "access": "public" }, "dependencies": { + "@talend/assets-api": "^1.0.0", "@talend/design-tokens": "^2.1.0", "classnames": "^2.3.1", "modern-css-reset": "^1.4.0", diff --git a/packages/design-system/src/components/IconsProvider/IconsProvider.tsx b/packages/design-system/src/components/IconsProvider/IconsProvider.tsx index 36e0a9ec18..ebabea663a 100644 --- a/packages/design-system/src/components/IconsProvider/IconsProvider.tsx +++ b/packages/design-system/src/components/IconsProvider/IconsProvider.tsx @@ -1,6 +1,7 @@ import React, { ReactElement, RefObject, useState, useEffect, useRef } from 'react'; +import assetsAPI from '@talend/assets-api'; -const DEFAULT_BUNDLES = ['/all.svg']; +const DEFAULT_BUNDLES = [assetsAPI.getURL('/dist/svg-bundle/all.svg', '@talend/icons')]; const FETCHING_BUNDLES: { [url: string]: Promise } = {}; const ICONS_PROVIDER_CLASS = '.tc-iconsprovider'; diff --git a/packages/design-system/src/components/Stepper/docs/Stepper.stories.mdx b/packages/design-system/src/components/Stepper/docs/Stepper.stories.mdx index e177c26a63..18221ae1bc 100644 --- a/packages/design-system/src/components/Stepper/docs/Stepper.stories.mdx +++ b/packages/design-system/src/components/Stepper/docs/Stepper.stories.mdx @@ -95,9 +95,9 @@ The sub-steps option is not available with the horizontal stepper. ## Content -Use nouns to create one-word labels for steps. Go for 2-word step labels when necessary. Step labels should describe the purpose of each step and let users know where they are in their setup.  +Use nouns to create one-word labels for steps. Go for 2-word step labels when necessary. Step labels should describe the purpose of each step and let users know where they are in their setup. -**In vertical steppers**, each step opens a section to the right of the stepper. Start the section title with a verb and tell users what to do and why. For example, a step label "Engine" could have a section title "Add the engine on which to process data". +**In vertical steppers**, each step opens a section to the right of the stepper. Start the section title with a verb and tell users what to do and why. For example, a step label "Engine" could have a section title "Add the engine on which to process data". Step sections can also have subtitles, if necessary, to provide users with extra important information—as in complicated steps for example. **In horizontal steppers**, no sections are used. Once a step is selected, user input shows below. Avoid using long nouns in horizontal steppers diff --git a/packages/design-system/tsconfig.build.json b/packages/design-system/tsconfig.build.json index c1e6a1eb9e..bb854f9c9a 100644 --- a/packages/design-system/tsconfig.build.json +++ b/packages/design-system/tsconfig.build.json @@ -1,6 +1,7 @@ { "extends": "./tsconfig.json", "include": ["src/index.ts", "src/styled.d.ts", "custom.d.ts"], + "exclude": ["*.spec.*"], "compilerOptions": { "declaration": true } diff --git a/packages/faceted-search/package.json b/packages/faceted-search/package.json index ed037fcc2a..614fa9d8d6 100644 --- a/packages/faceted-search/package.json +++ b/packages/faceted-search/package.json @@ -10,7 +10,7 @@ "build:prod": "talend-scripts build:lib:umd --prod", "pre-release": "yarn build:dev && yarn build:prod", "build:lib": "talend-scripts build:lib", - "test": "cross-env TZ=Europe/Paris talend-scripts test --silent", + "test": "cross-env TZ=Europe/Paris talend-scripts test", "test:watch": "cross-env TZ=Europe/Paris talend-scripts test --watch", "test:cov": "cross-env TZ=Europe/Paris talend-scripts test --coverage", "test:demo": "talend-scripts build-storybook --quiet --docs", diff --git a/packages/forms/package.json b/packages/forms/package.json index 687a5fcfde..d79bbc8cdd 100644 --- a/packages/forms/package.json +++ b/packages/forms/package.json @@ -36,6 +36,7 @@ "url": "https://github.com/Talend/ui.git" }, "dependencies": { + "@talend/assets-api": "^1.0.0", "@talend/design-system": "^2.4.0", "@talend/json-schema-form-core": "1.0.5", "@talend/react-components": "^6.45.0", diff --git a/packages/forms/src/UIForm/fields/Code/Code.component.js b/packages/forms/src/UIForm/fields/Code/Code.component.js index 3676e033a8..b354f6516e 100644 --- a/packages/forms/src/UIForm/fields/Code/Code.component.js +++ b/packages/forms/src/UIForm/fields/Code/Code.component.js @@ -1,143 +1,123 @@ +/* eslint-disable react/jsx-no-bind */ import PropTypes from 'prop-types'; -import React from 'react'; +import React, { Suspense, useEffect } from 'react'; import { withTranslation } from 'react-i18next'; import keyCode from 'keycode'; +import assetsApi from '@talend/assets-api'; +import { Skeleton } from '@talend/react-components'; import FieldTemplate from '../FieldTemplate'; -import TextArea from '../TextArea'; + import { generateId, generateDescriptionId, generateErrorId } from '../../Message/generateId'; import getDefaultT from '../../../translate'; import { I18N_DOMAIN_FORMS } from '../../../constants'; -let CodeWidget = TextArea; -let AceEditor; +const ReactAce = React.lazy(() => + assetsApi.getUMD('react-ace').then(mod => assetsApi.toDefaultModule(mod.default)), +); + +function CodeSkeleton() { + return ( +
+ + + + + +
+ ); +} + const DEFAULT_SET_OPTIONS = { enableBasicAutocompletion: true, enableLiveAutocompletion: true, enableSnippets: true, }; -class WrappedTextArea extends React.PureComponent { - constructor() { - super(); - // eslint-disable-next-line no-console - console.warn('CodeWidget react-ace not found, fallback to Textarea'); - } - - render() { - return