diff --git a/scripts/playwright-docker.sh b/scripts/playwright-docker.sh index 9c80c24c..5b24e8b7 100755 --- a/scripts/playwright-docker.sh +++ b/scripts/playwright-docker.sh @@ -1,4 +1,7 @@ #!/usr/bin/env bash +# Usage: +# npm run test:docker:update - run all visual tests with --update +# npm run test:docker:update -- - run only matching files (e.g. Base.visual, SplitTooltip) set -euo pipefail diff --git a/src/components/SplitPane/StyledSplitPane.scss b/src/components/SplitPane/StyledSplitPane.scss index 1e927872..c3c0bfc8 100644 --- a/src/components/SplitPane/StyledSplitPane.scss +++ b/src/components/SplitPane/StyledSplitPane.scss @@ -3,7 +3,16 @@ .styled-split-pane { &__pane-resizer { - background: var(--g-color-base-generic); + background: linear-gradient( + to right, + var(--g-color-base-generic), + var(--g-color-base-generic) + ), + linear-gradient( + to right, + var(--g-color-base-background), + var(--g-color-base-background) + ); z-index: 1; box-sizing: border-box; position: relative; @@ -44,12 +53,23 @@ height: 100%; width: 1px; min-width: 1px; - background: var(--data-table-border-color); } } .Pane { height: 100%; - background: var(--ck-split-pane-background); + } + + .Pane2 { + background: linear-gradient( + to right, + var(--g-color-infographics-tooltip-bg), + var(--g-color-infographics-tooltip-bg) + ), + linear-gradient( + to right, + var(--g-color-base-background), + var(--g-color-base-background) + ); } } diff --git a/src/plugins/gravity-charts/__screenshots__/Base.visual.test.tsx-screenshots/GravityCharts-base-tests-should-render-chart-with-valid-data-1-chromium-linux.png b/src/plugins/gravity-charts/__screenshots__/Base.visual.test.tsx-screenshots/GravityCharts-base-tests-should-render-chart-with-valid-data-1-chromium-linux.png new file mode 100644 index 00000000..de2c6685 Binary files /dev/null and b/src/plugins/gravity-charts/__screenshots__/Base.visual.test.tsx-screenshots/GravityCharts-base-tests-should-render-chart-with-valid-data-1-chromium-linux.png differ diff --git a/src/plugins/gravity-charts/__screenshots__/SplitTooltip.visual.test.tsx-screenshots/Split-tooltip-visual-tests-should-render-tooltip-in-album-orientation-1-chromium-linux.png b/src/plugins/gravity-charts/__screenshots__/SplitTooltip.visual.test.tsx-screenshots/Split-tooltip-visual-tests-should-render-tooltip-in-album-orientation-1-chromium-linux.png new file mode 100644 index 00000000..aceb4178 Binary files /dev/null and b/src/plugins/gravity-charts/__screenshots__/SplitTooltip.visual.test.tsx-screenshots/Split-tooltip-visual-tests-should-render-tooltip-in-album-orientation-1-chromium-linux.png differ diff --git a/src/plugins/gravity-charts/__screenshots__/SplitTooltip.visual.test.tsx-screenshots/Split-tooltip-visual-tests-should-render-tooltip-in-portrait-orientation-1-chromium-linux.png b/src/plugins/gravity-charts/__screenshots__/SplitTooltip.visual.test.tsx-screenshots/Split-tooltip-visual-tests-should-render-tooltip-in-portrait-orientation-1-chromium-linux.png new file mode 100644 index 00000000..64c318a6 Binary files /dev/null and b/src/plugins/gravity-charts/__screenshots__/SplitTooltip.visual.test.tsx-screenshots/Split-tooltip-visual-tests-should-render-tooltip-in-portrait-orientation-1-chromium-linux.png differ diff --git a/src/plugins/gravity-charts/__tests__/Base.visual.test.tsx b/src/plugins/gravity-charts/__tests__/Base.visual.test.tsx new file mode 100644 index 00000000..f91637c8 --- /dev/null +++ b/src/plugins/gravity-charts/__tests__/Base.visual.test.tsx @@ -0,0 +1,41 @@ +import React from 'react'; + +import type {ChartData} from '@gravity-ui/charts'; + +import {render} from '../../../../test-utils/utils.js'; +import {settings} from '../../../libs/index.js'; +import {GravityChartsPlugin} from '../index.js'; + +import {CHART_TEST_STORY_DATA_QA, ChartTestStory} from './ChartTestStory.js'; + +describe('GravityCharts base tests', () => { + beforeAll(() => { + settings.set({plugins: [GravityChartsPlugin]}); + }); + + test('should render chart with valid data', async () => { + const data: ChartData = { + series: { + data: [ + { + type: 'line', + name: 'Line 1', + data: [ + {x: 0, y: 100}, + {x: 2, y: 80}, + {x: 3, y: 120}, + ], + }, + ], + }, + }; + const screen = await render(); + await expect(screen.getByTestId(CHART_TEST_STORY_DATA_QA)).toMatchScreenshot(); + }); + + test('should display "No data" when series is empty', async () => { + const data: ChartData = {series: {data: []}}; + const screen = await render(); + await expect.element(screen.getByText('No data')).toBeVisible(); + }); +}); diff --git a/src/plugins/gravity-charts/__tests__/ChartTestStory.tsx b/src/plugins/gravity-charts/__tests__/ChartTestStory.tsx new file mode 100644 index 00000000..f673c3b0 --- /dev/null +++ b/src/plugins/gravity-charts/__tests__/ChartTestStory.tsx @@ -0,0 +1,27 @@ +import React from 'react'; + +import type {ChartData} from '@gravity-ui/charts'; + +import {ChartKit} from '../../../components/ChartKit.js'; + +export const CHART_TEST_STORY_DATA_QA = 'chart-test-story-data-qa'; + +type Props = { + data: ChartData; + styles?: React.CSSProperties; + tooltip?: {splitted?: boolean}; +}; + +export const ChartTestStory = ({data, styles, tooltip}: Props) => { + const containerStyle: React.CSSProperties = { + height: '100vh', + width: '100vw', + ...styles, + }; + + return ( +
+ +
+ ); +}; diff --git a/src/plugins/gravity-charts/__tests__/GravityCharts.visual.test.tsx b/src/plugins/gravity-charts/__tests__/GravityCharts.visual.test.tsx deleted file mode 100644 index 71dda8f9..00000000 --- a/src/plugins/gravity-charts/__tests__/GravityCharts.visual.test.tsx +++ /dev/null @@ -1,38 +0,0 @@ -import React from 'react'; - -import type {ChartData} from '@gravity-ui/charts'; - -import {render} from '../../../../test-utils/utils.js'; - -import {TestStory} from './TestStory.visual.js'; - -const VALID_CHART_DATA = { - series: { - data: [ - { - type: 'line', - data: [{x: 0, y: 100}], - }, - ], - }, - xAxis: { - type: 'category', - categories: ['A'], - }, -} as ChartData; - -test('Validation should work when updating chart data (empty series)', async () => { - const screen = await render(); - - const emptyData = { - series: { - data: [], - }, - xAxis: { - type: 'category', - }, - }; - - await screen.getByRole('textbox').fill(JSON.stringify(emptyData)); - await expect.element(screen.getByText('No data')).toBeVisible(); -}); diff --git a/src/plugins/gravity-charts/__tests__/SplitTooltip.visual.test.tsx b/src/plugins/gravity-charts/__tests__/SplitTooltip.visual.test.tsx new file mode 100644 index 00000000..c1ed2ea9 --- /dev/null +++ b/src/plugins/gravity-charts/__tests__/SplitTooltip.visual.test.tsx @@ -0,0 +1,50 @@ +import React from 'react'; + +import type {ChartData} from '@gravity-ui/charts'; +import {page} from 'vitest/browser'; + +import {render} from '../../../../test-utils/utils.js'; +import {settings} from '../../../libs/index.js'; +import {GravityChartsPlugin} from '../index.js'; + +import {CHART_TEST_STORY_DATA_QA, ChartTestStory} from './ChartTestStory.js'; + +const SPLIT_TOOLTIP_DATA: ChartData = { + series: { + data: [ + { + type: 'bar-x', + name: 'Series 1', + data: [ + {x: 0, y: 40}, + {x: 1, y: 55}, + ], + }, + ], + }, + xAxis: {type: 'category', categories: ['A', 'B']}, +}; + +describe('Split tooltip visual tests', () => { + beforeAll(() => { + settings.set({plugins: [GravityChartsPlugin]}); + }); + + test('should render tooltip in album orientation', async () => { + await page.viewport(600, 280); + const screen = await render( + , + {providers: {theme: 'dark'}}, + ); + await expect(screen.getByTestId(CHART_TEST_STORY_DATA_QA)).toMatchScreenshot(); + }); + + test('should render tooltip in portrait orientation', async () => { + await page.viewport(280, 400); + const screen = await render( + , + {providers: {theme: 'dark'}}, + ); + await expect(screen.getByTestId(CHART_TEST_STORY_DATA_QA)).toMatchScreenshot(); + }); +}); diff --git a/src/plugins/gravity-charts/__tests__/TestStory.visual.tsx b/src/plugins/gravity-charts/__tests__/TestStory.visual.tsx deleted file mode 100644 index 5c62d9a2..00000000 --- a/src/plugins/gravity-charts/__tests__/TestStory.visual.tsx +++ /dev/null @@ -1,36 +0,0 @@ -import React, {ChangeEventHandler} from 'react'; - -import {ChartKit} from '../../../components/ChartKit'; -import {settings} from '../../../libs'; -import {GravityChartsPlugin} from '../index'; - -export const TestStory = (props: any) => { - const [chartData, setChartData] = React.useState(props.data); - const [loading, setLoading] = React.useState(true); - - React.useEffect(() => { - settings.set({plugins: [GravityChartsPlugin]}); - setLoading(false); - }, []); - - const updateData: ChangeEventHandler = (event) => { - const value = event.target.value; - setChartData(JSON.parse(value)); - }; - - if (loading) { - return loading; - } - - return ( -
- - -
- ); -}; diff --git a/src/plugins/gravity-charts/renderer/__stories__/SplitTooltip.stories.tsx b/src/plugins/gravity-charts/renderer/__stories__/SplitTooltip.stories.tsx index 7d08ec7f..41243cae 100644 --- a/src/plugins/gravity-charts/renderer/__stories__/SplitTooltip.stories.tsx +++ b/src/plugins/gravity-charts/renderer/__stories__/SplitTooltip.stories.tsx @@ -6,39 +6,63 @@ import {ChartKit} from '../../../../components/ChartKit'; import {StoryWrapper} from './StoryWrapper'; -function getPieSegmentData(name: string, color: string, index: number) { +const MONTHS = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']; + +function generateBarXData() { + const categories = MONTHS; + const revenue = categories.map((_, i) => ({ + x: i, + y: 40 + Math.sin(i * 0.5) * 25 + (i % 5) * 2, + })); + const expenses = categories.map((_, i) => ({ + x: i, + y: 25 + Math.cos(i * 0.3) * 15 + (i % 3) * 3, + })); + const profit = categories.map((_, i) => ({ + x: i, + y: (revenue[i].y as number) - (expenses[i].y as number), + })); + return { - name, - value: index * 10, - label: name, - color: color, + categories, + series: [ + {name: 'Revenue', color: '#4DA2F1', data: revenue}, + {name: 'Expenses', color: '#FF3D64', data: expenses}, + {name: 'Profit', color: '#8AD554', data: profit}, + ], }; } -export const SplitTooltipBasic: StoryObj = { - name: 'Basic', +const barXData = generateBarXData(); + +export const SplitTooltipBarX: StoryObj = { + name: 'Bar-X with crosshair', render: () => { return ( - + ({ + type: 'bar-x' as const, + name: s.name, + color: s.color, + stacking: 'normal', + data: s.data, + })), + }, + legend: {enabled: false}, + xAxis: { + type: 'category', + categories: barXData.categories, + crosshair: {enabled: true}, + }, + tooltip: { + valueFormat: { + precision: 2, + type: 'number', + }, }, }} tooltip={{splitted: true}} diff --git a/src/plugins/gravity-charts/renderer/withSplitPane/withSplitPane.tsx b/src/plugins/gravity-charts/renderer/withSplitPane/withSplitPane.tsx index 2a611806..2af86d50 100644 --- a/src/plugins/gravity-charts/renderer/withSplitPane/withSplitPane.tsx +++ b/src/plugins/gravity-charts/renderer/withSplitPane/withSplitPane.tsx @@ -63,7 +63,9 @@ const SplitPaneContent = ( const containerHeight = height; if (containerHeight - RESIZER_HEIGHT === size) { setSize(containerHeight - RESIZER_HEIGHT - tooltipHeight); - chartRef.current?.reflow(); + queueMicrotask(() => { + chartRef.current?.reflow({immediate: true}); + }); } } @@ -104,6 +106,9 @@ const SplitPaneContent = ( }; return { + defaultState: { + hoveredPosition: {x: 0, y: 0}, + }, ...data, chart: { ...data.chart, @@ -116,7 +121,7 @@ const SplitPaneContent = ( ...data.tooltip, enabled: false, }, - }; + } satisfies ChartData; }, [data]); const handleOrientationChange = React.useCallback(() => { diff --git a/src/plugins/indicator/__screenshots__/Indicator.visual.test.tsx-screenshots/Indicator-visual-tests-default-state-1-chromium-linux.png b/src/plugins/indicator/__screenshots__/Indicator.visual.test.tsx-screenshots/Indicator-visual-tests-default-state-1-chromium-linux.png index a0039560..39336608 100644 Binary files a/src/plugins/indicator/__screenshots__/Indicator.visual.test.tsx-screenshots/Indicator-visual-tests-default-state-1-chromium-linux.png and b/src/plugins/indicator/__screenshots__/Indicator.visual.test.tsx-screenshots/Indicator-visual-tests-default-state-1-chromium-linux.png differ diff --git a/test-utils/utils.tsx b/test-utils/utils.tsx index 67eb463d..c82c39d7 100644 --- a/test-utils/utils.tsx +++ b/test-utils/utils.tsx @@ -4,6 +4,8 @@ import {MobileProvider, ThemeProvider} from '@gravity-ui/uikit'; import {render, renderHook} from 'vitest-browser-react'; import type {ComponentRenderOptions} from 'vitest-browser-react'; +const DEFAULT_PROVIDERS: ProvidersProps = {theme: 'light'}; + interface ProvidersProps { theme?: string; mobile?: boolean; @@ -43,7 +45,8 @@ function customRender( providers?: {theme?: string; mobile?: boolean}; } = {}, ) { - const wrapper = createWrapper(providers, options.wrapper); + const mergedProviders = {...DEFAULT_PROVIDERS, ...providers}; + const wrapper = createWrapper(mergedProviders, options.wrapper); return render(ui, {...options, wrapper}); } diff --git a/vitest.config.ts b/vitest.config.ts index 22f89026..28ca981a 100644 --- a/vitest.config.ts +++ b/vitest.config.ts @@ -21,7 +21,7 @@ export default defineConfig({ instances: [ { browser: 'chromium', - viewport: {width: 1280, height: 720}, + viewport: {height: 280, width: 400}, }, ], locators: {testIdAttribute: 'data-qa'},