diff --git a/conf/base.yaml b/conf/base.yaml index 3f0ddf1fa..39581a5ee 100644 --- a/conf/base.yaml +++ b/conf/base.yaml @@ -1,4 +1,4 @@ -baseUrl: http://localhost:8000 +baseUrl: http://localhost:9000 capture: pages: - id: home @@ -28,6 +28,10 @@ capture: - id: voronoi charts: + - pkg: boxplot + chart: boxplot + flavors: [svg] + - pkg: bump chart: bump flavors: [svg] diff --git a/packages/boxplot/README.md b/packages/boxplot/README.md index 4eedf0918..d07d1ac9f 100644 --- a/packages/boxplot/README.md +++ b/packages/boxplot/README.md @@ -2,8 +2,8 @@ # `@nivo/boxplot` -[![version](https://img.shields.io/npm/v/@nivo/boxplot?style=for-the-badge)](https://www.npmjs.com/package/@nivo/bar) -[![downloads](https://img.shields.io/npm/dm/@nivo/boxplot?style=for-the-badge)](https://www.npmjs.com/package/@nivo/bar) +[![version](https://img.shields.io/npm/v/@nivo/boxplot?style=for-the-badge)](https://www.npmjs.com/package/@nivo/boxplot) +[![downloads](https://img.shields.io/npm/dm/@nivo/boxplot?style=for-the-badge)](https://www.npmjs.com/package/@nivo/boxplot) ## Boxplot diff --git a/packages/boxplot/package.json b/packages/boxplot/package.json index 6a18dd67f..01e8d4c42 100644 --- a/packages/boxplot/package.json +++ b/packages/boxplot/package.json @@ -1,6 +1,6 @@ { "name": "@nivo/boxplot", - "version": "0.79.1", + "version": "0.81.0", "license": "MIT", "author": { "name": "Raphaël Benitte", @@ -34,24 +34,24 @@ "!dist/tsconfig.tsbuildinfo" ], "dependencies": { - "@nivo/annotations": "0.79.1", - "@nivo/axes": "0.79.0", - "@nivo/colors": "0.79.1", - "@nivo/legends": "0.79.1", - "@nivo/scales": "0.79.0", - "@nivo/tooltip": "0.79.0", - "@react-spring/web": "9.4.5", + "@nivo/annotations": "0.81.0", + "@nivo/axes": "0.81.0", + "@nivo/colors": "0.81.0", + "@nivo/legends": "0.81.0", + "@nivo/scales": "0.81.0", + "@nivo/tooltip": "0.81.0", + "@react-spring/web": "9.4.5 || ^9.7.2", "d3-scale": "^3.2.3", - "d3-shape": "^1.2.2", + "d3-shape": "^1.3.5", "lodash": "^4.17.21" }, "devDependencies": { - "@nivo/core": "0.79.0", - "@nivo/generators": "0.79.0" + "@nivo/core": "0.81.0", + "@nivo/generators": "0.81.0" }, "peerDependencies": { - "@nivo/core": "0.79.0", - "react": ">= 16.14.0 < 18.0.0" + "@nivo/core": "0.81.0", + "react": ">= 16.14.0 < 19.0.0" }, "publishConfig": { "access": "public" diff --git a/scripts/capture.js b/scripts/capture.js index a2ae45849..2eb086ac7 100644 --- a/scripts/capture.js +++ b/scripts/capture.js @@ -40,19 +40,16 @@ const getChartUrl = (chart, flavor) => { return `${Path.join(...chunks)}/?capture=1` } -const getChartIconFilePath = (chart, variant) => Path.join( - websiteIconsDir, - `${chart}-${variant}.png` -) -const getHomeDemoFilePath = (id) => Path.join(websiteHomeDemosDir, `${id}.png`) -const getPageUrl = (path) => { - return `${Path.join(config.get('baseUrl'), path)}/?capture=1` -} -const getPageFilePath = (id) => Path.join(websitePagesDir, `${id}.png`) - -const delay = (time) => new Promise((resolve) => { - setTimeout(resolve, time) -}) +const getChartIconFilePath = (chart, variant) => + Path.join(websiteIconsDir, `${chart}-${variant}.png`) +const getHomeDemoFilePath = id => Path.join(websiteHomeDemosDir, `${id}.png`) +const getPageUrl = path => `${Path.join(config.get('baseUrl'), path)}/?capture=1` +const getPageFilePath = id => Path.join(websitePagesDir, `${id}.png`) + +const delay = time => + new Promise(resolve => { + setTimeout(resolve, time) + }) const captureChart = async (page, { pkg, chart, flavor, theme }) => { const url = getChartUrl(chart, flavor) @@ -100,7 +97,7 @@ const captureCharts = async () => { try { const browser = await puppeteer.launch({ - headless: true + headless: true, }) const page = await browser.newPage() @@ -131,15 +128,11 @@ const captureIcons = async () => { try { const browser = await puppeteer.launch({ - headless: true + headless: false, }) const page = await browser.newPage() await page.setViewport(VIEWPORT.icons) - await page.goto(`${Path.join( - config.get('baseUrl'), - 'internal', - 'icons' - )}/`) + await page.goto(`${Path.join(config.get('baseUrl'), 'internal', 'icons')}/?capture=1`) for (let icon of icons) { console.log(chalk`{yellow Capturing {white ${icon}} chart icons}`) @@ -186,15 +179,11 @@ const captureHomeDemos = async () => { try { const browser = await puppeteer.launch({ - headless: true + headless: true, }) const page = await browser.newPage() await page.setViewport(VIEWPORT.homeDemos) - await page.goto(`${Path.join( - config.get('baseUrl'), - 'internal', - 'home-demos' - )}/?capture=1`) + await page.goto(`${Path.join(config.get('baseUrl'), 'internal', 'home-demos')}/?capture=1`) for (let demo of demos) { console.log(chalk`{yellow Capturing {white ${demo.id}} demo}`) @@ -239,7 +228,7 @@ const capturePages = async () => { try { const browser = await puppeteer.launch({ - headless: true + headless: true, }) const page = await browser.newPage() @@ -287,10 +276,10 @@ const capturePages = async () => { const run = async () => { - await capturePages() + // await capturePages() // await captureHomeDemos() // await captureCharts() - // await captureIcons() + await captureIcons() } run() diff --git a/website/src/@types/file_types.d.ts b/website/src/@types/file_types.d.ts index 96576d8f1..47049a68f 100644 --- a/website/src/@types/file_types.d.ts +++ b/website/src/@types/file_types.d.ts @@ -24,6 +24,15 @@ declare module '*bar/meta.yml' { export default meta } +declare module '*boxplot/meta.yml' { + const meta: { + flavors: ChartMetaFlavors + BoxPlot: ChartMeta + } + + export default meta +} + declare module '*bullet/meta.yml' { const meta: { flavors: ChartMetaFlavors diff --git a/website/src/assets/icons.png b/website/src/assets/icons.png index 9f4e6c059..b8349a12a 100644 Binary files a/website/src/assets/icons.png and b/website/src/assets/icons.png differ diff --git a/website/src/assets/icons/boxplot-dark-colored.png b/website/src/assets/icons/boxplot-dark-colored.png new file mode 100644 index 000000000..5ca6f8818 Binary files /dev/null and b/website/src/assets/icons/boxplot-dark-colored.png differ diff --git a/website/src/assets/icons/boxplot-dark-neutral.png b/website/src/assets/icons/boxplot-dark-neutral.png new file mode 100644 index 000000000..0b2cc85e6 Binary files /dev/null and b/website/src/assets/icons/boxplot-dark-neutral.png differ diff --git a/website/src/assets/icons/boxplot-light-colored.png b/website/src/assets/icons/boxplot-light-colored.png new file mode 100644 index 000000000..9b0356c84 Binary files /dev/null and b/website/src/assets/icons/boxplot-light-colored.png differ diff --git a/website/src/assets/icons/boxplot-light-neutral.png b/website/src/assets/icons/boxplot-light-neutral.png new file mode 100644 index 000000000..62d522375 Binary files /dev/null and b/website/src/assets/icons/boxplot-light-neutral.png differ diff --git a/website/src/assets/icons@2x.png b/website/src/assets/icons@2x.png index 82020e335..cad7ea737 100644 Binary files a/website/src/assets/icons@2x.png and b/website/src/assets/icons@2x.png differ diff --git a/website/src/components/icons/BoxPlotIcon.tsx b/website/src/components/icons/BoxPlotIcon.tsx new file mode 100644 index 000000000..eed95828e --- /dev/null +++ b/website/src/components/icons/BoxPlotIcon.tsx @@ -0,0 +1,96 @@ +import React, { useMemo } from 'react' +import { BoxPlot, BoxPlotSvgProps } from '@nivo/boxplot' +import boxplotLightNeutralImg from '../../assets/icons/boxplot-light-neutral.png' +import boxplotLightColoredImg from '../../assets/icons/boxplot-light-colored.png' +import boxplotDarkNeutralImg from '../../assets/icons/boxplot-dark-neutral.png' +import boxplotDarkColoredImg from '../../assets/icons/boxplot-dark-colored.png' +import { ICON_SIZE, Icon, colors, IconImg } from './styled' +import { IconType } from './types' + +const quantiles = [0.1, 0.25, 0.5, 0.75, 0.9] + +const chartProps: BoxPlotSvgProps = { + width: ICON_SIZE, + height: ICON_SIZE, + layout: 'horizontal', + margin: { + top: 6, + right: 2, + bottom: 6, + left: 2, + }, + padding: 0.15, + enableGridX: false, + enableGridY: false, + axisTop: null, + axisRight: null, + axisBottom: null, + axisLeft: null, + // Using pre-computed data for predictability. + data: [ + { + group: 'A', + subGroup: '', + quantiles: quantiles, + values: [1.5, 2.2, 2.9, 3.6, 4.3], + extrema: [1, 5], + mean: 2.5, + n: 100, + }, + { + group: 'B', + subGroup: '', + quantiles: quantiles, + values: [2, 2.5, 3.7, 4.5, 5], + extrema: [2, 7], + mean: 3.75, + n: 200, + }, + { + group: 'C', + subGroup: '', + quantiles: quantiles, + values: [1, 1.5, 2, 2.5, 3], + extrema: [2, 7], + mean: 3.75, + n: 200, + }, + ], + medianWidth: 2, + whiskerWidth: 3, + whiskerEndSize: 0.6, + borderRadius: 2, + isInteractive: false, + animate: false, +} + +const BoxPlotIconItem = ({ type }: { type: IconType }) => { + const typedColors = useMemo( + () => [colors[type].colors[1], colors[type].colors[2], colors[type].colors[4]], + [type] + ) + + return ( + + + {...chartProps} + colors={[typedColors[1]]} + medianColor={typedColors[2]} + whiskerColor={typedColors[2]} + /> + + ) +} + +export const BoxPlotIcon = () => ( + <> + + + + + + + + + +) diff --git a/website/src/components/icons/Icons.tsx b/website/src/components/icons/Icons.tsx index 936a3559c..4861e6071 100644 --- a/website/src/components/icons/Icons.tsx +++ b/website/src/components/icons/Icons.tsx @@ -2,6 +2,7 @@ import React, { Fragment } from 'react' import { Container } from './styled' import { AreaBumpIcon } from './AreaBumpIcon' import { BarIcon } from './BarIcon' +import { BoxPlotIcon } from './BoxPlotIcon' import { BulletIcon } from './BulletIcon' import { BumpIcon } from './BumpIcon' import { CalendarIcon } from './CalendarIcon' @@ -63,6 +64,7 @@ export const Icons = () => ( + diff --git a/website/src/data/components/boxplot/generator.ts b/website/src/data/components/boxplot/generator.ts new file mode 100644 index 000000000..3c9e72191 --- /dev/null +++ b/website/src/data/components/boxplot/generator.ts @@ -0,0 +1,15 @@ +import { generateBoxPlotData } from '@nivo/generators' + +export const generateLightDataSet = () => + generateBoxPlotData([ + { group: 'Alpha', subgroup: 'A', mu: 5, sd: 1, n: 20 }, + { group: 'Alpha', subgroup: 'B', mu: 6, sd: 1, n: 20 }, + { group: 'Beta', subgroup: 'A', mu: 8, sd: 1.4, n: 20 }, + { group: 'Beta', subgroup: 'B', mu: 7.5, sd: 1.4, n: 20 }, + { group: 'Gamma', subgroup: 'A', mu: 5, sd: 1, n: 20 }, + { group: 'Gamma', subgroup: 'B', mu: 7.2, sd: 1.8, n: 20 }, + { group: 'Delta', subgroup: 'A', mu: 5, sd: 1, n: 20 }, + { group: 'Delta', subgroup: 'B', mu: 6, sd: 1, n: 20 }, + { group: 'Epsilon', subgroup: 'A', mu: 5, sd: 1.4, n: 20 }, + { group: 'Epsilon', subgroup: 'B', mu: 6, sd: 3, n: 20 }, + ]) diff --git a/website/src/data/components/boxplot/mapper.ts b/website/src/data/components/boxplot/mapper.ts new file mode 100644 index 000000000..744c870cd --- /dev/null +++ b/website/src/data/components/boxplot/mapper.ts @@ -0,0 +1,14 @@ +import { settingsMapper, mapAxis, mapFormat } from '../../../lib/settings' + +export default settingsMapper( + { + valueFormat: mapFormat, + axisTop: mapAxis('top'), + axisRight: mapAxis('right'), + axisBottom: mapAxis('bottom'), + axisLeft: mapAxis('left'), + }, + { + exclude: ['enable axisTop', 'enable axisRight', 'enable axisBottom', 'enable axisLeft'], + } +) diff --git a/website/src/data/components/boxplot/meta.yml b/website/src/data/components/boxplot/meta.yml new file mode 100644 index 000000000..54b3cb35b --- /dev/null +++ b/website/src/data/components/boxplot/meta.yml @@ -0,0 +1,34 @@ +flavors: + - flavor: svg + path: /boxplot/ + +BoxPlot: + package: '@nivo/boxplot' + tags: [] + stories: [] + # - label: Using markers + # link: bar--with-marker + # - label: Stacked diverging bar chart + # link: bar--diverging-stacked + # - label: Grouped diverging bar chart + # link: bar--diverging-grouped + # - label: Custom bar element + # link: bar--custom-bar-item + # - label: Formatting values + # link: bar--with-formatted-values + # - label: Using custom tooltip + # link: bar--custom-tooltip + # - label: Custom axis ticks + # link: bar--custom-axis-ticks + # - label: With symlog scale + # link: bar--with-symlog-scale + # - label: Race bar chart + # link: bar--race-chart + # - label: Initial hidden ids + # link: bar--initial-hidden-ids + # - label: Using custom label for legends + # link: bar--custom-legend-labels + # - label: Using annotations + # link: bar--with-annotations + description: | + BoxPlot description. diff --git a/website/src/data/components/boxplot/props.ts b/website/src/data/components/boxplot/props.ts new file mode 100644 index 000000000..9f102c85b --- /dev/null +++ b/website/src/data/components/boxplot/props.ts @@ -0,0 +1,477 @@ +import { svgDefaultProps } from '@nivo/boxplot' +import { + themeProperty, + motionProperties, + defsProperties, + getLegendsProps, + groupProperties, +} from '../../../lib/componentProperties' +import { + chartDimensions, + ordinalColors, + chartGrid, + axes, + isInteractive, + commonAccessibilityProps, +} from '../../../lib/chart-properties' +import { ChartProperty, Flavor } from '../../../types' + +const allFlavors: Flavor[] = ['svg'] + +const props: ChartProperty[] = [ + { + key: 'value', + group: 'Base', + flavors: allFlavors, + help: 'Value accessor.', + description: ` + Define how to access the value of each datum, + by default, nivo will look for the \`value\` property. + `, + type: 'string | (datum: RawDatum): number', + required: false, + defaultValue: svgDefaultProps.value, + }, + { + key: 'minValue', + group: 'Base', + help: 'Minimum value.', + description: ` + Minimum value, if 'auto', + will use min value from the provided data. + `, + required: false, + flavors: allFlavors, + defaultValue: svgDefaultProps.minValue, + type: `number | 'auto'`, + control: { + type: 'switchableRange', + disabledValue: 'auto', + defaultValue: 0, + min: -10, + max: 10, + }, + }, + { + key: 'maxValue', + group: 'Base', + help: 'Maximum value.', + description: ` + Maximum value, if 'auto', + will use max value from the provided data. + `, + required: false, + flavors: allFlavors, + defaultValue: svgDefaultProps.maxValue, + type: `number | 'auto'`, + control: { + type: 'switchableRange', + disabledValue: 'auto', + defaultValue: 10, + min: 0, + max: 20, + }, + }, + { + key: 'groupBy', + help: 'How to group box plots belonging to the same series', + type: 'string | function', + flavors: allFlavors, + defaultValue: svgDefaultProps.groupBy, + group: 'Base', + }, + { + key: 'groups', + help: 'Specify the groups available, if omitted, it will be inferred from the provided data.', + type: 'string[]', + flavors: allFlavors, + defaultValue: svgDefaultProps.groups, + group: 'Base', + }, + { + key: 'subGroupBy', + help: 'Used for sub-grouping.', + type: 'string | function', + flavors: allFlavors, + defaultValue: svgDefaultProps.subGroupBy, + group: 'Base', + }, + { + key: 'subGroups', + help: 'Specify the sub-groups available, if omitted, it will be inferred from the provided data.', + type: 'string[]', + flavors: allFlavors, + defaultValue: svgDefaultProps.subGroups, + group: 'Base', + }, + { + key: 'quantiles', + group: 'Base', + help: 'Quantiles', + description: ` + Quantiles represent respectively: + + \`\`\` + [ + 0: minimum/lower fence, + 1: lower quartile/hinge, + 2: median/middle quartile, + 3: upper quartile/hinge, + 4: maximum/upper fence, + ] + \`\`\` + `, + type: '[number, number, number, number, number]', + flavors: allFlavors, + required: false, + defaultValue: svgDefaultProps.quantiles, + control: { + type: 'numberArray', + items: [ + { + label: 'minimum', + min: 0, + max: 1, + step: 0.05, + }, + { + label: 'lower quartile', + min: 0, + max: 1, + step: 0.05, + }, + { + label: 'median', + min: 0, + max: 1, + step: 0.05, + }, + { + label: 'upper quartile', + min: 0, + max: 1, + step: 0.05, + }, + { + label: 'maximum', + min: 0, + max: 1, + step: 0.05, + }, + ], + }, + }, + { + key: 'layout', + group: 'Base', + help: `Chart layout.`, + type: `'horizontal' | 'vertical'`, + flavors: allFlavors, + required: false, + defaultValue: svgDefaultProps.layout, + control: { + type: 'radio', + choices: [ + { label: 'horizontal', value: 'horizontal' }, + { label: 'vertical', value: 'vertical' }, + ], + }, + }, + { + key: 'padding', + help: 'Padding between each group or boxplot when there is a single group (ratio).', + type: 'number', + required: false, + flavors: allFlavors, + defaultValue: svgDefaultProps.padding, + group: 'Base', + control: { + type: 'range', + min: 0, + max: 0.9, + step: 0.05, + }, + }, + { + key: 'innerPadding', + help: 'Padding between grouped boxplots.', + type: 'number', + required: false, + flavors: allFlavors, + defaultValue: svgDefaultProps.innerPadding, + group: 'Base', + control: { + type: 'range', + unit: 'px', + min: 0, + max: 10, + }, + }, + ...chartDimensions(allFlavors), + themeProperty(allFlavors), + ordinalColors({ + flavors: allFlavors, + defaultValue: svgDefaultProps.colors, + }), + { + key: 'colorBy', + group: 'Style', + help: `How to assign individual colors, also impacts legends.`, + type: `'group' | 'subGroup'`, + flavors: allFlavors, + required: false, + defaultValue: svgDefaultProps.colorBy, + control: { + type: 'radio', + choices: [ + { label: 'group', value: 'group' }, + { label: 'subGroup', value: 'subGroup' }, + ], + }, + }, + { + key: 'opacity', + help: 'Opacity.', + required: false, + defaultValue: svgDefaultProps.opacity, + type: 'number', + flavors: allFlavors, + control: { type: 'opacity' }, + group: 'Style', + }, + { + key: 'borderRadius', + help: 'Interquartile Range box border radius.', + type: 'number', + flavors: allFlavors, + required: false, + defaultValue: svgDefaultProps.borderRadius, + group: 'Style', + control: { + type: 'range', + unit: 'px', + min: 0, + max: 36, + }, + }, + { + key: 'borderWidth', + help: 'Interquartile Range box border.', + type: 'number', + flavors: allFlavors, + required: false, + defaultValue: svgDefaultProps.borderWidth, + group: 'Style', + control: { type: 'lineWidth' }, + }, + { + key: 'borderColor', + help: 'Method to compute the Interquartile Range box border color.', + description: ` + how to compute border color, + [see dedicated documentation](self:/guides/colors). + `, + type: 'string | object | Function', + flavors: allFlavors, + required: false, + defaultValue: svgDefaultProps.borderColor, + group: 'Style', + control: { type: 'inheritedColor' }, + }, + { + key: 'medianWidth', + help: 'Median line width.', + type: 'number', + flavors: allFlavors, + required: false, + defaultValue: svgDefaultProps.medianWidth, + group: 'Style', + control: { type: 'lineWidth' }, + }, + { + key: 'medianColor', + help: 'Method to compute the median color.', + description: `[see dedicated documentation](self:/guides/colors).`, + type: 'string | object | Function', + flavors: allFlavors, + required: false, + defaultValue: svgDefaultProps.medianColor, + group: 'Style', + control: { type: 'inheritedColor' }, + }, + { + key: 'whiskerWidth', + help: 'Whisker line width.', + type: 'number', + flavors: allFlavors, + required: false, + defaultValue: svgDefaultProps.borderWidth, + group: 'Style', + control: { type: 'lineWidth' }, + }, + { + key: 'whiskerEndSize', + help: 'Whisker end size.', + type: 'number', + flavors: allFlavors, + required: false, + defaultValue: svgDefaultProps.whiskerEndSize, + group: 'Style', + control: { + type: 'range', + min: 0, + max: 1, + step: 0.05, + }, + }, + { + key: 'whiskerColor', + help: 'Method to compute the whiskers color.', + description: `[see dedicated documentation](self:/guides/colors).`, + type: 'string | object | Function', + flavors: allFlavors, + required: false, + defaultValue: svgDefaultProps.whiskerColor, + group: 'Style', + control: { type: 'inheritedColor' }, + }, + ...defsProperties('Style', ['svg']), + ...chartGrid({ + flavors: allFlavors, + xDefault: svgDefaultProps.enableGridX, + yDefault: svgDefaultProps.enableGridY, + values: true, + }), + ...axes({ flavors: allFlavors }), + { + key: 'layers', + group: 'Customization', + help: 'Defines the order of layers and add custom layers.', + description: ` + You can also use this to insert extra layers + in the chart, the extra layer should be a valid React component, + and will be passed to [\`createElement\`](https://react.dev/reference/react/createElement). + + It will receive the chart's context & computed data + and must return a valid SVG element. + `, + flavors: allFlavors, + required: false, + type: 'Array', + defaultValue: svgDefaultProps.layers, + }, + { + key: 'boxPlotComponent', + group: 'Customization', + help: 'Override the default box plot component.', + required: false, + type: 'React.FC>', + }, + isInteractive({ + flavors: ['svg'], + defaultValue: svgDefaultProps.isInteractive, + }), + { + key: 'activeOpacity', + help: 'Opacity when active.', + required: false, + defaultValue: svgDefaultProps.activeOpacity, + type: 'number', + flavors: allFlavors, + control: { type: 'opacity' }, + group: 'Interactivity', + }, + { + key: 'inactiveOpacity', + help: 'Opacity when inactive.', + required: false, + defaultValue: svgDefaultProps.inactiveOpacity, + type: 'number', + flavors: allFlavors, + control: { type: 'opacity' }, + group: 'Interactivity', + }, + { + key: 'tooltip', + flavors: allFlavors, + group: 'Interactivity', + type: 'Function', + required: false, + help: 'Tooltip custom component', + description: ` + A function allowing complete tooltip customisation, + it must return a valid HTML element and will receive + the boxplot summary. + `, + }, + { + key: 'legends', + flavors: allFlavors, + type: 'object[]', + help: `Optional chart's legends.`, + group: 'Legends', + required: false, + control: { + type: 'array', + props: getLegendsProps(['svg']), + shouldCreate: true, + addLabel: 'add legend', + shouldRemove: true, + getItemTitle: (index: number, legend: any) => + `legend[${index}]: ${legend.anchor}, ${legend.direction}`, + defaults: { + dataFrom: 'keys', + anchor: 'top-right', + direction: 'column', + justify: false, + translateX: 120, + translateY: 0, + itemWidth: 100, + itemHeight: 20, + itemsSpacing: 2, + symbolSize: 20, + itemDirection: 'left-to-right', + onClick: (data: any) => { + console.log(JSON.stringify(data, null, ' ')) + }, + }, + }, + }, + ...motionProperties(['svg'], svgDefaultProps, 'react-spring'), + ...commonAccessibilityProps(['svg']), + { + key: 'isFocusable', + flavors: ['svg'], + required: false, + group: 'Accessibility', + help: 'Make the root SVG element and each boxplot item focusable, for keyboard navigation.', + type: 'boolean', + control: { type: 'switch' }, + }, + { + key: 'boxPlotAriaLabel', + flavors: ['svg'], + required: false, + group: 'Accessibility', + help: '[aria-label](https://www.w3.org/TR/wai-aria/#aria-label) for boxplot items.', + type: '(data) => string', + }, + { + key: 'boxPlotAriaLabelledBy', + flavors: ['svg'], + required: false, + group: 'Accessibility', + help: '[aria-labelledby](https://www.w3.org/TR/wai-aria/#aria-labelledby) for boxplot items.', + type: '(data) => string', + }, + { + key: 'boxPlotAriaDescribedBy', + flavors: ['svg'], + required: false, + group: 'Accessibility', + help: '[aria-describedby](https://www.w3.org/TR/wai-aria/#aria-describedby) for boxplot items.', + type: '(data) => string', + }, +] + +export const groups = groupProperties(props) diff --git a/website/src/data/nav.ts b/website/src/data/nav.ts index c15bdea4e..3f6b1434d 100644 --- a/website/src/data/nav.ts +++ b/website/src/data/nav.ts @@ -1,5 +1,6 @@ import areaBump from './components/area-bump/meta.yml' import bar from './components/bar/meta.yml' +import boxplot from './components/boxplot/meta.yml' import circlePacking from './components/circle-packing/meta.yml' import bump from './components/bump/meta.yml' import bullet from './components/bullet/meta.yml' @@ -46,6 +47,14 @@ export const components: ChartNavData[] = [ api: true, }, }, + { + name: 'BoxPlot', + id: 'boxplot', + tags: boxplot.BoxPlot.tags, + flavors: { + svg: true, + }, + }, { name: 'CirclePacking', id: 'circle-packing', diff --git a/website/src/pages/boxplot/index.tsx b/website/src/pages/boxplot/index.tsx new file mode 100644 index 000000000..fdcf9e15d --- /dev/null +++ b/website/src/pages/boxplot/index.tsx @@ -0,0 +1,177 @@ +import React from 'react' +import { ResponsiveBoxPlot, svgDefaultProps } from '@nivo/boxplot' +import { ComponentTemplate } from '../../components/components/ComponentTemplate' +import meta from '../../data/components/boxplot/meta.yml' +import mapper from '../../data/components/boxplot/mapper' +import { generateLightDataSet } from '../../data/components/boxplot/generator' +import { groups } from '../../data/components/boxplot/props' +import { graphql, useStaticQuery } from 'gatsby' + +const initialProperties = { + margin: { + top: 60, + right: 140, + bottom: 60, + left: 60, + }, + + value: svgDefaultProps.value, + minValue: 0, + maxValue: 10, + groupBy: 'group', + subGroupBy: 'subgroup', + quantiles: svgDefaultProps.quantiles, + layout: svgDefaultProps.layout, + padding: 0.12, + innerPadding: 6, + + valueFormat: '.2f', + + enableGridX: true, + enableGridY: true, + axisTop: { + enable: true, + tickSize: 5, + tickPadding: 5, + tickRotation: 0, + legend: '', + legendOffset: 36, + }, + axisRight: { + enable: true, + tickSize: 5, + tickPadding: 5, + tickRotation: 0, + legend: '', + legendOffset: 0, + }, + axisBottom: { + enable: true, + tickSize: 5, + tickPadding: 5, + tickRotation: 0, + legend: 'group', + legendPosition: 'middle', + legendOffset: 32, + }, + axisLeft: { + enable: true, + tickSize: 5, + tickPadding: 5, + tickRotation: 0, + legend: 'value', + legendPosition: 'middle', + legendOffset: -40, + }, + + colors: { scheme: 'nivo' }, + colorBy: svgDefaultProps.colorBy, + opacity: svgDefaultProps.opacity, + borderRadius: 2, + borderWidth: 2, + borderColor: { + from: 'color', + modifiers: [['darker', 0.3]], + }, + medianWidth: 2, + medianColor: { + from: 'color', + modifiers: [['darker', 0.3]], + }, + whiskerWidth: svgDefaultProps.whiskerWidth, + whiskerEndSize: 0.6, + whiskerColor: { + from: 'color', + modifiers: [['darker', 0.3]], + }, + + isInteractive: true, + activeOpacity: svgDefaultProps.activeOpacity, + inactiveOpacity: svgDefaultProps.inactiveOpacity, + + animate: true, + motionConfig: 'stiff', + + isFocusable: svgDefaultProps.isFocusable, + + legends: [ + { + anchor: 'right', + direction: 'column', + justify: false, + translateX: 100, + translateY: 0, + itemWidth: 60, + itemHeight: 20, + itemsSpacing: 3, + itemTextColor: '#999', + itemDirection: 'left-to-right', + symbolSize: 20, + symbolShape: 'square', + onClick: d => { + alert(JSON.stringify(d, null, ' ')) + }, + effects: [ + { + on: 'hover', + style: { + itemTextColor: '#000', + }, + }, + ], + }, + ], +} + +const BoxPlot = () => { + const { + image: { + childImageSharp: { gatsbyImageData: image }, + }, + } = useStaticQuery(graphql` + query { + image: file(absolutePath: { glob: "**/src/assets/captures/chord.png" }) { + childImageSharp { + gatsbyImageData(layout: FIXED, width: 700, quality: 100) + } + } + } + `) + + return ( + data} + image={image} + > + {(properties, data, theme, logAction) => { + return ( + { + logAction({ + type: 'click', + label: `[boxplot] ${boxPlot.key} - ${boxPlot.label}`, + color: boxPlot.color, + data: boxPlot, + }) + }} + /> + ) + }} + + ) +} + +export default BoxPlot diff --git a/website/src/pages/chord/index.tsx b/website/src/pages/chord/index.tsx index 735b232d8..5942cd05b 100644 --- a/website/src/pages/chord/index.tsx +++ b/website/src/pages/chord/index.tsx @@ -139,7 +139,6 @@ const Chord = () => { }) }} onRibbonClick={ribbon => { - console.log(ribbon) logAction({ type: 'click', label: `[ribbon] ${ribbon.source.label} (${ribbon.source.formattedValue}) → ${ribbon.target.label} (${ribbon.target.formattedValue})`, diff --git a/website/src/styles/icons.css b/website/src/styles/icons.css index 883d73858..91ed46303 100644 --- a/website/src/styles/icons.css +++ b/website/src/styles/icons.css @@ -1,4 +1,4 @@ -/* glue: 0.13 hash: cc3bb7ef36 */ +/* glue: 0.13 hash: 78b58a59dc */ .sprite-icons-waffle-light-neutral, .sprite-icons-waffle-light-colored, .sprite-icons-waffle-dark-neutral, @@ -116,6 +116,10 @@ .sprite-icons-bullet-light-colored, .sprite-icons-bullet-dark-neutral, .sprite-icons-bullet-dark-colored, +.sprite-icons-boxplot-light-neutral, +.sprite-icons-boxplot-light-colored, +.sprite-icons-boxplot-dark-neutral, +.sprite-icons-boxplot-dark-colored, .sprite-icons-bar-light-neutral, .sprite-icons-bar-light-colored, .sprite-icons-bar-dark-neutral, @@ -830,54 +834,78 @@ height: 52px; } -.sprite-icons-bar-light-neutral { +.sprite-icons-boxplot-light-neutral { background-position: -424px -604px; width: 52px; height: 52px; } -.sprite-icons-bar-light-colored { +.sprite-icons-boxplot-light-colored { background-position: -484px -604px; width: 52px; height: 52px; } -.sprite-icons-bar-dark-neutral { +.sprite-icons-boxplot-dark-neutral { background-position: -544px -604px; width: 52px; height: 52px; } -.sprite-icons-bar-dark-colored { +.sprite-icons-boxplot-dark-colored { background-position: -604px -604px; width: 52px; height: 52px; } -.sprite-icons-area-bump-light-neutral { +.sprite-icons-bar-light-neutral { background-position: -664px -4px; width: 52px; height: 52px; } -.sprite-icons-area-bump-light-colored { +.sprite-icons-bar-light-colored { background-position: -664px -64px; width: 52px; height: 52px; } -.sprite-icons-area-bump-dark-neutral { +.sprite-icons-bar-dark-neutral { background-position: -664px -124px; width: 52px; height: 52px; } -.sprite-icons-area-bump-dark-colored { +.sprite-icons-bar-dark-colored { background-position: -664px -184px; width: 52px; height: 52px; } +.sprite-icons-area-bump-light-neutral { + background-position: -664px -244px; + width: 52px; + height: 52px; +} + +.sprite-icons-area-bump-light-colored { + background-position: -664px -304px; + width: 52px; + height: 52px; +} + +.sprite-icons-area-bump-dark-neutral { + background-position: -664px -364px; + width: 52px; + height: 52px; +} + +.sprite-icons-area-bump-dark-colored { + background-position: -664px -424px; + width: 52px; + height: 52px; +} + @media screen and (-webkit-min-device-pixel-ratio: 1), screen and (min--moz-device-pixel-ratio: 1), screen and (-o-min-device-pixel-ratio: 100/100), @@ -1000,6 +1028,10 @@ .sprite-icons-bullet-light-colored, .sprite-icons-bullet-dark-neutral, .sprite-icons-bullet-dark-colored, + .sprite-icons-boxplot-light-neutral, + .sprite-icons-boxplot-light-colored, + .sprite-icons-boxplot-dark-neutral, + .sprite-icons-boxplot-dark-colored, .sprite-icons-bar-light-neutral, .sprite-icons-bar-light-colored, .sprite-icons-bar-dark-neutral, @@ -1137,6 +1169,10 @@ .sprite-icons-bullet-light-colored, .sprite-icons-bullet-dark-neutral, .sprite-icons-bullet-dark-colored, + .sprite-icons-boxplot-light-neutral, + .sprite-icons-boxplot-light-colored, + .sprite-icons-boxplot-dark-neutral, + .sprite-icons-boxplot-dark-colored, .sprite-icons-bar-light-neutral, .sprite-icons-bar-light-colored, .sprite-icons-bar-dark-neutral,