Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[charts] Add watermark on the pro ResponsiveChartContainer #13398

Merged
merged 3 commits into from
Jun 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import * as React from 'react';
import { expect } from 'chai';
import { createRenderer, screen, waitFor } from '@mui/internal-test-utils';
import { LicenseInfo } from '@mui/x-license';
import { ResponsiveChartContainerPro } from './ResponsiveChartContainerPro';

describe('<ResponsiveChartContainerPro /> - License', () => {
const { render } = createRenderer();

it('should render watermark when the license is missing', async () => {
LicenseInfo.setLicenseKey('');

expect(() =>
render(<ResponsiveChartContainerPro series={[]} width={100} height={100} />),
).toErrorDev(['MUI X: Missing license key.']);

await waitFor(() => {
expect(screen.getByText('MUI X Missing license key')).to.not.equal(null);
});
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,255 @@
import * as React from 'react';
import PropTypes from 'prop-types';
import { useLicenseVerifier, Watermark } from '@mui/x-license';
import { ChartContainer } from '@mui/x-charts/ChartContainer';
import { ResponsiveChartContainerProps } from '@mui/x-charts/ResponsiveChartContainer';
import { ResizableContainer, useChartContainerDimensions } from '@mui/x-charts/internals';
import { getReleaseInfo } from '../internals/utils/releaseInfo';

export interface ResponsiveChartContainerProProps extends ResponsiveChartContainerProps {}

const releaseInfo = getReleaseInfo();

const ResponsiveChartContainerPro = React.forwardRef(function ResponsiveChartContainerPro(
props: ResponsiveChartContainerProProps,
ref,
) {
const { width: inWidth, height: inHeight, ...other } = props;
const [containerRef, width, height] = useChartContainerDimensions(inWidth, inHeight);

useLicenseVerifier('x-charts-pro', releaseInfo);

return (
<ResizableContainer ref={containerRef} ownerState={{ width: inWidth, height: inHeight }}>
{width && height ? (
<ChartContainer {...other} width={width} height={height} ref={ref} />
) : null}
<Watermark packageName="x-charts-pro" releaseInfo={releaseInfo} />
</ResizableContainer>
);
});

ResponsiveChartContainerPro.propTypes = {
// ----------------------------- Warning --------------------------------
// | These PropTypes are generated from the TypeScript type definitions |
// | To update them edit the TypeScript types and run "pnpm proptypes" |
// ----------------------------------------------------------------------
children: PropTypes.node,
className: PropTypes.string,
/**
* Color palette used to colorize multiple series.
* @default blueberryTwilightPalette
*/
colors: PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.string), PropTypes.func]),
/**
* An array of objects that can be used to populate series and axes data using their `dataKey` property.
*/
dataset: PropTypes.arrayOf(PropTypes.object),
desc: PropTypes.string,
/**
* If `true`, the charts will not listen to the mouse move event.
* It might break interactive features, but will improve performance.
* @default false
*/
disableAxisListener: PropTypes.bool,
/**
* The height of the chart in px. If not defined, it takes the height of the parent element.
*/
height: PropTypes.number,
/**
* The item currently highlighted. Turns highlighting into a controlled prop.
*/
highlightedItem: PropTypes.shape({
dataIndex: PropTypes.number,
seriesId: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
}),
/**
* The margin between the SVG and the drawing area.
* It's used for leaving some space for extra information such as the x- and y-axis or legend.
* Accepts an object with the optional properties: `top`, `bottom`, `left`, and `right`.
* @default object Depends on the charts type.
*/
margin: PropTypes.shape({
bottom: PropTypes.number,
left: PropTypes.number,
right: PropTypes.number,
top: PropTypes.number,
}),
/**
* The callback fired when the highlighted item changes.
*
* @param {HighlightItemData | null} highlightedItem The newly highlighted item.
*/
onHighlightChange: PropTypes.func,
/**
* An array of plugins defining how to preprocess data.
* If not provided, the container supports line, bar, scatter and pie charts.
*/
plugins: PropTypes.arrayOf(PropTypes.object),
/**
* The array of series to display.
* Each type of series has its own specificity.
* Please refer to the appropriate docs page to learn more about it.
*/
series: PropTypes.arrayOf(PropTypes.object).isRequired,
sx: PropTypes.oneOfType([
PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.func, PropTypes.object, PropTypes.bool])),
PropTypes.func,
PropTypes.object,
]),
title: PropTypes.string,
viewBox: PropTypes.shape({
height: PropTypes.number,
width: PropTypes.number,
x: PropTypes.number,
y: PropTypes.number,
}),
/**
* The width of the chart in px. If not defined, it takes the width of the parent element.
*/
width: PropTypes.number,
/**
* The configuration of the x-axes.
* If not provided, a default axis config is used.
* An array of [[AxisConfig]] objects.
*/
xAxis: PropTypes.arrayOf(
PropTypes.shape({
axisId: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
classes: PropTypes.object,
colorMap: PropTypes.oneOfType([
PropTypes.shape({
colors: PropTypes.arrayOf(PropTypes.string).isRequired,
type: PropTypes.oneOf(['ordinal']).isRequired,
unknownColor: PropTypes.string,
values: PropTypes.arrayOf(
PropTypes.oneOfType([PropTypes.instanceOf(Date), PropTypes.number, PropTypes.string])
.isRequired,
),
}),
PropTypes.shape({
color: PropTypes.oneOfType([
PropTypes.arrayOf(PropTypes.string.isRequired),
PropTypes.func,
]).isRequired,
max: PropTypes.oneOfType([PropTypes.instanceOf(Date), PropTypes.number]),
min: PropTypes.oneOfType([PropTypes.instanceOf(Date), PropTypes.number]),
type: PropTypes.oneOf(['continuous']).isRequired,
}),
PropTypes.shape({
colors: PropTypes.arrayOf(PropTypes.string).isRequired,
thresholds: PropTypes.arrayOf(
PropTypes.oneOfType([PropTypes.instanceOf(Date), PropTypes.number]).isRequired,
).isRequired,
type: PropTypes.oneOf(['piecewise']).isRequired,
}),
]),
data: PropTypes.array,
dataKey: PropTypes.string,
disableLine: PropTypes.bool,
disableTicks: PropTypes.bool,
fill: PropTypes.string,
hideTooltip: PropTypes.bool,
id: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
label: PropTypes.string,
labelFontSize: PropTypes.number,
labelStyle: PropTypes.object,
max: PropTypes.oneOfType([PropTypes.instanceOf(Date), PropTypes.number]),
min: PropTypes.oneOfType([PropTypes.instanceOf(Date), PropTypes.number]),
position: PropTypes.oneOf(['bottom', 'top']),
reverse: PropTypes.bool,
scaleType: PropTypes.oneOf(['band', 'linear', 'log', 'point', 'pow', 'sqrt', 'time', 'utc']),
slotProps: PropTypes.object,
slots: PropTypes.object,
stroke: PropTypes.string,
tickFontSize: PropTypes.number,
tickInterval: PropTypes.oneOfType([
PropTypes.oneOf(['auto']),
PropTypes.array,
PropTypes.func,
]),
tickLabelInterval: PropTypes.oneOfType([PropTypes.oneOf(['auto']), PropTypes.func]),
tickLabelPlacement: PropTypes.oneOf(['middle', 'tick']),
tickLabelStyle: PropTypes.object,
tickMaxStep: PropTypes.number,
tickMinStep: PropTypes.number,
tickNumber: PropTypes.number,
tickPlacement: PropTypes.oneOf(['end', 'extremities', 'middle', 'start']),
tickSize: PropTypes.number,
valueFormatter: PropTypes.func,
}),
),
/**
* The configuration of the y-axes.
* If not provided, a default axis config is used.
* An array of [[AxisConfig]] objects.
*/
yAxis: PropTypes.arrayOf(
PropTypes.shape({
axisId: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
classes: PropTypes.object,
colorMap: PropTypes.oneOfType([
PropTypes.shape({
colors: PropTypes.arrayOf(PropTypes.string).isRequired,
type: PropTypes.oneOf(['ordinal']).isRequired,
unknownColor: PropTypes.string,
values: PropTypes.arrayOf(
PropTypes.oneOfType([PropTypes.instanceOf(Date), PropTypes.number, PropTypes.string])
.isRequired,
),
}),
PropTypes.shape({
color: PropTypes.oneOfType([
PropTypes.arrayOf(PropTypes.string.isRequired),
PropTypes.func,
]).isRequired,
max: PropTypes.oneOfType([PropTypes.instanceOf(Date), PropTypes.number]),
min: PropTypes.oneOfType([PropTypes.instanceOf(Date), PropTypes.number]),
type: PropTypes.oneOf(['continuous']).isRequired,
}),
PropTypes.shape({
colors: PropTypes.arrayOf(PropTypes.string).isRequired,
thresholds: PropTypes.arrayOf(
PropTypes.oneOfType([PropTypes.instanceOf(Date), PropTypes.number]).isRequired,
).isRequired,
type: PropTypes.oneOf(['piecewise']).isRequired,
}),
]),
data: PropTypes.array,
dataKey: PropTypes.string,
disableLine: PropTypes.bool,
disableTicks: PropTypes.bool,
fill: PropTypes.string,
hideTooltip: PropTypes.bool,
id: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
label: PropTypes.string,
labelFontSize: PropTypes.number,
labelStyle: PropTypes.object,
max: PropTypes.oneOfType([PropTypes.instanceOf(Date), PropTypes.number]),
min: PropTypes.oneOfType([PropTypes.instanceOf(Date), PropTypes.number]),
position: PropTypes.oneOf(['left', 'right']),
reverse: PropTypes.bool,
scaleType: PropTypes.oneOf(['band', 'linear', 'log', 'point', 'pow', 'sqrt', 'time', 'utc']),
slotProps: PropTypes.object,
slots: PropTypes.object,
stroke: PropTypes.string,
tickFontSize: PropTypes.number,
tickInterval: PropTypes.oneOfType([
PropTypes.oneOf(['auto']),
PropTypes.array,
PropTypes.func,
]),
tickLabelInterval: PropTypes.oneOfType([PropTypes.oneOf(['auto']), PropTypes.func]),
tickLabelPlacement: PropTypes.oneOf(['middle', 'tick']),
tickLabelStyle: PropTypes.object,
tickMaxStep: PropTypes.number,
tickMinStep: PropTypes.number,
tickNumber: PropTypes.number,
tickPlacement: PropTypes.oneOf(['end', 'extremities', 'middle', 'start']),
tickSize: PropTypes.number,
valueFormatter: PropTypes.func,
}),
),
} as any;

