Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
3e2482f
[d3-chart] added responsiveness for charts by default
ilyabrower Jun 4, 2026
6fd8340
[chore] Merge remote-tracking branch 'origin/release/v17' into UIK-50…
ilyabrower Jun 5, 2026
b942b61
[d3-chart] init responsiveness
ilyabrower Jun 5, 2026
82c03b1
Merge branch 'release/v17' into UIK-5092/reposiveness-charts
Valeria-Zimnitskaya Jun 10, 2026
d8b03c0
[d3-chart] moved resizable containers to test stories
ilyabrower Jun 10, 2026
bba232f
[d3-chart] moved resizable containers to test stories
ilyabrower Jun 10, 2026
dd05c14
[chore] update some snapshots
Valeria-Zimnitskaya Jun 10, 2026
28d841f
[stories] fixed d3 chart responsive stories
ilyabrower Jun 10, 2026
16482a0
[d3-chart] rollback required plot width/height for cigarette
ilyabrower Jun 10, 2026
d8684b7
[chore] update snapshots
Valeria-Zimnitskaya Jun 11, 2026
9cc2fb5
[d3-chart] rollback plot width/height props
ilyabrower Jun 11, 2026
bb27464
[d3-chart] rollback plot width/height props
ilyabrower Jun 11, 2026
b6b463e
[d3-chart] fixed types
ilyabrower Jun 11, 2026
c3e32e0
[chore] update test and snapshots
Valeria-Zimnitskaya Jun 11, 2026
e000cdb
[chore] Merge branch 'UIK-5092/reposiveness-charts' of https://github…
Valeria-Zimnitskaya Jun 11, 2026
8b41f50
[chore] update test and snapshots
Valeria-Zimnitskaya Jun 11, 2026
bb1c616
Merge branch 'release/v17' into UIK-5092/reposiveness-charts
ilyabrower Jun 11, 2026
a915cd1
[docs] add detailes about responsiveness and story with responsive chart
j-mnizhek Jun 12, 2026
5e8e926
[stories] update stories for responsive charts
j-mnizhek Jun 12, 2026
254d02a
[chore] fix website
Valeria-Zimnitskaya Jun 12, 2026
7e39477
Merge branch 'release/v17' into UIK-5092/reposiveness-charts
Valeria-Zimnitskaya Jun 12, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions playground/entries/Chart/Cigarette.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { Box } from '@semcore/ui/base-components';
import type { CigaretteChartProps } from '@semcore/ui/d3-chart';
import { Chart } from '@semcore/ui/d3-chart';
import React from 'react';
Expand Down
2 changes: 2 additions & 0 deletions semcore/base-components/__tests__/flex-box.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,8 @@ describe('Box', () => {
right: { css: 'right', values: ['2px'], tests: [2, '2px'] },
bottom: { css: 'bottom', values: ['2px'], tests: [2, '2px'] },
zIndex: { css: 'z-index', values: ['5'], tests: [5] },
resize: { css: 'resize', values: ['both'], tests: ['both'] },
overflow: { css: 'overflow', values: ['hidden'], tests: ['hidden'] },
};
const components = Object.keys(MAP_CSS).map((prop, id) => {
const { tests } = MAP_CSS[prop];
Expand Down
23 changes: 13 additions & 10 deletions semcore/base-components/src/components/flex-box/Box/useBox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -167,19 +167,16 @@ export type BoxProps = IStyledProps & {
children?: React.ReactNode;
/** Hover cursor */
hoverCursor?: Property.Cursor;
/**
* Background
*/
/** Css `background` property */
bg?: Property.Background | BasicColorKeys | SemanticColorKeys;
/**
* Border radius
*/
/** Css `border-radius` property */
borderRadius?: Property.BorderRadius | BorderRadius;
/**
* Border
*/
/** Css `border` property */
border?: Property.Border;

/** Css `resize` property */
resize?: Property.Resize;
/** Css `overflow` property */
overflow?: Property.Overflow;
/**
* Old way to add custom style
* @deprecated
Expand Down Expand Up @@ -254,6 +251,8 @@ function calculateIndentStyles(props: BoxProps, scaleIndent: number, colorResolv
getAutoOrScaleIndent(props['px'], scaleIndent),

border: props.border,
resize: props.resize,
overflow: props.overflow,
borderRadius: props.borderRadius ? colorResolver(props.borderRadius) : undefined,
backgroundColor: props.bg ? colorResolver(props.bg) : undefined,
});
Expand All @@ -277,7 +276,9 @@ export default function useBox<T extends BoxProps>(
bg,
border,
borderRadius,
resize,
flex,
overflow,
w,
h,
wMin,
Expand Down Expand Up @@ -351,6 +352,8 @@ export default function useBox<T extends BoxProps>(
border,
borderRadius,
bg,
resize,
overflow,
]);

const styles = sstyled(style);
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 2 additions & 2 deletions semcore/d3-chart/__tests__/axe-tests/d3-chart.axe-test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ test.describe(`@d3-chart ${TAG.ACCESSIBILITY}`, () => {
expect(violations).toEqual([]);
});

test('adaptive-chart', async ({ page }) => {
await loadPage(page, 'stories/components/d3-chart/docs/examples/d3-chart/adaptive-chart.tsx', 'en');
test('responsive-low-level-chart', async ({ page }) => {
await loadPage(page, 'stories/components/d3-chart/docs/examples/d3-chart/responsive-low-level-chart.tsx', 'en');
const violations = await getAccessibilityViolations({ page });
expect(violations).toEqual([]);
});
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion semcore/d3-chart/__tests__/d3-chart-base.browser-test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -229,7 +229,7 @@ test.describe(`${TAG.VISUAL}`, () => {
test('Verify chart looks good on small resolutions', {
tag: [TAG.PRIORITY_MEDIUM, '@d3-chart'],
}, async ({ page }) => {
await loadPage(page, 'stories/components/d3-chart/docs/examples/d3-chart/adaptive-chart.tsx', 'en');
await loadPage(page, 'stories/components/d3-chart/docs/examples/d3-chart/responsive-low-level-chart.tsx', 'en');

await page.setViewportSize({ width: 768, height: 1024 });
await page.waitForTimeout(500);
Expand Down
88 changes: 83 additions & 5 deletions semcore/d3-chart/src/component/Chart/AbstractChart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ import type { Intergalactic } from '@semcore/core';
import { Component, Root, sstyled } from '@semcore/core';
import { extractAriaProps } from '@semcore/core/lib/utils/ariaProps';
import { callAllEventHandlers } from '@semcore/core/lib/utils/assignProps';
import canUseDOM from '@semcore/core/lib/utils/canUseDOM';
import cssToIntDefault from '@semcore/core/lib/utils/cssToIntDefault';
import trottle from '@semcore/core/lib/utils/rafTrottle';
import { Text } from '@semcore/typography';
import type { ScaleBand, ScaleLinear, ScaleTime } from 'd3-scale';
import React, { Fragment } from 'react';
Expand All @@ -24,6 +27,9 @@ export type ChartState = {
dataDefinitions: Array<LegendItem & { columns: React.ReactNode[] }>;
highlightedLine: number;
withTrend: boolean;

plotWidth: number;
plotHeight: number;
};

export const NOT_A_VALUE = 'n/a';
Expand All @@ -45,9 +51,20 @@ export abstract class AbstractChart<

protected dataHints = makeDataHintsContainer();

protected chartRef = React.createRef<HTMLElement>();
protected legendRef = React.createRef<HTMLDivElement>();

private observer: ResizeObserver | undefined;

constructor(props: Props) {
super(props);

this.handleResize = trottle(this.handleResize.bind(this));

if (canUseDOM() && (!props.plotWidth || !props.plotHeight)) {
this.observer = new ResizeObserver(this.handleResize);
}

this.setHighlightedLine = this.setHighlightedLine.bind(this);
this.handleChangeVisible = this.handleChangeVisible.bind(this);
this.handleMouseEnter = this.handleMouseEnter.bind(this);
Expand All @@ -60,15 +77,33 @@ export abstract class AbstractChart<
dataDefinitions: this.getDefaultDataDefinitions(),
highlightedLine: -1,
withTrend: false,
plotWidth: 0,
plotHeight: 0,
} as State;
}

public componentDidMount(): void {
this.observer?.observe(this.chartRef.current!);
}

public componentDidUpdate(prevProps: Props) {
if (prevProps.data !== this.props.data || prevProps.legendProps !== this.props.legendProps) {
this.setState({ dataDefinitions: this.getDefaultDataDefinitions() });
}
}

public componentWillUnmount(): void {
this.observer?.disconnect();
}

protected get plotWidth() {
return this.asProps.plotWidth ?? this.state.plotWidth;
}

protected get plotHeight() {
return this.asProps.plotHeight ?? this.state.plotHeight;
}

protected getDefaultDataDefinitions(): Array<LegendItem & { columns: React.ReactNode[] }> {
const { data, legendProps } = this.props;

Expand Down Expand Up @@ -398,7 +433,7 @@ export abstract class AbstractChart<
};

if (lProps.legendType === 'Table') {
return <ChartLegendTable {...(commonLegendProps as LegendTableProps)} />;
return <ChartLegendTable {...(commonLegendProps as LegendTableProps)} ref={this.legendRef} />;
}

if ('withTrend' in lProps) {
Expand All @@ -410,10 +445,10 @@ export abstract class AbstractChart<
onTrendIsVisibleChange: this.handleWithTrendChange,
};

return <ChartLegend {...(flexLegendProps as LegendFlexProps)} />;
return <ChartLegend {...(flexLegendProps as LegendFlexProps)} ref={this.legendRef} />;
}

return <ChartLegend {...(commonLegendProps as LegendFlexProps)} />;
return <ChartLegend {...(commonLegendProps as LegendFlexProps)} ref={this.legendRef} />;
}

protected renderAxis(): React.ReactNode {
Expand Down Expand Up @@ -531,13 +566,14 @@ export abstract class AbstractChart<

public render() {
const SChart = Root;
const { styles, plotWidth, plotHeight, data, patterns, a11yAltTextConfig, duration, eventEmitter, showTooltip } =
const { styles, data, patterns, a11yAltTextConfig, duration, eventEmitter, showTooltip } =
this.asProps;
const { plotWidth, plotHeight } = this;

const { extractedAriaProps } = extractAriaProps(this.asProps);

return sstyled(styles)(
<SChart render={Flex} gap={5} __excludeProps={['data', 'eventEmitter']} role='group'>
<SChart render={Flex} gap={5} __excludeProps={['data', 'eventEmitter']} role='group' ref={this.chartRef}>
{this.renderLegend()}
<Plot
data={data}
Expand All @@ -558,4 +594,46 @@ export abstract class AbstractChart<
</SChart>,
);
}

private handleResize(entities: ResizeObserverEntry[]) {
const { aspect, direction, onResize, plotWidth, plotHeight } = this.asProps;
const chartElement = this.chartRef.current;

if (!chartElement) return;

const legendElement = this.legendRef.current;
const computedStyles = window.getComputedStyle(chartElement);

let width: number = chartElement.clientWidth;
let height: number = chartElement.clientHeight;

if (legendElement) {
const gap: number = cssToIntDefault(computedStyles.gap, 0);
if (direction?.includes('column')) {
height = height - legendElement.clientHeight - gap;
} else {
width = width - legendElement.clientWidth - gap;
}
}

if (aspect) {
const minHeight = cssToIntDefault(computedStyles.getPropertyValue('min-height'));
const maxHeight = cssToIntDefault(computedStyles.getPropertyValue('max-height'));
height = width * aspect;

if (height < minHeight) {
height = minHeight;
}
if (height > maxHeight) {
height = maxHeight;
}
}

this.setState({
plotWidth: plotWidth ? 0 : width,
plotHeight: plotHeight ? 0 : height,
});

onResize?.([width, height], entities);
}
}
10 changes: 8 additions & 2 deletions semcore/d3-chart/src/component/Chart/AbstractChart.type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,12 +63,14 @@ export type BaseChartProps<T extends ListData | ObjectData> = FlexProps & {
data: T;
/**
* Width of plot
* @default width is 100% of the parent element.
*/
plotWidth: number;
plotWidth?: number;
/**
* Height of plot
* @default height is 100% of the parent element.
*/
plotHeight: number;
plotHeight?: number;

/** Enables charts patterns that enhances charts accessibility */
patterns?: PatternsConfig;
Expand Down Expand Up @@ -158,6 +160,10 @@ export type BaseChartProps<T extends ListData | ObjectData> = FlexProps & {
* Flag to show/hide legend
*/
showLegend?: boolean;
/** Relation between height and width dimensions block */
aspect?: number;
/** Callback which will be called after changing the block size */
onResize?: (size: [number, number], entries: ResizeObserverEntry[]) => void;
/**
* Props for Legend
*/
Expand Down
6 changes: 4 additions & 2 deletions semcore/d3-chart/src/component/Chart/AreaChart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@ class AreaChartComponent extends AbstractChart<
} as const;

get xScale() {
const { xScale, marginY = 40, plotWidth, data, groupKey } = this.asProps;
const { xScale, marginY = 40, data, groupKey } = this.asProps;
const { plotWidth } = this;

if (xScale) {
return xScale;
Expand All @@ -48,7 +49,8 @@ class AreaChartComponent extends AbstractChart<
}

get yScale(): ScaleLinear<any, any> {
const { yScale, marginX = 24, plotHeight, stacked } = this.asProps;
const { yScale, marginX = 24, stacked } = this.asProps;
const { plotHeight } = this;

if (yScale) {
return yScale;
Expand Down
6 changes: 3 additions & 3 deletions semcore/d3-chart/src/component/Chart/BarChart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -267,12 +267,11 @@ class BarChartComponent extends AbstractChart<
const {
marginY = 40,
marginX = 24,
plotWidth,
plotHeight,
invertAxis,
data,
groupKey,
} = this.asProps;
const { plotWidth, plotHeight } = this;

const testItem = data[0][groupKey];
const range = invertAxis
Expand All @@ -291,7 +290,8 @@ class BarChartComponent extends AbstractChart<
}

private get valueScale() {
const { marginY = 40, marginX = 24, plotWidth, plotHeight, invertAxis, type } = this.asProps;
const { marginY = 40, marginX = 24, invertAxis, type } = this.asProps;
const { plotWidth, plotHeight } = this;

const max = type === 'stack' ? super.maxStackedValue : Math.max(...super.flatValues);

Expand Down
6 changes: 4 additions & 2 deletions semcore/d3-chart/src/component/Chart/BubbleChart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,8 @@ class BubbleChartComponent extends AbstractChart<
}

get xScale() {
const { xScale, marginY = 30, plotWidth, data } = this.asProps;
const { xScale, marginY = 30, data } = this.asProps;
const { plotWidth } = this;

if (xScale) {
return xScale;
Expand All @@ -85,7 +86,8 @@ class BubbleChartComponent extends AbstractChart<
}

get yScale(): ScaleLinear<any, any> {
const { yScale, marginX = 30, plotHeight, data } = this.asProps;
const { yScale, marginX = 30, data } = this.asProps;
const { plotHeight } = this;

if (yScale) {
return yScale;
Expand Down
Loading
Loading