export { ResponsiveChartContainerPro };
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './ResponsiveChartContainerPro';
27 changes: 26 additions & 1 deletion packages/x-charts-pro/src/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,26 @@
export * from '@mui/x-charts';
export * from '@mui/x-charts/constants';
export * from '@mui/x-charts/context';
export * from '@mui/x-charts/hooks';
export * from '@mui/x-charts/colorPalettes';
export * from '@mui/x-charts/models';
export * from '@mui/x-charts/ChartsClipPath';
export * from '@mui/x-charts/ChartsReferenceLine';
export * from '@mui/x-charts/ChartsAxis';
export * from '@mui/x-charts/ChartsXAxis';
export * from '@mui/x-charts/ChartsYAxis';
export * from '@mui/x-charts/ChartsGrid';
export * from '@mui/x-charts/ChartsText';
export * from '@mui/x-charts/ChartsTooltip';
export * from '@mui/x-charts/ChartsLegend';
export * from '@mui/x-charts/ChartsAxisHighlight';
export * from '@mui/x-charts/ChartsVoronoiHandler';
export * from '@mui/x-charts/ChartsOnAxisClickHandler';
export * from '@mui/x-charts/BarChart';
export * from '@mui/x-charts/LineChart';
export * from '@mui/x-charts/PieChart';
export * from '@mui/x-charts/ScatterChart';
export * from '@mui/x-charts/SparkLineChart';
export * from '@mui/x-charts/Gauge';
export * from '@mui/x-charts/ChartsSurface';

export * from './ResponsiveChartContainerPro';
15 changes: 15 additions & 0 deletions packages/x-charts-pro/src/internals/utils/releaseInfo.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { ponyfillGlobal } from '@mui/utils';

export const getReleaseInfo = () => {
const releaseInfo = '__RELEASE_INFO__';
if (process.env.NODE_ENV !== 'production') {
// A simple hack to set the value in the test environment (has no build step).
// eslint-disable-next-line no-useless-concat
if (releaseInfo === '__RELEASE' + '_INFO__') {
// eslint-disable-next-line no-underscore-dangle
return ponyfillGlobal.__MUI_RELEASE_INFO__;
}
}

return releaseInfo;
};
2 changes: 1 addition & 1 deletion packages/x-charts/.mocharc.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,5 +30,5 @@ module.exports = {
'**/build/**',
'docs/.next/**',
],
spec: ['packages/x-charts/**/*.test.{js,ts,tsx}'],
spec: ['packages/x-charts{,-pro,-premium}/**/*.test.{js,ts,tsx}'],
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { styled } from '@mui/material/styles';
import type { ResponsiveChartContainerProps } from './ResponsiveChartContainer';

/**
* Wrapping div that take the shape of its parent.
*
* @ignore - do not document.
*/
export const ResizableContainer = styled('div', {
name: 'MuiResponsiveChart',
slot: 'Container',
})<{ ownerState: Pick<ResponsiveChartContainerProps, 'width' | 'height'> }>(({ ownerState }) => ({
width: ownerState.width ?? '100%',
height: ownerState.height ?? '100%',
display: 'flex',
position: 'relative',
flexGrow: 1,
flexDirection: 'column',
alignItems: 'center',
justifyContent: 'center',
overflow: 'hidden',
'&>svg': {
width: '100%',
height: '100%',
},
}));
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import * as React from 'react';
import PropTypes from 'prop-types';
import { styled } from '@mui/material/styles';
import { ChartContainer, ChartContainerProps } from '../ChartContainer';
import { useChartContainerDimensions } from './useChartContainerDimensions';
import { ResizableContainer } from './ResizableContainer';

export interface ResponsiveChartContainerProps
extends Omit<ChartContainerProps, 'width' | 'height'> {
Expand All @@ -16,25 +16,6 @@ export interface ResponsiveChartContainerProps
height?: number;
}

const ResizableContainer = styled('div', {
name: 'MuiResponsiveChart',
slot: 'Container',
})<{ ownerState: Pick<ResponsiveChartContainerProps, 'width' | 'height'> }>(({ ownerState }) => ({
width: ownerState.width ?? '100%',
height: ownerState.height ?? '100%',
display: 'flex',
position: 'relative',
flexGrow: 1,
flexDirection: 'column',
alignItems: 'center',
justifyContent: 'center',
overflow: 'hidden',
'&>svg': {
width: '100%',
height: '100%',
},
}));

const ResponsiveChartContainer = React.forwardRef(function ResponsiveChartContainer(
props: ResponsiveChartContainerProps,
ref,
Expand Down
Loading