diff --git a/.gitignore b/.gitignore index 396a4f3aa..08356ef5f 100644 --- a/.gitignore +++ b/.gitignore @@ -17,4 +17,6 @@ test-report.xml # exclude vscode config except for our shared snippets .vscode/* -!.vscode/reactSpectrumCharts.code-snippets \ No newline at end of file +!.vscode/reactSpectrumCharts.code-snippets + +.idea/ \ No newline at end of file diff --git a/.storybook/main.ts b/.storybook/main.ts index 909f545d0..dcef8e234 100644 --- a/.storybook/main.ts +++ b/.storybook/main.ts @@ -4,7 +4,12 @@ import TsconfigPathsPlugin from 'tsconfig-paths-webpack-plugin'; const config: StorybookConfig = { stories: ['../src/**/*.story.mdx', '../src/**/*.story.@(js|jsx|ts|tsx)'], - addons: ['@storybook/addon-links', '@storybook/addon-essentials', 'storybook-dark-mode'], + addons: [ + '@storybook/addon-links', + '@storybook/addon-essentials', + 'storybook-dark-mode', + '@storybook/addon-webpack5-compiler-babel' + ], framework: { name: '@storybook/react-webpack5', diff --git a/global.mock.js b/global.mock.js new file mode 100644 index 000000000..e515ae065 --- /dev/null +++ b/global.mock.js @@ -0,0 +1 @@ +global.structuredClone = (value) => JSON.parse(JSON.stringify(value)); \ No newline at end of file diff --git a/jest.config.js b/jest.config.js index 8772da321..3102a742d 100644 --- a/jest.config.js +++ b/jest.config.js @@ -21,9 +21,7 @@ module.exports = { transform: { '^.+\\.(j|t)sx?$': 'babel-jest', }, - globals: { - structuredClone: (value) => JSON.parse(JSON.stringify(value)), - }, + setupFiles: ['./global.mock.js'], moduleDirectories: ['src', 'node_modules'], moduleNameMapper: { '\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$': diff --git a/package.json b/package.json index 4f9743c9d..849cbf90c 100644 --- a/package.json +++ b/package.json @@ -56,14 +56,15 @@ "@babel/preset-react": "^7.18.6", "@babel/preset-typescript": "^7.18.6", "@babel/runtime": "^7.20.6", - "@storybook/addon-actions": "7.6.17", - "@storybook/addon-essentials": "7.6.17", - "@storybook/addon-links": "7.6.17", + "@storybook/addon-actions": "^8.0.4", + "@storybook/addon-essentials": "^8.0.4", + "@storybook/addon-links": "^8.0.4", + "@storybook/addon-webpack5-compiler-babel": "^3.0.3", "@storybook/preset-scss": "^1.0.3", - "@storybook/react": "7.6.17", - "@storybook/react-webpack5": "7.6.17", - "@storybook/testing-library": "^0.2.2", - "@storybook/theming": "7.6.17", + "@storybook/react": "^8.0.4", + "@storybook/react-webpack5": "^8.0.4", + "@storybook/test": "^8.0.4", + "@storybook/theming": "^8.0.4", "@testing-library/jest-dom": "^5.16.5", "@testing-library/react": "12.1.5", "@testing-library/user-event": "^14.4.3", @@ -100,9 +101,9 @@ "react-docgen-typescript-loader": "3.7.2", "react-dom": "17.0.2", "resize-observer-polyfill": "^1.5.1", - "storybook": "7.6.17", + "storybook": "^8.0.4", "storybook-addon-react-docgen": "^1.2.44", - "storybook-dark-mode": "^3.0.3", + "storybook-dark-mode": "^4.0.1", "style-loader": "^3.3.3", "ts-jest": "^29.0.3", "ts-loader": "^9.4.2", diff --git a/src/Chart.tsx b/src/Chart.tsx index c85dc15c5..4fb11ca10 100644 --- a/src/Chart.tsx +++ b/src/Chart.tsx @@ -13,7 +13,12 @@ import { FC, forwardRef, useEffect, useMemo, useRef, useState } from 'react'; import { EmptyState } from '@components/EmptyState'; import { LoadingState } from '@components/LoadingState'; -import { DEFAULT_BACKGROUND_COLOR, DEFAULT_COLOR_SCHEME, DEFAULT_LINE_TYPES, DEFAULT_LOCALE } from '@constants'; +import { + DEFAULT_BACKGROUND_COLOR, + DEFAULT_COLOR_SCHEME, + DEFAULT_LINE_TYPES, + DEFAULT_LOCALE +} from '@constants'; import useChartImperativeHandle from '@hooks/useChartImperativeHandle'; import useChartWidth from '@hooks/useChartWidth'; import { useResizeObserver } from '@hooks/useResizeObserver'; @@ -27,6 +32,7 @@ import { Theme } from '@react-types/provider'; import './Chart.css'; import { ChartData, ChartHandle, ChartProps } from './types'; +import usePreviousChartData from '@hooks/usePreviousChartData'; interface PlaceholderContentProps { data: ChartData[]; @@ -40,6 +46,7 @@ export const Chart = forwardRef( { backgroundColor = DEFAULT_BACKGROUND_COLOR, data, + animations = true, colors = 'categorical12', colorScheme = DEFAULT_COLOR_SCHEME, config, @@ -77,6 +84,8 @@ export const Chart = forwardRef( useChartImperativeHandle(forwardedRef, { chartView, title }); + const previousChartData = usePreviousChartData(data); + const containerRef = useResizeObserver((_target, entry) => { if (typeof width === 'number') return; setContainerWidth(entry.contentRect.width); @@ -129,6 +138,8 @@ export const Chart = forwardRef( chartView={chartView} chartId={chartId} data={data} + previousData={previousChartData} + animations={animations} backgroundColor={backgroundColor} colors={colors} colorScheme={colorScheme} diff --git a/src/RscChart.tsx b/src/RscChart.tsx index 4138aad00..e090d95be 100644 --- a/src/RscChart.tsx +++ b/src/RscChart.tsx @@ -68,6 +68,8 @@ export const RscChart = forwardRef( chartView, backgroundColor = DEFAULT_BACKGROUND_COLOR, data, + previousData, + animations, colors = 'categorical12', colorScheme = DEFAULT_COLOR_SCHEME, config, @@ -93,7 +95,10 @@ export const RscChart = forwardRef( forwardedRef ) => { // uuid is used to make a unique id so there aren't duplicate ids if there is more than one Chart component in the document - const selectedData = useRef(null); // data that is currently selected, get's set on click if a popover exists + + // state variable for storing if chart should reanimate from zero / animate across data sets + const [animateFromZero, setAnimateFromZero] = useState(true); + const selectedData = useRef(null); // data that is currently selected, gets set on click if a popover exists const selectedDataName = useRef(); const selectedDataBounds = useRef(); const popoverAnchorRef = useRef(null); @@ -102,12 +107,20 @@ export const RscChart = forwardRef( const sanitizedChildren = sanitizeRscChartChildren(props.children); - // THE MAGIC, builds our spec + // when data changes, make sure that we are animating from zero (especially in the case where a popover was just + // opened and closed) + useEffect(() => { + setAnimateFromZero(true); + }, [data]); + const spec = useSpec({ backgroundColor, children: sanitizedChildren, colors, data, + previousData, + animations: animations ?? true, + animateFromZero, description, hiddenSeries, highlightedSeries, @@ -130,8 +143,11 @@ export const RscChart = forwardRef( // Hide tooltips on all charts when a popover is open tooltipElement.hidden = popoverIsOpen; - // if the popover is closed, reset the selected data - if (!popoverIsOpen) { + + if (popoverIsOpen) { + setAnimateFromZero(false); + } else { + // if the popover is closed, reset the selected data selectedData.current = null; } }, [popoverIsOpen]); @@ -173,7 +189,6 @@ export const RscChart = forwardRef( ); @@ -198,6 +213,7 @@ export const RscChart = forwardRef( const signals: Record = { backgroundColor: getColorValue('gray-50', colorScheme), }; + if (legendIsToggleable) { signals.hiddenSeries = hiddenSeriesState; } @@ -207,6 +223,56 @@ export const RscChart = forwardRef( return signals; }, [colorScheme, hiddenSeriesState, legendIsToggleable]); + const newSpec = structuredClone(spec); + + const onNewView = (view) => { + chartView.current = view; + // Add a delay before displaying legend tooltips on hover. + let tooltipTimeout: NodeJS.Timeout | undefined; + view.tooltip((_, event, item, value) => { + const tooltipHandler = new Handler(tooltipConfig); + // Cancel delayed tooltips if the mouse moves before the delay is resolved. + if (tooltipTimeout) { + clearTimeout(tooltipTimeout); + tooltipTimeout = undefined; + } + if (event && event.type === 'pointermove' && itemIsLegendItem(item) && 'tooltip' in item) { + tooltipTimeout = setTimeout(() => { + tooltipHandler.call(this, event, item, value); + tooltipTimeout = undefined; + }, LEGEND_TOOLTIP_DELAY); + } else { + tooltipHandler.call(this, event, item, value); + } + }); + if (popovers.length || legendIsToggleable || onLegendClick) { + if (legendIsToggleable) { + view.signal('hiddenSeries', hiddenSeriesState); + } + setSelectedSignals({ + selectedData: selectedData.current, + view, + }); + view.addEventListener( + 'click', + getOnMarkClickCallback( + chartView, + hiddenSeriesState, + chartId, + selectedData, + selectedDataBounds, + selectedDataName, + setHiddenSeries, + legendIsToggleable, + onLegendClick + ) + ); + } + view.addEventListener('mouseover', getOnMouseInputCallback(onLegendMouseOver)); + view.addEventListener('mouseout', getOnMouseInputCallback(onLegendMouseOut)); + // this will trigger the autosize calculation making sure that everything is correct size + }; + return ( <>
( style={targetStyle} /> ( padding={padding} signals={signals} tooltip={tooltipConfig} // legend show/hide relies on this - onNewView={(view) => { - chartView.current = view; - // Add a delay before displaying legend tooltips on hover. - let tooltipTimeout: NodeJS.Timeout | undefined; - view.tooltip((_, event, item, value) => { - const tooltipHandler = new Handler(tooltipConfig); - // Cancel delayed tooltips if the mouse moves before the delay is resolved. - if (tooltipTimeout) { - clearTimeout(tooltipTimeout); - tooltipTimeout = undefined; - } - if (event && event.type === 'pointermove' && itemIsLegendItem(item) && 'tooltip' in item) { - tooltipTimeout = setTimeout(() => { - tooltipHandler.call(this, event, item, value); - tooltipTimeout = undefined; - }, LEGEND_TOOLTIP_DELAY); - } else { - tooltipHandler.call(this, event, item, value); - } - }); - if (popovers.length || legendIsToggleable || onLegendClick) { - if (legendIsToggleable) { - view.signal('hiddenSeries', hiddenSeriesState); - } - setSelectedSignals({ - selectedData: selectedData.current, - view, - }); - view.addEventListener( - 'click', - getOnMarkClickCallback( - chartView, - hiddenSeriesState, - chartId, - selectedData, - selectedDataBounds, - selectedDataName, - setHiddenSeries, - legendIsToggleable, - onLegendClick - ) - ); - } - view.addEventListener('mouseover', getOnMouseInputCallback(onLegendMouseOver)); - view.addEventListener('mouseout', getOnMouseInputCallback(onLegendMouseOut)); - // this will trigger the autosize calculation making sure that everything is correct size - }} + onNewView={onNewView} /> = ({ value, descriptions, domain }) const itemIsLegendItem = (item: Item): boolean => { return 'name' in item.mark && typeof item.mark.name === 'string' && item.mark.name.includes('legend'); -}; +}; \ No newline at end of file diff --git a/src/VegaChart.tsx b/src/VegaChart.tsx index 0be144369..3e57d3542 100644 --- a/src/VegaChart.tsx +++ b/src/VegaChart.tsx @@ -11,7 +11,7 @@ */ import { FC, useEffect, useMemo, useRef } from 'react'; -import { TABLE } from '@constants'; +import { PREVIOUS_TABLE, TABLE } from '@constants'; import { useDebugSpec } from '@hooks/useDebugSpec'; import { extractValues, isVegaData } from '@specBuilder/specUtils'; import { expressionFunctions, formatTimeDurationLabels } from 'expressionFunctions'; @@ -25,6 +25,7 @@ export interface VegaChartProps { config: Config; // eslint-disable-next-line @typescript-eslint/no-explicit-any data: ChartData[]; + previousData: ChartData[]; debug: boolean; height: number; locale: ChartProps['locale']; @@ -40,6 +41,7 @@ export interface VegaChartProps { export const VegaChart: FC = ({ config, data, + previousData, debug, height, locale, @@ -58,7 +60,7 @@ export const VegaChart: FC = ({ // Need to de a deep copy of the data because vega tries to transform the data const chartData = useMemo(() => { - const clonedData = JSON.parse(JSON.stringify(data)); + const clonedData = structuredClone(data); // We received a full Vega data array with potentially multiple dataset objects if (isVegaData(clonedData)) { @@ -69,7 +71,19 @@ export const VegaChart: FC = ({ return { [TABLE]: clonedData }; }, [data]); - useDebugSpec(debug, spec, chartData, width, height, config); + const previousChartData = useMemo(() => { + const clonedData = structuredClone(previousData); + + // We received a full Vega data array with potentially multiple dataset objects + if (isVegaData(clonedData)) { + return extractValues(clonedData); + } + + // We received a simple array of data and we'll set a default key of 'table' to reference internally + return { [PREVIOUS_TABLE]: clonedData }; + }, [previousData]); + + useDebugSpec(debug, spec, { ...previousChartData, ...chartData }, width, height, config); useEffect(() => { if (width && height && containerRef.current) { @@ -78,6 +92,10 @@ export const VegaChart: FC = ({ if (tableData && 'values' in tableData) { tableData.values = chartData.table; } + const previousTableData = specCopy.data?.find((d) => d.name === PREVIOUS_TABLE); + if (previousTableData && 'values' in previousTableData) { + previousTableData.values = previousChartData ? previousChartData.previousTable : []; + } if (signals) { specCopy.signals = specCopy.signals?.map((signal) => { if (signal.name in signals && signals[signal.name] !== undefined && 'value' in signal) { @@ -86,6 +104,7 @@ export const VegaChart: FC = ({ return signal; }); } + embed(containerRef.current, specCopy, { actions: false, config, @@ -116,8 +135,10 @@ export const VegaChart: FC = ({ }; }, [ chartData.table, + previousChartData, config, data, + previousData, height, numberLocale, timeLocale, diff --git a/src/components/ChartPopover/ChartPopover.tsx b/src/components/ChartPopover/ChartPopover.tsx index a778c0a9a..d29126e53 100644 --- a/src/components/ChartPopover/ChartPopover.tsx +++ b/src/components/ChartPopover/ChartPopover.tsx @@ -14,7 +14,7 @@ import { FC } from 'react'; import { ChartPopoverProps } from '../../types'; // eslint-disable-next-line @typescript-eslint/no-unused-vars -const ChartPopover: FC = ({ children, width = 250 }) => { +const ChartPopover: FC = ({ children, width = 250, animations }) => { return null; }; diff --git a/src/constants.ts b/src/constants.ts index 9bbaec239..3307826e0 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -39,6 +39,10 @@ export const DEFAULT_TITLE_FONT_WEIGHT = 'bold'; // vega data table name export const TABLE = 'table'; export const FILTERED_TABLE = 'filteredTable'; +export const PREVIOUS_TABLE = 'previousTable'; + +export const FILTERED_PREVIOUS_TABLE = 'filteredPreviousTable'; +export const PREVIOUS_PREFIX = 'previous_'; // vega data field names export const SERIES_ID = 'rscSeriesId'; @@ -77,6 +81,8 @@ export const TRELLIS_PADDING = 0.2; // ratio that each opacity is divded by when hovering or highlighting from legend export const HIGHLIGHT_CONTRAST_RATIO = 5; +export const RSC_ANIMATION = `rscAnimation`; + // legend tooltips export const LEGEND_TOOLTIP_DELAY = 350; @@ -86,3 +92,15 @@ export const BACKGROUND_COLOR = 'chartBackgroundColor'; // time constants export const MS_PER_DAY = 86400000; + +// chart animation constants +export const DATA_ANIMATION_DURATION_FRAMES = 78; // 60 fps * 1.3 seconds = 78 frames +export const DATA_ANIMATION_MILLISECONDS_PER_FRAME = 1000 / 60; // 60fps + +export const OPACITY_ANIMATION_FRAMES = 1 / 30; // 0.033 - 30fps + +const EASE_OUT_CUBIC = '(1 - pow(1 - timerValue, 3))'; + +export const ANIMATION_FUNCTION = EASE_OUT_CUBIC; + +export const annotationFillOpacity = 'timerValue === 1 ? 1 : 0'; diff --git a/src/hooks/useChartProps.tsx b/src/hooks/useChartProps.tsx index 12e74f30c..45e7aa7c7 100644 --- a/src/hooks/useChartProps.tsx +++ b/src/hooks/useChartProps.tsx @@ -14,5 +14,5 @@ import { ChartProps } from 'types'; export default function useChartProps(props: ChartProps): ChartProps { const darkMode = useDarkMode(); - return { colorScheme: darkMode ? 'dark' : 'light', ...props }; + return { animations: false, colorScheme: darkMode ? 'dark' : 'light', ...props }; } diff --git a/src/hooks/usePreviousChartData.tsx b/src/hooks/usePreviousChartData.tsx new file mode 100644 index 000000000..2a36e7363 --- /dev/null +++ b/src/hooks/usePreviousChartData.tsx @@ -0,0 +1,11 @@ +import { useEffect, useRef } from 'react'; +import { ChartData } from '@rsc'; + +export default function usePreviousChartData(data: ChartData[]): ChartData[] | undefined { + const ref = useRef(); + useEffect(() => { + ref.current = data; + }, [data]); + + return ref.current; +} \ No newline at end of file diff --git a/src/hooks/useSpec.tsx b/src/hooks/useSpec.tsx index c60d500dc..dc711cfc1 100644 --- a/src/hooks/useSpec.tsx +++ b/src/hooks/useSpec.tsx @@ -23,6 +23,9 @@ export default function useSpec({ colors, colorScheme, data, + previousData, + animations, + animateFromZero, description, hiddenSeries, highlightedSeries, @@ -57,6 +60,10 @@ export default function useSpec({ backgroundColor, children, colors, + data, + previousData, + animations, + animateFromZero, colorScheme, description, hiddenSeries, @@ -76,6 +83,9 @@ export default function useSpec({ colors, colorScheme, data, + previousData, + animations, + animateFromZero, description, hiddenSeries, highlightedSeries, diff --git a/src/specBuilder/area/areaSpecBuilder.test.ts b/src/specBuilder/area/areaSpecBuilder.test.ts index 21d611c9b..1d2b2e978 100644 --- a/src/specBuilder/area/areaSpecBuilder.test.ts +++ b/src/specBuilder/area/areaSpecBuilder.test.ts @@ -20,6 +20,8 @@ import { DEFAULT_METRIC, DEFAULT_TIME_DIMENSION, DEFAULT_TRANSFORMED_TIME_DIMENSION, + PREVIOUS_TABLE, + FILTERED_PREVIOUS_TABLE, FILTERED_TABLE, HIGHLIGHTED_ITEM, HIGHLIGHTED_SERIES, @@ -27,6 +29,7 @@ import { SELECTED_ITEM, SELECTED_SERIES, TABLE, + RSC_ANIMATION } from '@constants'; import { defaultSignals } from '@specBuilder/specTestUtils'; import { AreaSpecProps } from 'types'; @@ -34,9 +37,10 @@ import { Data, GroupMark, Spec } from 'vega'; import { initializeSpec } from '../specUtils'; import { addArea, addAreaMarks, addData, addSignals, setScales } from './areaSpecBuilder'; +import { defaultAnimationScales } from '@specBuilder/scale/scaleSpecBuilder.test'; const startingSpec: Spec = initializeSpec({ - scales: [{ name: COLOR_SCALE, type: 'ordinal' }], + scales: [{ name: COLOR_SCALE, type: 'ordinal' }] }); const defaultAreaProps: AreaSpecProps = { @@ -49,6 +53,7 @@ const defaultAreaProps: AreaSpecProps = { name: 'area0', opacity: 0.8, scaleType: 'time', + animations: false }; const defaultSpec = initializeSpec({ @@ -79,6 +84,32 @@ const defaultSpec = initializeSpec({ }, ], }, + { + name: PREVIOUS_TABLE, + transform: [ + { as: MARK_ID, type: 'identifier' }, + { + as: [DEFAULT_TRANSFORMED_TIME_DIMENSION, `${DEFAULT_TIME_DIMENSION}1`], + field: DEFAULT_TIME_DIMENSION, + type: 'timeunit', + units: ['year', 'month', 'date', 'hours', 'minutes'], + }, + ], + values: [], + }, + { + name: FILTERED_PREVIOUS_TABLE, + source: PREVIOUS_TABLE, + transform: [ + { + as: ['value0', 'value1'], + field: DEFAULT_METRIC, + groupby: [DEFAULT_TIME_DIMENSION], + sort: undefined, + type: 'stack', + }, + ], + } ], marks: [ { @@ -157,11 +188,11 @@ const defaultPointScale = { describe('areaSpecBuilder', () => { describe('addArea()', () => { test('should add area', () => { - expect(addArea(startingSpec, {})).toStrictEqual(defaultSpec); + expect(addArea(startingSpec, { animations: false })).toStrictEqual(defaultSpec); }); test('metricStart defined but valueEnd not defined, should default to value', () => { - expect(addArea(startingSpec, { metricStart: 'test' })).toStrictEqual(defaultSpec); + expect(addArea(startingSpec, { metricStart: 'test', animations: false })).toStrictEqual(defaultSpec); }); }); @@ -205,6 +236,28 @@ describe('areaSpecBuilder', () => { expect(signals[3]).toHaveProperty('name', SELECTED_SERIES); expect(signals[4]).toHaveProperty('name', 'area0_controlledHoveredId'); }); + + test('children: should add signals with animations', () => { + const tooltip = createElement(ChartTooltip); + const signals = addSignals(defaultSignals, { ...defaultAreaProps, children: [tooltip], animations: true }); + expect(signals).toHaveLength(10); + expect(signals[0]).toHaveProperty('name', HIGHLIGHTED_ITEM); + expect(signals[1]).toHaveProperty('name', HIGHLIGHTED_SERIES); + expect(signals[1].on).toHaveLength(2); + expect(signals[2]).toHaveProperty('name', SELECTED_ITEM); + expect(signals[3]).toHaveProperty('name', SELECTED_SERIES); + expect(signals[4]).toHaveProperty('name', RSC_ANIMATION); + expect(signals[4].on).toHaveLength(1); + expect(signals[5]).toHaveProperty('name', 'rscColorAnimationDirection'); + expect(signals[5].on).toHaveLength(2); + expect(signals[6]).toHaveProperty('name', 'rscColorAnimation'); + expect(signals[6].on).toHaveLength(1); + expect(signals[7]).toHaveProperty('name', `${HIGHLIGHTED_ITEM}_prev`); + expect(signals[7].on).toHaveLength(1); + expect(signals[8]).toHaveProperty('name', `${HIGHLIGHTED_SERIES}_prev`); + expect(signals[8].on).toHaveLength(1); + expect(signals[9]).toHaveProperty('name', 'area0_controlledHoveredId'); + }); }); describe('setScales()', () => { @@ -220,6 +273,15 @@ describe('areaSpecBuilder', () => { ]); }); + test('linear with animations', () => { + expect(setScales(startingSpec.scales ?? [], { ...defaultAreaProps, scaleType: 'linear', children: [createElement(ChartTooltip)], animations: true })).toStrictEqual([ + defaultSpec.scales?.[0], + ...defaultAnimationScales, + defaultLinearScale, + defaultSpec.scales?.[2], + ]); + }); + test('point', () => { expect(setScales(startingSpec.scales ?? [], { ...defaultAreaProps, scaleType: 'point' })).toStrictEqual([ defaultSpec.scales?.[0], diff --git a/src/specBuilder/area/areaSpecBuilder.ts b/src/specBuilder/area/areaSpecBuilder.ts index f83daddab..78f422e09 100644 --- a/src/specBuilder/area/areaSpecBuilder.ts +++ b/src/specBuilder/area/areaSpecBuilder.ts @@ -22,22 +22,43 @@ import { SELECTED_ITEM, SELECTED_SERIES, } from '@constants'; +import { hasInteractiveChildren } from '@specBuilder/marks/markUtils'; import { addHighlightedSeriesSignalEvents, getControlledHoverSignal, + getRscAnimationSignals, hasSignalByName, } from '@specBuilder/signal/signalSpecBuilder'; import { spectrumColors } from '@themes'; import { sanitizeMarkChildren, toCamelCase } from '@utils'; import { produce } from 'immer'; -import { AreaProps, AreaSpecProps, ColorScheme, MarkChildElement, ScaleType } from 'types'; -import { Data, Mark, Scale, Signal, Spec } from 'vega'; +import { AreaProps, AreaSpecProps, ChartData, ColorScheme, MarkChildElement, ScaleType } from 'types'; +import { Data, Mark, Scale, Signal, Spec, Transforms } from 'vega'; -import { addTimeTransform, getFilteredTableData, getTableData, getTransformSort } from '../data/dataUtils'; -import { addContinuousDimensionScale, addFieldToFacetScaleDomain, addMetricScale } from '../scale/scaleSpecBuilder'; +import { + addTimeTransform, + getFilteredPreviousTableData, + getFilteredTableData, + getPreviousTableData, + getTableData, + getTransformSort, +} from '../data/dataUtils'; import { getAreaMark, getX } from './areaUtils'; +import { addContinuousDimensionScale, addFieldToFacetScaleDomain, addMetricScale, addRscAnimationScales } from '@specBuilder/scale/scaleSpecBuilder'; -export const addArea = produce( +export const addArea = produce< + Spec, + [ + AreaProps & { + colorScheme?: ColorScheme; + index?: number; + previousData?: ChartData[]; + data?: ChartData[]; + animations?: boolean; + animateFromZero?: boolean; + } + ] +>( ( spec, { @@ -95,13 +116,16 @@ export const addData = produce( (data, { name, dimension, scaleType, color, metric, metricEnd, metricStart, order, children }) => { if (scaleType === 'time') { const tableData = getTableData(data); + const previousTableData = getPreviousTableData(data); tableData.transform = addTimeTransform(tableData.transform ?? [], dimension); + previousTableData.transform = addTimeTransform(tableData.transform ?? [], dimension); } if (!metricEnd || !metricStart) { const filteredTableData = getFilteredTableData(data); - // if metricEnd and metricStart don't exist, then we are using metric so we will support stacked - filteredTableData.transform = [ + const filteredPreviousTableData = getFilteredPreviousTableData(data); + // if metricEnd and metricStart don't exist, then we are using metric, so we will support stacked + const transform: Transforms[] = [ ...(filteredTableData.transform ?? []), { type: 'stack', @@ -111,6 +135,8 @@ export const addData = produce( as: [`${metric}0`, `${metric}1`], }, ]; + filteredTableData.transform = transform; + filteredPreviousTableData.transform = transform; } if (children.length) { @@ -125,6 +151,7 @@ export const addData = produce( }, ], }); + if (children.some((child) => child.type === ChartPopover)) { data.push({ name: `${name}_selectedDataSeries`, @@ -141,8 +168,13 @@ export const addData = produce( } ); -export const addSignals = produce((signals, { children, name }) => { +export const addSignals = produce((signals, { children, name, animations }) => { if (!children.length) return; + + // If animations is enabled and has hover functionality, add all the necessary animation signals. + if (animations && hasInteractiveChildren(children)) { + signals.push(...getRscAnimationSignals(name)); + } if (!hasSignalByName(signals, `${name}_controlledHoveredId`)) { signals.push(getControlledHoverSignal(name)); } @@ -150,7 +182,11 @@ export const addSignals = produce((signals, { childre }); export const setScales = produce( - (scales, { metric, metricEnd, metricStart, dimension, color, scaleType, padding }) => { + (scales, { metric, metricEnd, metricStart, dimension, color, scaleType, padding, animations, children }) => { + // If animations is enabled and has hover functionality, add all the necessary animation scales. + if (animations && hasInteractiveChildren(children)) { + addRscAnimationScales(scales); + } // add dimension scale addContinuousDimensionScale(scales, { scaleType, dimension, padding }); // add color to the color domain @@ -166,7 +202,20 @@ export const setScales = produce( ); export const addAreaMarks = produce((marks, props) => { - const { name, color, colorScheme, metric, dimension, scaleType, opacity, children } = props; + const { + name, + color, + colorScheme, + metric, + dimension, + scaleType, + opacity, + children, + animations, + animateFromZero, + data, + previousData, + } = props; let { metricStart, metricEnd } = props; let isStacked = false; if (!metricEnd || !metricStart) { @@ -193,6 +242,10 @@ export const addAreaMarks = produce((marks, props) => { children, metricStart, metricEnd, + animations, + animateFromZero, + data, + previousData, isStacked, dimension, scaleType, @@ -311,7 +364,7 @@ const getSelectedAreaMarks = ({ strokeJoin: { value: 'round' }, }, update: { - // this has to be in update because when you resize the window that doesn't rebuild the spec + // this has to be in update because when you resize the window that doesn't rebuild the spec, // but it may change the x position if it causes the chart to resize x: getX(scaleType, dimension), }, diff --git a/src/specBuilder/area/areaUtils.test.ts b/src/specBuilder/area/areaUtils.test.ts index ceb5ee902..dc966770d 100644 --- a/src/specBuilder/area/areaUtils.test.ts +++ b/src/specBuilder/area/areaUtils.test.ts @@ -15,6 +15,7 @@ import { DEFAULT_COLOR, DEFAULT_COLOR_SCHEME, DEFAULT_TRANSFORMED_TIME_DIMENSION, + ANIMATION_FUNCTION, } from '@constants'; import { getAreaMark } from './areaUtils'; @@ -33,6 +34,7 @@ describe('getAreaMark', () => { dimension: 'dimension', scaleType: 'linear', opacity: 0.5, + animations: false, }) ).toStrictEqual({ name: 'area0', @@ -87,6 +89,7 @@ describe('getAreaMark', () => { dimension: 'dimension', scaleType: 'linear', opacity: 0.5, + animations: false, }) ).toStrictEqual({ name: 'area0', @@ -149,6 +152,7 @@ describe('getAreaMark', () => { dimension: 'dimension', scaleType: 'time', opacity: 0.5, + animations: false, }) ).toStrictEqual({ name: 'area0', @@ -203,6 +207,7 @@ describe('getAreaMark', () => { dimension: 'dimension', scaleType: 'point', opacity: 0.5, + animations: false, }) ).toStrictEqual({ name: 'area0', @@ -242,4 +247,59 @@ describe('getAreaMark', () => { }, }); }); + + test('basic props with animations', () => { + expect( + getAreaMark({ + name: 'area0', + color: DEFAULT_COLOR, + colorScheme: DEFAULT_COLOR_SCHEME, + children: [], + metricStart: 'metricStart', + metricEnd: 'metricEnd', + isStacked: false, + dimension: 'dimension', + scaleType: 'linear', + opacity: 0.5, + animations: true, + animateFromZero: true, + }) + ).toStrictEqual({ + name: 'area0', + type: 'area', + from: { + data: 'area0_facet', + }, + interactive: false, + encode: { + enter: { + fill: { + scale: COLOR_SCALE, + field: DEFAULT_COLOR, + }, + }, + update: { + y: { + scale: 'yLinear', + signal: `datum.${'metricStart'} * ${ANIMATION_FUNCTION}`, + }, + y2: { + scale: 'yLinear', + signal: `datum.${'metricEnd'} * ${ANIMATION_FUNCTION}`, + }, + cursor: undefined, + x: { + scale: 'xLinear', + field: 'dimension', + }, + fillOpacity: [ + { + value: 0.5, + }, + ], + tooltip: undefined, + }, + }, + }); + }); }); diff --git a/src/specBuilder/area/areaUtils.ts b/src/specBuilder/area/areaUtils.ts index b2cf3773d..6e4178c0c 100644 --- a/src/specBuilder/area/areaUtils.ts +++ b/src/specBuilder/area/areaUtils.ts @@ -22,9 +22,11 @@ import { getColorProductionRule, getCursor, getInteractive, + getSeriesAnimationOpacityRules, getTooltip, } from '@specBuilder/marks/markUtils'; -import { ColorFacet, ColorScheme, MarkChildElement, ScaleType } from 'types'; +import { getAnimationMarks } from '@specBuilder/specUtils'; +import { ChartData, ColorFacet, ColorScheme, MarkChildElement, ScaleType } from 'types'; import { AreaMark, NumericValueRef, ProductionRule } from 'vega'; export interface AreaMarkProps { @@ -32,6 +34,10 @@ export interface AreaMarkProps { color: ColorFacet; colorScheme: ColorScheme; children: MarkChildElement[]; + animations?: boolean; + animateFromZero?: boolean; + data?: ChartData[]; + previousData?: ChartData[]; metricStart: string; metricEnd: string; isStacked: boolean; @@ -51,6 +57,10 @@ export const getAreaMark = ( children, metricStart, metricEnd, + animations, + animateFromZero, + data, + previousData, isStacked, scaleType, dimension, @@ -67,18 +77,35 @@ export const getAreaMark = ( interactive: getInteractive(children), encode: { enter: { - y: { scale: 'yLinear', field: metricStart }, - y2: { scale: 'yLinear', field: metricEnd }, + ...((!animations || !animateFromZero) && { + y: { scale: 'yLinear', field: metricStart }, + y2: { scale: 'yLinear', field: metricEnd }, + tooltip: getTooltip({ children, name }), + }), fill: getColorProductionRule(color, colorScheme), - tooltip: getTooltip(children, name), ...getBorderStrokeEncodings(isStacked, true), }, update: { // this has to be in update because when you resize the window that doesn't rebuild the spec // but it may change the x position if it causes the chart to resize + ...(animations && + animateFromZero && { + y: getAnimationMarks(dimension, metricStart, data, previousData), + y2: getAnimationMarks(dimension, metricEnd, data, previousData), + tooltip: getTooltip({ children, name, animations }), + }), x: getX(scaleType, dimension), cursor: getCursor(children), - fillOpacity: getFillOpacity(name, color, opacity, children, isMetricRange, parentName, displayOnHover), + fillOpacity: getFillOpacity( + name, + color, + opacity, + children, + isMetricRange, + parentName, + displayOnHover, + animations + ), }, }, }); @@ -90,7 +117,8 @@ export function getFillOpacity( children: MarkChildElement[], isMetricRange?: boolean, parentName?: string, - displayOnHover?: boolean + displayOnHover?: boolean, + animations?: boolean ): ProductionRule | undefined { // if metric ranges only display when hovering, we don't need to include other hover rules for this specific area if (isMetricRange && displayOnHover) { @@ -106,6 +134,10 @@ export function getFillOpacity( if (!children.length) { return [{ value: opacity }]; } + // if animations are enabled, get opacity rules for charts that highlight according to series ID + if (animations) { + return getSeriesAnimationOpacityRules({ value: opacity }); + } // if an area is hovered or selected, all other areas should have half opacity if (children.some((child) => child.type === ChartPopover && !isMetricRange)) { diff --git a/src/specBuilder/bar/barSpecBuilder.test.ts b/src/specBuilder/bar/barSpecBuilder.test.ts index 301530b08..60c10cd69 100644 --- a/src/specBuilder/bar/barSpecBuilder.test.ts +++ b/src/specBuilder/bar/barSpecBuilder.test.ts @@ -21,13 +21,18 @@ import { DEFAULT_COLOR, DEFAULT_METRIC, DEFAULT_OPACITY_RULE, - DEFAULT_SECONDARY_COLOR, + DEFAULT_SECONDARY_COLOR, + FILTERED_PREVIOUS_TABLE, FILTERED_TABLE, LINE_TYPE_SCALE, MARK_ID, + PREVIOUS_TABLE, OPACITY_SCALE, STACK_ID, TABLE, + HIGHLIGHTED_ITEM, + HIGHLIGHTED_SERIES, + RSC_ANIMATION } from '@constants'; import { defaultSignals } from '@specBuilder/specTestUtils'; import { spectrumColors } from '@themes'; @@ -66,9 +71,10 @@ import { stackedAnnotationMarks, } from './barTestUtils'; import { defaultDodgedMark } from './dodgedBarUtils.test'; +import { defaultAnimationScales } from '@specBuilder/scale/scaleSpecBuilder.test'; const startingSpec: Spec = initializeSpec({ - scales: [{ name: COLOR_SCALE, type: 'ordinal' }], + scales: [{ name: COLOR_SCALE, type: 'ordinal' }] }); const defaultMetricScaleDomain: ScaleData = { data: FILTERED_TABLE, fields: ['value1'] }; @@ -103,9 +109,19 @@ const defaultTableData: ValuesData = { values: [], transform: [{ type: 'identifier', as: MARK_ID }], }; + +const defaultPreviousTableData: ValuesData = { + name: PREVIOUS_TABLE, + values: [], + transform: [{ type: 'identifier', as: MARK_ID }], +}; + const defaultFilteredTableData: SourceData = { name: FILTERED_TABLE, source: TABLE }; -const defaultData: Data[] = [defaultTableData, defaultFilteredTableData]; +const defaultFilteredPreviousTableData: SourceData = { name: FILTERED_PREVIOUS_TABLE, source: PREVIOUS_TABLE }; + + +const defaultData: Data[] = [defaultTableData, defaultFilteredTableData, defaultPreviousTableData, defaultFilteredPreviousTableData]; const defaultStacksTransforms: Transforms[] = [ { @@ -199,6 +215,16 @@ const defaultSpec: Spec = { source: TABLE, transform: defaultStackedTransforms, }, + { + name: PREVIOUS_TABLE, + transform: [{ type: 'identifier', as: MARK_ID }], + values: [], + }, + { + name: FILTERED_PREVIOUS_TABLE, + source: PREVIOUS_TABLE, + transform: defaultStackedTransforms, + }, defaultStacksData, ], signals: [defaultPaddingSignal], @@ -225,7 +251,7 @@ const defaultSpec: Spec = { describe('barSpecBuilder', () => { describe('addBar()', () => { test('no props', () => { - expect(addBar(startingSpec, {})).toStrictEqual(defaultSpec); + expect(addBar(startingSpec, { animations: false })).toStrictEqual(defaultSpec); }); }); @@ -241,6 +267,25 @@ describe('barSpecBuilder', () => { expect(signals[0].on).toHaveLength(2); expect(signals[0].on?.[0]).toHaveProperty('events', '@bar0:mouseover'); }); + test('should add hover events if tooltip is present with animations', () => { + const signals = addSignals(defaultSignals, { ...defaultBarProps, animations: true, children: [createElement(ChartTooltip)] }); + expect(signals).toHaveLength(10); + expect(signals[0]).toHaveProperty('name', HIGHLIGHTED_ITEM); + expect(signals[0]).toHaveProperty('on'); + expect(signals[0].on).toHaveLength(2); + expect(signals[0].on?.[0]).toHaveProperty('events', '@bar0:mouseover'); + expect(signals[1]).toHaveProperty('name', HIGHLIGHTED_SERIES); + expect(signals[5]).toHaveProperty('name', RSC_ANIMATION); + expect(signals[5].on).toHaveLength(1); + expect(signals[6]).toHaveProperty('name', 'rscColorAnimationDirection'); + expect(signals[6].on).toHaveLength(2); + expect(signals[7]).toHaveProperty('name', 'rscColorAnimation'); + expect(signals[7].on).toHaveLength(1); + expect(signals[8]).toHaveProperty('name', `${HIGHLIGHTED_ITEM}_prev`); + expect(signals[8].on).toHaveLength(1); + expect(signals[9]).toHaveProperty('name', `${HIGHLIGHTED_SERIES}_prev`); + expect(signals[9].on).toHaveLength(1); + }); }); describe('addScales()', () => { @@ -318,6 +363,31 @@ describe('barSpecBuilder', () => { }, ]); }); + + test('should add trellis scales with animations', () => { + expect( + addScales([{ name: COLOR_SCALE, type: 'ordinal' }], { + ...defaultBarProps, + trellis: 'event', + trellisOrientation: 'vertical', + trellisPadding: 0.5, + animations: true, + children: [createElement(ChartPopover)] + }) + ).toStrictEqual([ + defaultColorScale, + ...defaultAnimationScales, + defaultMetricScale, + defaultDimensionScale, + { + name: 'yTrellisBand', + type: 'band', + domain: { data: TABLE, fields: ['event'] }, + range: 'height', + paddingInner: 0.5, + }, + ]); + }); }); }); @@ -444,6 +514,19 @@ describe('barSpecBuilder', () => { { ...defaultStackedTransforms[1], expr: 'datum.browser' }, ], }, + defaultPreviousTableData, + { + ...defaultFilteredPreviousTableData, + transform: [ + { + ...defaultStackedTransforms[0], + groupby: ['browser'], + field: 'views', + as: ['views0', 'views1'], + }, + { ...defaultStackedTransforms[1], expr: 'datum.browser' }, + ], + }, { ...defaultStacksData, transform: [ @@ -461,6 +544,11 @@ describe('barSpecBuilder', () => { ...defaultFilteredTableData, transform: defaultStackedTransforms, }, + defaultPreviousTableData, + { + ...defaultFilteredPreviousTableData, + transform: defaultStackedTransforms, + }, defaultStacksData, ]); }); @@ -480,6 +568,19 @@ describe('barSpecBuilder', () => { defaultStackedTransforms[1], ], }, + defaultPreviousTableData, + { + ...defaultFilteredPreviousTableData, + transform: [ + { + ...defaultStackedTransforms[0], + sort: { + field: 'order', + }, + }, + defaultStackedTransforms[1], + ], + }, defaultStacksData, ]); }); @@ -488,12 +589,17 @@ describe('barSpecBuilder', () => { describe('transform already exists', () => { test('no props, new transform should be pushed onto the end with default values', () => { expect( - addData([{ ...defaultFilteredTableData, transform: defaultStackedTransforms }], defaultBarProps) + addData([{ ...defaultFilteredTableData, transform: defaultStackedTransforms }, + {...defaultFilteredPreviousTableData, transform: defaultStackedTransforms}], defaultBarProps) ).toStrictEqual([ { ...defaultFilteredTableData, transform: [...defaultStackedTransforms, ...defaultStackedTransforms], }, + { + ...defaultFilteredPreviousTableData, + transform: [...defaultStackedTransforms, ...defaultStackedTransforms], + }, defaultStacksData, ]); }); @@ -536,6 +642,25 @@ describe('barSpecBuilder', () => { { as: 'bar0_dodgeGroup', expr: `datum.${DEFAULT_COLOR}`, type: 'formula' }, ], }, + defaultPreviousTableData, + { + ...defaultFilteredPreviousTableData, + transform: [ + { + as: [`${DEFAULT_METRIC}0`, `${DEFAULT_METRIC}1`], + field: DEFAULT_METRIC, + groupby: [DEFAULT_CATEGORICAL_DIMENSION, DEFAULT_COLOR], + sort: undefined, + type: 'stack', + }, + { + as: STACK_ID, + expr: `datum.${DEFAULT_CATEGORICAL_DIMENSION} + "," + datum.${DEFAULT_COLOR}`, + type: 'formula', + }, + { as: 'bar0_dodgeGroup', expr: `datum.${DEFAULT_COLOR}`, type: 'formula' }, + ], + }, { name: 'bar0_stacks', source: FILTERED_TABLE, @@ -578,6 +703,25 @@ describe('barSpecBuilder', () => { { as: 'bar0_dodgeGroup', expr: `datum.${DEFAULT_SECONDARY_COLOR}`, type: 'formula' }, ], }, + defaultPreviousTableData, + { + ...defaultFilteredPreviousTableData, + transform: [ + { + as: [`${DEFAULT_METRIC}0`, `${DEFAULT_METRIC}1`], + field: DEFAULT_METRIC, + groupby: [DEFAULT_CATEGORICAL_DIMENSION, DEFAULT_SECONDARY_COLOR], + sort: undefined, + type: 'stack', + }, + { + as: STACK_ID, + expr: `datum.${DEFAULT_CATEGORICAL_DIMENSION} + "," + datum.${DEFAULT_SECONDARY_COLOR}`, + type: 'formula', + }, + { as: 'bar0_dodgeGroup', expr: `datum.${DEFAULT_SECONDARY_COLOR}`, type: 'formula' }, + ], + }, { name: 'bar0_stacks', source: FILTERED_TABLE, diff --git a/src/specBuilder/bar/barSpecBuilder.ts b/src/specBuilder/bar/barSpecBuilder.ts index 4cde41905..12dd1c024 100644 --- a/src/specBuilder/bar/barSpecBuilder.ts +++ b/src/specBuilder/bar/barSpecBuilder.ts @@ -14,6 +14,7 @@ import { DEFAULT_CATEGORICAL_DIMENSION, DEFAULT_COLOR_SCHEME, DEFAULT_METRIC, + FILTERED_PREVIOUS_TABLE, FILTERED_TABLE, LINE_TYPE_SCALE, OPACITY_SCALE, @@ -22,28 +23,46 @@ import { TRELLIS_PADDING, } from '@constants'; import { getTransformSort } from '@specBuilder/data/dataUtils'; +import { hasInteractiveChildren } from '@specBuilder/marks/markUtils'; import { addDomainFields, addFieldToFacetScaleDomain, addMetricScale, + addRscAnimationScales, getDefaultScale, getMetricScale, getScaleIndexByName, getScaleIndexByType, } from '@specBuilder/scale/scaleSpecBuilder'; -import { addHighlightedItemSignalEvents, getGenericSignal } from '@specBuilder/signal/signalSpecBuilder'; +import { + addHighlightedItemSignalEvents, + getGenericSignal, + getRscAnimationSignals, +} from '@specBuilder/signal/signalSpecBuilder'; import { getFacetsFromProps } from '@specBuilder/specUtils'; import { sanitizeMarkChildren, toCamelCase } from '@utils'; import { produce } from 'immer'; -import { BarProps, BarSpecProps, ColorScheme } from 'types'; -import { BandScale, Data, FormulaTransform, Mark, OrdinalScale, Scale, Signal, Spec } from 'vega'; +import { BarProps, BarSpecProps, ChartData, ColorScheme } from 'types'; +import { BandScale, Data, FormulaTransform, Mark, OrdinalScale, Scale, Signal, Spec, Transforms } from 'vega'; import { getBarPadding, getScaleValues, isDodgedAndStacked } from './barUtils'; import { getDodgedMark } from './dodgedBarUtils'; import { getDodgedAndStackedBarMark, getStackedBarMarks } from './stackedBarUtils'; import { addTrellisScale, getTrellisGroupMark, isTrellised } from './trellisedBarUtils'; -export const addBar = produce( +export const addBar = produce< + Spec, + [ + BarProps & { + colorScheme?: ColorScheme; + index?: number; + data?: ChartData[]; + previousData?: ChartData[]; + animations?: boolean; + animateFromZero?: boolean; + } + ] +>( ( spec, { @@ -84,7 +103,6 @@ export const addBar = produce( - (signals, { children, name, paddingRatio, paddingOuter: barPaddingOuter }) => { + (signals, { children, name, animations, animateFromZero, paddingRatio, paddingOuter: barPaddingOuter }) => { // We use this value to calculate ReferenceLine positions. const { paddingInner } = getBarPadding(paddingRatio, barPaddingOuter); signals.push(getGenericSignal('paddingInner', paddingInner)); @@ -101,28 +119,47 @@ export const addSignals = produce( if (!children.length) { return; } - addHighlightedItemSignalEvents(signals, name); + + // if animations are enabled, push all necessary animation signals. + if (animations && hasInteractiveChildren(children)) { + signals.push(...getRscAnimationSignals(name, undefined, true)); + } + addHighlightedItemSignalEvents({ signals, markName: name, animations, animateFromZero, isEnabled: false}); } ); export const addData = produce((data, props) => { const { metric, order, type } = props; - const index = data.findIndex((d) => d.name === FILTERED_TABLE); - data[index].transform = data[index].transform ?? []; + + const filteredIndex = data.findIndex((d) => d.name === FILTERED_TABLE); + const filteredPreviousIndex = data.findIndex((d) => d.name === FILTERED_PREVIOUS_TABLE); + + data[filteredIndex].transform = data[filteredIndex].transform ?? []; + data[filteredPreviousIndex].transform = data[filteredPreviousIndex].transform ?? []; if (type === 'stacked' || isDodgedAndStacked(props)) { - data[index].transform?.push({ + const stackedDataGroup: Transforms = { type: 'stack', groupby: getStackFields(props), field: metric, sort: getTransformSort(order), as: [`${metric}0`, `${metric}1`], - }); + }; + + data[filteredIndex].transform?.push(stackedDataGroup); + data[filteredPreviousIndex].transform?.push(stackedDataGroup); + + const stackIdTransform = getStackIdTransform(props); + + data[filteredIndex].transform?.push(stackIdTransform); + data[filteredPreviousIndex].transform?.push(stackIdTransform); - data[index].transform?.push(getStackIdTransform(props)); data.push(getStackAggregateData(props)); } if (type === 'dodged' || isDodgedAndStacked(props)) { - data[index].transform?.push(getDodgeGroupTransform(props)); + const dodgeGroupTransform = getDodgeGroupTransform(props); + + data[filteredIndex].transform?.push(dodgeGroupTransform); + data[filteredPreviousIndex].transform?.push(dodgeGroupTransform); } }); @@ -150,6 +187,23 @@ export const getStackAggregateData = (props: BarSpecProps): Data => { }; }; +export const getPreviousStackAggregateData = (props: BarSpecProps): Data => { + const { metric, name } = props; + return { + name: `${name}_stacks`, + source: FILTERED_PREVIOUS_TABLE, + transform: [ + { + type: 'aggregate', + groupby: getStackFields(props), + fields: [`${metric}1`, `${metric}1`], + ops: ['min', 'max'], + }, + getStackIdTransform(props), + ], + }; +}; + export const getStackIdTransform = (props: BarSpecProps): FormulaTransform => { return { type: 'formula', @@ -180,7 +234,11 @@ export const getDodgeGroupTransform = ({ color, lineType, name, opacity, type }: }; export const addScales = produce((scales, props) => { - const { color, lineType, opacity, orientation } = props; + const { color, lineType, opacity, orientation, animations, children } = props; + // if animations are enabled and the chart has interactive children, get all animation scales. + if (animations && hasInteractiveChildren(children)) { + addRscAnimationScales(scales); + } addMetricScale(scales, getScaleValues(props), orientation === 'vertical' ? 'y' : 'x'); addDimensionScale(scales, props); addTrellisScale(scales, props); diff --git a/src/specBuilder/bar/barTestUtils.ts b/src/specBuilder/bar/barTestUtils.ts index 27ff82dce..4fa5aec36 100644 --- a/src/specBuilder/bar/barTestUtils.ts +++ b/src/specBuilder/bar/barTestUtils.ts @@ -32,8 +32,10 @@ import { BarSpecProps } from 'types'; import { NumericValueRef, ProductionRule, RectEncodeEntry } from 'vega'; export const defaultBarProps: BarSpecProps = { + animations: false, children: [], color: DEFAULT_COLOR, + colorScheme: DEFAULT_COLOR_SCHEME, dimension: DEFAULT_CATEGORICAL_DIMENSION, index: 0, lineType: { value: 'solid' }, @@ -41,12 +43,11 @@ export const defaultBarProps: BarSpecProps = { metric: DEFAULT_METRIC, name: 'bar0', opacity: { value: 1 }, + orientation: 'vertical', paddingRatio: PADDING_RATIO, - colorScheme: DEFAULT_COLOR_SCHEME, trellisOrientation: 'horizontal', trellisPadding: TRELLIS_PADDING, type: 'stacked', - orientation: 'vertical', }; export const defaultBarPropsWithSecondayColor: BarSpecProps = { @@ -204,7 +205,7 @@ export const stackedLabelText = { baseline: { value: 'middle' }, align: { value: 'center' }, }, - }, + } }; export const dodgedLabelWithStyles = { diff --git a/src/specBuilder/bar/barUtils.test.ts b/src/specBuilder/bar/barUtils.test.ts index 42e2e493b..5f9c93edd 100644 --- a/src/specBuilder/bar/barUtils.test.ts +++ b/src/specBuilder/bar/barUtils.test.ts @@ -15,6 +15,7 @@ import { Annotation } from '@components/Annotation'; import { ChartPopover } from '@components/ChartPopover'; import { ChartTooltip } from '@components/ChartTooltip'; import { + ANIMATION_FUNCTION, COLOR_SCALE, CORNER_RADIUS, DEFAULT_CATEGORICAL_DIMENSION, @@ -586,6 +587,16 @@ describe('barUtils', () => { test('should return x and width', () => { expect(getDodgedDimensionEncodings(defaultBarProps)).toStrictEqual(defaultDodgedXEncodings); }); + test('should return x and width with animations', () => { + expect(getDodgedDimensionEncodings({...defaultBarProps, animations: true, animateFromZero: true})).toStrictEqual({ + ...defaultDodgedXEncodings, + y: { + scale: 'yLinear', + signal: `datum.value * ${ANIMATION_FUNCTION}`, + }, + y2: { scale: 'yLinear', signal: "0" }, + }); + }); }); describe('getBarPadding()', () => { diff --git a/src/specBuilder/bar/barUtils.ts b/src/specBuilder/bar/barUtils.ts index 2f5a67cce..775b31a01 100644 --- a/src/specBuilder/bar/barUtils.ts +++ b/src/specBuilder/bar/barUtils.ts @@ -22,18 +22,20 @@ import { MARK_ID, SELECTED_ITEM, STACK_ID, + annotationFillOpacity, } from '@constants'; import { getColorProductionRule, getCursor, getHighlightOpacityValue, + getMarkHighlightOpacityRules, getOpacityProductionRule, getStrokeDashProductionRule, getTooltip, hasInteractiveChildren, hasPopover, } from '@specBuilder/marks/markUtils'; -import { getColorValue, getLineWidthPixelsFromLineWidth } from '@specBuilder/specUtils'; +import { getAnimationMarks, getColorValue, getLineWidthPixelsFromLineWidth } from '@specBuilder/specUtils'; import { sanitizeMarkChildren } from '@utils'; import { AnnotationElement, AnnotationStyleProps, BarSpecProps, Orientation } from 'types'; import { @@ -98,12 +100,35 @@ export const getDodgedGroupMark = (props: BarSpecProps): GroupMark => { }; export const getDodgedDimensionEncodings = (props: BarSpecProps): RectEncodeEntry => { - const { dimensionAxis, rangeScale } = getOrientationProperties(props.orientation); + const { animations, animateFromZero, dimension, metric, previousData, data } = props; + + const { + dimensionAxis, + metricAxis: startKey, + rangeScale, + metricScaleKey: scaleKey, + } = getOrientationProperties(props.orientation); const scale = `${props.name}_position`; const field = `${props.name}_dodgeGroup`; + const isStacked = isDodgedAndStacked(props); + + const startMetric = isStacked ? `${metric}0` : metric; + const endMetric = `${metric}1`; + + const endAnimations = isStacked + ? getAnimationMarks(dimension, endMetric, data, previousData, scaleKey) + : { scale: scaleKey, signal: '0' }; + + const endKey = `${startKey}2`; + return { + ...(animations && + animateFromZero && { + [startKey]: getAnimationMarks(dimension, startMetric, data, previousData, scaleKey), + [endKey]: endAnimations, + }), [dimensionAxis]: { scale, field }, [rangeScale]: { scale, band: 1 }, }; @@ -296,7 +321,7 @@ export const getAnnotationMarks = ( // bar only supports one annotation const annotation = children.find((el) => el.type === Annotation) as AnnotationElement; if (annotation?.props.textKey) { - const { orientation, name } = barProps; + const { orientation, name, animations } = barProps; const { textKey, style } = annotation.props; const { metricAxis, dimensionAxis } = getOrientationProperties(orientation); const annotationWidth = getAnnotationWidth(textKey, style); @@ -323,6 +348,13 @@ export const getAnnotationMarks = ( ], width: annotationWidth, }, + ...(animations && { + update: { + fillOpacity: { + signal: annotationFillOpacity, + }, + }, + }), }, }); marks.push({ @@ -344,6 +376,13 @@ export const getAnnotationMarks = ( baseline: { value: 'middle' }, align: { value: 'center' }, }, + ...(animations && { + update: { + fillOpacity: { + signal: annotationFillOpacity, + }, + }, + }), }, }); } @@ -355,10 +394,17 @@ export const getBaseBarEnterEncodings = (props: BarSpecProps): EncodeEntry => ({ ...getCornerRadiusEncodings(props), }); -export const getBarEnterEncodings = ({ children, color, colorScheme, name, opacity }: BarSpecProps): EncodeEntry => ({ +export const getBarEnterEncodings = ({ + children, + color, + colorScheme, + name, + opacity, + animations, +}: BarSpecProps): EncodeEntry => ({ fill: getColorProductionRule(color, colorScheme), fillOpacity: getOpacityProductionRule(opacity), - tooltip: getTooltip(children, name), + tooltip: getTooltip({ children, name, animations, isBar: true }), }); export const getBarUpdateEncodings = (props: BarSpecProps): EncodeEntry => ({ @@ -369,11 +415,15 @@ export const getBarUpdateEncodings = (props: BarSpecProps): EncodeEntry => ({ strokeWidth: getStrokeWidth(props), }); -export const getBarOpacity = ({ children }: BarSpecProps): ProductionRule => { +export const getBarOpacity = ({ children, animations }: BarSpecProps): ProductionRule => { // if there aren't any interactive components, then we don't need to add special opacity rules if (!hasInteractiveChildren(children)) { return [DEFAULT_OPACITY_RULE]; } + // if animations are enabled, get opacity rules for charts that use the mark ID as the highlighted item. + if (animations) { + return getMarkHighlightOpacityRules(); + } // if a bar is hovered/selected, all other bars should have reduced opacity if (hasPopover(children)) { @@ -393,7 +443,7 @@ export const getBarOpacity = ({ children }: BarSpecProps): ProductionRule { test('with annotation', () => { const annotationElement = createElement(Annotation, { textKey: 'textLabel' }); expect( - getDodgedMark({ + getDodgedMark({ animations: false, ...defaultDodgedProps, children: [...defaultDodgedProps.children, annotationElement], }) diff --git a/src/specBuilder/bar/stackedBarUtils.test.ts b/src/specBuilder/bar/stackedBarUtils.test.ts index 004203d62..9354ee20d 100644 --- a/src/specBuilder/bar/stackedBarUtils.test.ts +++ b/src/specBuilder/bar/stackedBarUtils.test.ts @@ -19,6 +19,7 @@ import { DEFAULT_COLOR, DEFAULT_OPACITY_RULE, DEFAULT_SECONDARY_COLOR, + ANIMATION_FUNCTION, FILTERED_TABLE, } from '@constants'; import { GroupMark, Mark, RectEncodeEntry } from 'vega'; @@ -31,7 +32,7 @@ import { } from './barTestUtils'; import { getDodgedAndStackedBarMark, getStackedBarMarks, getStackedDimensionEncodings } from './stackedBarUtils'; -const defaultStackedBarXEncondings: RectEncodeEntry = { +const defaultStackedBarXEncodings: RectEncodeEntry = { x: { scale: 'xBand', field: DEFAULT_CATEGORICAL_DIMENSION }, width: { scale: 'xBand', band: 1 }, }; @@ -42,7 +43,7 @@ const defaultBackgroundMark: Mark = { ...defaultBarEnterEncodings, fill: { signal: BACKGROUND_COLOR }, }, - update: defaultStackedBarXEncondings, + update: defaultStackedBarXEncodings, }, from: { data: FILTERED_TABLE }, interactive: false, @@ -61,7 +62,7 @@ const defaultMark = { update: { cursor: undefined, opacity: [DEFAULT_OPACITY_RULE], - ...defaultStackedBarXEncondings, + ...defaultStackedBarXEncodings, ...defaultBarStrokeEncodings, }, }, @@ -157,7 +158,7 @@ describe('stackedBarUtils', () => { describe('getStackedDimensionEncodings()', () => { test('should return x and width encodings', () => { - expect(getStackedDimensionEncodings(defaultBarProps)).toStrictEqual(defaultStackedBarXEncondings); + expect(getStackedDimensionEncodings(defaultBarProps)).toStrictEqual(defaultStackedBarXEncodings); }); test('should get dodged x encoding if stacked/dodged', () => { @@ -171,5 +172,41 @@ describe('stackedBarUtils', () => { x: { field: 'bar0_dodgeGroup', scale: 'bar0_position' }, }); }); + + test('should return x and width encodings with animations', () => { + expect(getStackedDimensionEncodings({...defaultBarProps, animations: true, animateFromZero: true,})).toStrictEqual({ + ...defaultStackedBarXEncodings, + y: { + scale: 'yLinear', + signal: `datum.value0 * ${ANIMATION_FUNCTION}`, + }, + y2: { + scale: 'yLinear', + signal: `datum.value1 * ${ANIMATION_FUNCTION}`, + }, + }); + }); + + test('should get dodged x encoding if stacked/dodged with animations', () => { + expect( + getStackedDimensionEncodings({ + ...defaultBarProps, + color: [DEFAULT_COLOR, DEFAULT_SECONDARY_COLOR], + animations: true, + animateFromZero: true, + }) + ).toStrictEqual({ + width: { band: 1, scale: 'bar0_position' }, + x: { field: 'bar0_dodgeGroup', scale: 'bar0_position' }, + y: { + scale: 'yLinear', + signal: `datum.value0 * ${ANIMATION_FUNCTION}`, + }, + y2: { + scale: 'yLinear', + signal: `datum.value1 * ${ANIMATION_FUNCTION}`, + }, + }); + }); }); }); diff --git a/src/specBuilder/bar/stackedBarUtils.ts b/src/specBuilder/bar/stackedBarUtils.ts index 3dfaa739e..6ff811c0b 100644 --- a/src/specBuilder/bar/stackedBarUtils.ts +++ b/src/specBuilder/bar/stackedBarUtils.ts @@ -11,6 +11,7 @@ */ import { BACKGROUND_COLOR, FILTERED_TABLE } from '@constants'; import { getInteractive } from '@specBuilder/marks/markUtils'; +import { getAnimationMarks } from '@specBuilder/specUtils'; import { BarSpecProps } from 'types'; import { Mark, RectEncodeEntry, RectMark } from 'vega'; @@ -105,14 +106,27 @@ export const getStackedBar = (props: BarSpecProps): RectMark => { }; export const getStackedDimensionEncodings = (props: BarSpecProps): RectEncodeEntry => { - const { dimension, orientation } = props; + const { dimension, orientation, previousData, data, animations, animateFromZero, metric } = props; if (isDodgedAndStacked(props)) { return getDodgedDimensionEncodings(props); } - const { dimensionAxis, rangeScale, dimensionScaleKey } = getOrientationProperties(orientation); + const { + dimensionAxis, + rangeScale, + dimensionScaleKey, + metricScaleKey: scaleKey, + metricAxis: startKey, + } = getOrientationProperties(orientation); + + const endKey = `${startKey}2`; return { + ...(animations && + animateFromZero && { + [startKey]: getAnimationMarks(dimension, `${metric}0`, data, previousData, scaleKey), + [endKey]: getAnimationMarks(dimension, `${metric}1`, data, previousData, scaleKey), + }), [dimensionAxis]: { scale: dimensionScaleKey, field: dimension }, [rangeScale]: { scale: dimensionScaleKey, band: 1 }, }; diff --git a/src/specBuilder/chartSpecBuilder.test.ts b/src/specBuilder/chartSpecBuilder.test.ts index c3df37eb2..9ee75e3ff 100644 --- a/src/specBuilder/chartSpecBuilder.test.ts +++ b/src/specBuilder/chartSpecBuilder.test.ts @@ -382,6 +382,7 @@ describe('Chart spec builder', () => { colorScheme: 'light', hiddenSeries: undefined, highlightedSeries: 'Chrome', + animations: false, }); expect(spec.signals?.find((signal) => signal.name === HIGHLIGHTED_SERIES)).toStrictEqual({ @@ -400,6 +401,7 @@ describe('Chart spec builder', () => { colorScheme: 'light', hiddenSeries: undefined, highlightedSeries: 'Chrome', + animations: false, }); expect(spec.signals?.find((signal) => signal.name === HIGHLIGHTED_SERIES)).toStrictEqual({ @@ -418,6 +420,7 @@ describe('Chart spec builder', () => { colorScheme: 'light', hiddenSeries: undefined, highlightedSeries: undefined, + animations: false, }); const uncontrolledHighlightSignal = { name: HIGHLIGHTED_SERIES, @@ -468,14 +471,22 @@ describe('Chart spec builder', () => { test('hiddenSeries is empty when no hidden series', () => { expect( - getDefaultSignals(DEFAULT_BACKGROUND_COLOR, 'categorical12', 'light', ['dashed'], [1]) + getDefaultSignals(false, DEFAULT_BACKGROUND_COLOR, 'categorical12', 'light', ['dashed'], [1]) ).toStrictEqual([...beginningSignals, { name: 'hiddenSeries', value: [] }, ...endSignals]); }); test('hiddenSeries contains provided hidden series', () => { const hiddenSeries = ['test']; expect( - getDefaultSignals(DEFAULT_BACKGROUND_COLOR, 'categorical12', 'light', ['dashed'], [1], hiddenSeries) + getDefaultSignals( + false, + DEFAULT_BACKGROUND_COLOR, + 'categorical12', + 'light', + ['dashed'], + [1], + hiddenSeries + ) ).toStrictEqual([...beginningSignals, { name: 'hiddenSeries', value: hiddenSeries }, ...endSignals]); }); }); diff --git a/src/specBuilder/chartSpecBuilder.ts b/src/specBuilder/chartSpecBuilder.ts index 568af076f..693eeeeac 100644 --- a/src/specBuilder/chartSpecBuilder.ts +++ b/src/specBuilder/chartSpecBuilder.ts @@ -11,6 +11,8 @@ */ import { BACKGROUND_COLOR, + DATA_ANIMATION_DURATION_FRAMES, + DATA_ANIMATION_MILLISECONDS_PER_FRAME, DEFAULT_BACKGROUND_COLOR, DEFAULT_COLOR_SCHEME, DEFAULT_LINE_TYPES, @@ -85,6 +87,10 @@ export function buildSpec({ children, colors = 'categorical12', description, + data, + previousData, + animations, + animateFromZero, hiddenSeries, highlightedSeries, lineTypes = DEFAULT_LINE_TYPES, @@ -97,6 +103,7 @@ export function buildSpec({ }: SanitizedSpecProps) { let spec = initializeSpec(null, { backgroundColor, colorScheme, description, title }); spec.signals = getDefaultSignals( + animations, backgroundColor, colors, colorScheme, @@ -132,16 +139,33 @@ export function buildSpec({ * If we simply compare cur.type to the component, * that uses referential equailty which fails in production when the component is imported from a different module like ./alpha */ + switch (cur.type.displayName) { case Area.displayName: areaCount++; - return addArea(acc, { ...(cur as AreaElement).props, colorScheme, index: areaCount }); + return addArea(acc, { + ...(cur as AreaElement).props, + colorScheme, + index: areaCount, + previousData, + data, + animations, + animateFromZero + }); case Axis.displayName: axisCount++; return addAxis(acc, { ...(cur as AxisElement).props, colorScheme, index: axisCount }); case Bar.displayName: barCount++; - return addBar(acc, { ...(cur as BarElement).props, colorScheme, index: barCount }); + return addBar(acc, { + ...(cur as BarElement).props, + colorScheme, + index: barCount, + previousData, + data, + animations, + animateFromZero + }); case Donut.displayName: donutCount++; return addDonut(acc, { ...(cur as DonutElement).props, colorScheme, index: donutCount }); @@ -153,13 +177,22 @@ export function buildSpec({ index: legendCount, hiddenSeries, highlightedSeries, + animations, }); case Line.displayName: lineCount++; - return addLine(acc, { ...(cur as LineElement).props, colorScheme, index: lineCount }); + return addLine(acc, { + ...(cur as LineElement).props, + colorScheme, + index: lineCount, + data, + previousData, + animations, + animateFromZero + }); case Scatter.displayName: scatterCount++; - return addScatter(acc, { ...(cur as ScatterElement).props, colorScheme, index: scatterCount }); + return addScatter(acc, { ...(cur as ScatterElement).props, colorScheme, index: scatterCount, animations }); case Title.displayName: // No title count. There can only be one title. return addTitle(acc, { ...(cur as TitleElement).props }); @@ -175,7 +208,7 @@ export function buildSpec({ // add signals and update marks for controlled highlighting if there isn't a legend with highlight enabled if (highlightedSeries) { - setHoverOpacityForMarks(spec.marks ?? []); + setHoverOpacityForMarks(spec.marks ?? [], animations); } // clear out all scales that don't have any fields on the domain @@ -206,6 +239,7 @@ const initializeComponentCounts = () => { }; export const getDefaultSignals = ( + animations: boolean, backgroundColor: string, colors: ChartColors, colorScheme: ColorScheme, @@ -217,7 +251,7 @@ export const getDefaultSignals = ( // if the background color is transparent, then we want to set the signal background color to gray-50 // if the signal background color were transparent then backgroundMarks and annotation fill would also be transparent const signalBackgroundColor = backgroundColor === 'transparent' ? 'gray-50' : backgroundColor; - return [ + const signals = [ getGenericSignal(BACKGROUND_COLOR, getColorValue(signalBackgroundColor, colorScheme)), getGenericSignal('colors', getTwoDimensionalColorScheme(colors, colorScheme)), getGenericSignal('lineTypes', getTwoDimensionalLineTypes(lineTypes)), @@ -227,7 +261,19 @@ export const getDefaultSignals = ( getGenericSignal(HIGHLIGHTED_SERIES, highlightedSeries), getGenericSignal(SELECTED_ITEM), getGenericSignal(SELECTED_SERIES), - ]; + ] + if (animations) { + signals.push(getTimer()); + } + return signals; +}; + +export const getTimer = () => { + return { + name: 'timerValue', + value: '0', + on: [{ events: `timer{${DATA_ANIMATION_MILLISECONDS_PER_FRAME}}`, update: `min(1, timerValue + (1 / ${DATA_ANIMATION_DURATION_FRAMES}))` }], + }; }; export const getTwoDimensionalColorScheme = (colors: ChartColors, colorScheme: ColorScheme): string[][] => { diff --git a/src/specBuilder/data/dataUtils.ts b/src/specBuilder/data/dataUtils.ts index 9f29cd49a..aa80b430b 100644 --- a/src/specBuilder/data/dataUtils.ts +++ b/src/specBuilder/data/dataUtils.ts @@ -12,12 +12,15 @@ import { DEFAULT_TIME_DIMENSION, DEFAULT_TRANSFORMED_TIME_DIMENSION, + FILTERED_PREVIOUS_TABLE, FILTERED_TABLE, + MARK_ID, + PREVIOUS_TABLE, SERIES_ID, TABLE, } from '@constants'; import { produce } from 'immer'; -import { Compare, Data, FormulaTransform, SourceData, Transforms, ValuesData } from 'vega'; +import { Compare, Data, FormulaTransform, IdentifierTransform, SourceData, Transforms, ValuesData } from 'vega'; export const addTimeTransform = produce((transforms, dimension) => { if (transforms.findIndex((transform) => transform.type === 'timeunit') === -1) { @@ -45,6 +48,11 @@ export const getTableData = (data: Data[]): ValuesData => { // ok to cast this here because we know that the data array will always have table data of type ValuesData return data.find((d) => d.name === TABLE) as ValuesData; }; + +export const getPreviousTableData = (data: Data[]): ValuesData => { + // ok to cast this here because we know that the data array will always have table data of type ValuesData + return data.find((d) => d.name === PREVIOUS_TABLE) as ValuesData; +}; /** * gets the filtered table data from the data array * @param data @@ -55,6 +63,11 @@ export const getFilteredTableData = (data: Data[]): SourceData => { return data.find((d) => d.name === FILTERED_TABLE) as SourceData; }; +export const getFilteredPreviousTableData = (data: Data[]): SourceData => { + // ok to cast this here because we know that the data array will always have table data of type SourceData + return data.find((d) => d.name === FILTERED_PREVIOUS_TABLE) as SourceData; +}; + export const getSeriesIdTransform = (facets: string[]): FormulaTransform => { const expr = facets.map((facet) => `datum.${facet}`).join(' + " | " + '); return { @@ -63,3 +76,10 @@ export const getSeriesIdTransform = (facets: string[]): FormulaTransform => { expr, }; }; + +export const getIdentifierTransform = (): IdentifierTransform => { + return { + type: 'identifier', + as: MARK_ID, + }; +}; diff --git a/src/specBuilder/donut/donutSpecBuilder.ts b/src/specBuilder/donut/donutSpecBuilder.ts index 1aa817cac..9d6dcf158 100644 --- a/src/specBuilder/donut/donutSpecBuilder.ts +++ b/src/specBuilder/donut/donutSpecBuilder.ts @@ -132,5 +132,5 @@ export const addMarks = produce((marks, props) => { export const addSignals = produce((signals, props) => { const { name, children } = props; if (!hasInteractiveChildren(children)) return; - addHighlightedItemSignalEvents(signals, name); + addHighlightedItemSignalEvents({ signals, markName: name, isEnabled: false }); }); diff --git a/src/specBuilder/donut/donutUtils.test.ts b/src/specBuilder/donut/donutUtils.test.ts index 7fa17960b..2f831e709 100644 --- a/src/specBuilder/donut/donutUtils.test.ts +++ b/src/specBuilder/donut/donutUtils.test.ts @@ -469,7 +469,7 @@ describe('getArcMark', () => { fill: { scale: COLOR_SCALE, field: 'id' }, x: { signal: 'width / 2' }, y: { signal: 'height / 2' }, - tooltip: getTooltip(children, 'Test'), + tooltip: getTooltip({children, name: 'Test'}), }, update: { startAngle: { field: 'startAngle' }, diff --git a/src/specBuilder/donut/donutUtils.ts b/src/specBuilder/donut/donutUtils.ts index f1822e808..58d91e085 100644 --- a/src/specBuilder/donut/donutUtils.ts +++ b/src/specBuilder/donut/donutUtils.ts @@ -40,7 +40,7 @@ export const getArcMark = (name: string, holeRatio: number, radius: string, chil fill: { scale: COLOR_SCALE, field: 'id' }, x: { signal: 'width / 2' }, y: { signal: 'height / 2' }, - tooltip: getTooltip(children, name), + tooltip: getTooltip({ children, name }), }, update: { startAngle: { field: 'startAngle' }, diff --git a/src/specBuilder/legend/legendHighlightUtils.test.ts b/src/specBuilder/legend/legendHighlightUtils.test.ts index 75b34d26c..beac63b33 100644 --- a/src/specBuilder/legend/legendHighlightUtils.test.ts +++ b/src/specBuilder/legend/legendHighlightUtils.test.ts @@ -12,8 +12,10 @@ import { DEFAULT_COLOR, DEFAULT_OPACITY_RULE, + HIGHLIGHTED_ITEM, HIGHLIGHTED_SERIES, HIGHLIGHT_CONTRAST_RATIO, + MARK_ID, OPACITY_SCALE, SERIES_ID, } from '@constants'; @@ -36,6 +38,43 @@ const defaultOpacityEncoding = { ], }; +const animationsOpacityEncodingDefault = { opacity: [{ + test: `${HIGHLIGHTED_SERIES} && ${HIGHLIGHTED_SERIES} !== datum.${SERIES_ID}`, + signal: `max(1-rscColorAnimation, 1 / ${HIGHLIGHT_CONTRAST_RATIO})` +}, +{ + test: `!${HIGHLIGHTED_SERIES} && ${HIGHLIGHTED_SERIES}_prev !== datum.${SERIES_ID}`, + signal: `max(1-rscColorAnimation, 1 / ${HIGHLIGHT_CONTRAST_RATIO})` +}, +DEFAULT_OPACITY_RULE +]} + +const animationsOpacityEncodingBarScatter = { + opacity: [ + { + // If there is no current selection, but there is a hover and the hover is NOT for the current bar + test: `${HIGHLIGHTED_ITEM} && ${HIGHLIGHTED_ITEM} !== datum.${MARK_ID}`, + signal: `max(1-rscColorAnimation, 1 / ${HIGHLIGHT_CONTRAST_RATIO})` + }, + { + // If there is a highlighted series and the highlighted series is NOT the series of the current bar + test: `${HIGHLIGHTED_SERIES} && ${HIGHLIGHTED_SERIES} !== datum.${SERIES_ID}`, + signal: `max(1-rscColorAnimation, 1 / ${HIGHLIGHT_CONTRAST_RATIO})` + }, + { + // If there is no highlighted series and the previously highlighted series is the series of the current bar + test: `!${HIGHLIGHTED_SERIES} && ${HIGHLIGHTED_SERIES}_prev == datum.${SERIES_ID}`, + value: 1 + }, + { + // If the previously hovered bar is NOT the current bar and the color animation direction is reversed (fading in) + test: `${HIGHLIGHTED_ITEM}_prev !== datum.${MARK_ID} && rscColorAnimationDirection === -1`, + signal: `max(1-rscColorAnimation, 1 / ${HIGHLIGHT_CONTRAST_RATIO})` + }, + { value: 1 } + ] +} + describe('getHighlightOpacityRule()', () => { test('scale ref should divide by highlight contrast ratio', () => { expect(getHighlightOpacityRule({ scale: OPACITY_SCALE, field: DEFAULT_COLOR })).toStrictEqual({ @@ -96,14 +135,19 @@ describe('setHoverOpacityForMarks()', () => { describe('no initial state', () => { test('should not modify the marks', () => { const marks = []; - setHoverOpacityForMarks(marks); + setHoverOpacityForMarks(marks, false); + expect(marks).toEqual([]); + }); + test('should not modify the marks with animations', () => { + const marks = []; + setHoverOpacityForMarks(marks, true); expect(marks).toEqual([]); }); }); describe('bar mark initial state', () => { test('encoding should be added for opacity', () => { const marks = JSON.parse(JSON.stringify([defaultMark])); - setHoverOpacityForMarks(marks); + setHoverOpacityForMarks(marks, false); expect(marks).toStrictEqual([ { ...defaultMark, encode: { ...defaultMark.encode, update: defaultOpacityEncoding } }, ]); @@ -117,7 +161,7 @@ describe('setHoverOpacityForMarks()', () => { }, ]) ); - setHoverOpacityForMarks(marks); + setHoverOpacityForMarks(marks, false); expect(marks).toStrictEqual([ { ...defaultMark, @@ -135,7 +179,7 @@ describe('setHoverOpacityForMarks()', () => { describe('group mark initial state', () => { test('encoding should be added for opacity', () => { const marks = JSON.parse(JSON.stringify([defaultGroupMark])); - setHoverOpacityForMarks(marks); + setHoverOpacityForMarks(marks, false); expect(marks).toStrictEqual([ { ...defaultGroupMark, @@ -144,4 +188,55 @@ describe('setHoverOpacityForMarks()', () => { ]); }); }); + describe('bar mark initial state with animations', () => { + test('encoding should be added for opacity', () => { + const marks = JSON.parse(JSON.stringify([defaultMark])); + setHoverOpacityForMarks(marks, true); + expect(marks).toStrictEqual([ + { ...defaultMark, encode: { ...defaultMark.encode, update: animationsOpacityEncodingDefault } }, + ]); + }); + test('opacity encoding already exists, rules should be added in the correct spot', () => { + const marks = JSON.parse( + JSON.stringify([ + { + ...defaultMark, + encode: { ...defaultMark.encode, update: { opacity: [DEFAULT_OPACITY_RULE] } }, + }, + ]) + ); + setHoverOpacityForMarks(marks, true); + expect(marks).toStrictEqual([ + { + ...defaultMark, + encode: { + ...defaultMark.encode, + update: animationsOpacityEncodingDefault, + }, + }, + ]); + }); + test('opacity encoding for bar0', () => { + const marks = JSON.parse(JSON.stringify([{ + ...defaultMark, + name: 'bar0', + type: 'bar', + }])); + setHoverOpacityForMarks(marks, true); + expect(marks).toStrictEqual([ + { ...defaultMark, name: 'bar0', type:'bar', encode: { ...defaultMark.encode, update: animationsOpacityEncodingBarScatter } }, + ]); + }) + test('opacity encoding for scatter0', () => { + const marks = JSON.parse(JSON.stringify([{ + ...defaultMark, + name: 'scatter0', + type: 'scatter', + }])); + setHoverOpacityForMarks(marks, true); + expect(marks).toStrictEqual([ + { ...defaultMark, name: 'scatter0', type: 'scatter', encode: { ...defaultMark.encode, update: animationsOpacityEncodingBarScatter } }, + ]); + }) + }); }); diff --git a/src/specBuilder/legend/legendHighlightUtils.ts b/src/specBuilder/legend/legendHighlightUtils.ts index a655674ad..9e040866b 100644 --- a/src/specBuilder/legend/legendHighlightUtils.ts +++ b/src/specBuilder/legend/legendHighlightUtils.ts @@ -9,13 +9,15 @@ * OF ANY KIND, either express or implied. See the License for the specific language * governing permissions and limitations under the License. */ -import { COLOR_SCALE, HIGHLIGHTED_SERIES, HIGHLIGHT_CONTRAST_RATIO, SERIES_ID } from '@constants'; +import { COLOR_SCALE, HIGHLIGHT_CONTRAST_RATIO, HIGHLIGHTED_SERIES, SERIES_ID } from '@constants'; import { GroupMark, Mark, NumericValueRef, ProductionRule } from 'vega'; +import { getMarkWithLegendHighlightOpacityRules, getSeriesAnimationOpacityRules } from '@specBuilder/marks/markUtils'; + /** * Adds opacity tests for the fill and stroke of marks that use the color scale to set the fill or stroke value. */ -export const setHoverOpacityForMarks = (marks: Mark[], keys?: string[], name?: string) => { +export const setHoverOpacityForMarks = (marks: Mark[], animations?: boolean, keys?: string[], name?: string) => { if (!marks.length) return; const flatMarks = flattenMarks(marks); const seriesMarks = flatMarks.filter(markUsesSeriesColorScale); @@ -39,9 +41,20 @@ export const setHoverOpacityForMarks = (marks: Mark[], keys?: string[], name?: s if (!Array.isArray(update.opacity)) { update.opacity = []; } - // need to insert the new test in the second to last slot - const opacityRuleInsertIndex = Math.max(update.opacity.length - 1, 0); - update.opacity.splice(opacityRuleInsertIndex, 0, highlightOpacityRule); + // if animations are enabled, update the opacity rules for the mark. + if (animations) { + // bar and scatter have different rules due to using the mark ID for highlighting + if (mark.name == 'bar0' || mark.name == 'scatter0') { + update.opacity = getMarkWithLegendHighlightOpacityRules(); + } else { + // the mark rules for charts using the series ID for highlighting (line, area). + update.opacity = getSeriesAnimationOpacityRules(); + } + } else { + // need to insert the new test in the second to last slot + const opacityRuleInsertIndex = Math.max(update.opacity.length - 1, 0); + update.opacity.splice(opacityRuleInsertIndex, 0, highlightOpacityRule); + } } }); }; diff --git a/src/specBuilder/legend/legendSpecBuilder.test.ts b/src/specBuilder/legend/legendSpecBuilder.test.ts index e30390645..44c0a22c7 100644 --- a/src/specBuilder/legend/legendSpecBuilder.test.ts +++ b/src/specBuilder/legend/legendSpecBuilder.test.ts @@ -14,8 +14,10 @@ import { DEFAULT_COLOR, DEFAULT_COLOR_SCHEME, DEFAULT_SECONDARY_COLOR, + HIGHLIGHTED_ITEM, HIGHLIGHTED_SERIES, LINEAR_COLOR_SCALE, + RSC_ANIMATION, TABLE, } from '@constants'; import { @@ -29,6 +31,7 @@ import { Data, Legend, LegendEncode, Scale, Spec, SymbolEncodeEntry } from 'vega import { addData, addLegend, addSignals, formatFacetRefsWithPresets, getContinuousLegend } from './legendSpecBuilder'; import { defaultLegendProps, opacityEncoding } from './legendTestUtils'; +import { defaultAnimationScales } from '@specBuilder/scale/scaleSpecBuilder.test'; const defaultSpec: Spec = { signals: defaultSignals, @@ -141,7 +144,7 @@ const defaultHighlightSeriesSignal = { describe('addLegend()', () => { describe('no initial legend', () => { test('no props, should setup default legend', () => { - expect(addLegend(defaultSpec, {})).toStrictEqual({ + expect(addLegend(defaultSpec, {animations: false,})).toStrictEqual({ ...defaultSpec, data: [defaultLegendAggregateData], scales: [...(defaultSpec.scales || []), defaultLegendEntriesScale], @@ -151,7 +154,7 @@ describe('addLegend()', () => { test('descriptions, should add encoding', () => { expect( - addLegend(defaultSpec, { descriptions: [{ seriesName: 'test', description: 'test' }] }) + addLegend(defaultSpec, { animations: false, descriptions: [{ seriesName: 'test', description: 'test' }] }) ).toStrictEqual({ ...defaultSpec, data: [defaultLegendAggregateData], @@ -161,7 +164,7 @@ describe('addLegend()', () => { }); test('highlight, should add encoding', () => { - expect(addLegend(defaultSpec, { highlight: true })).toStrictEqual({ + expect(addLegend(defaultSpec, { animations: false, highlight: true })).toStrictEqual({ ...defaultSpec, data: [defaultLegendAggregateData], scales: [...(defaultSpec.scales || []), defaultLegendEntriesScale], @@ -176,7 +179,7 @@ describe('addLegend()', () => { }); test('position, should set the orientation correctly', () => { - expect(addLegend(defaultSpec, { position: 'left' }).legends).toStrictEqual([ + expect(addLegend(defaultSpec, { animations: false, position: 'left' }).legends).toStrictEqual([ { ...defaultLegend, orient: 'left', direction: 'vertical', columns: undefined, labelLimit: undefined }, ]); }); @@ -188,6 +191,7 @@ describe('addLegend()', () => { test('should add labels to signals using legendLabels', () => { expect( addLegend(defaultSpec, { + animations: false, legendLabels: [ { seriesName: 1, label: 'Any event' }, { seriesName: 2, label: 'Any event' }, @@ -218,6 +222,7 @@ describe('addLegend()', () => { test('should have both labels and highlight encoding', () => { expect( addLegend(defaultSpec, { + animations: false, highlight: true, legendLabels: [ { seriesName: 1, label: 'Any event' }, @@ -246,6 +251,7 @@ describe('addLegend()', () => { test('should add custom labels to encoding based on legendLabels', () => { expect( addLegend(defaultSpec, { + animations: false, legendLabels: [ { seriesName: 1, label: 'Any event' }, { seriesName: 2, label: 'Any event' }, @@ -265,6 +271,7 @@ describe('addLegend()', () => { test('should add labelLimit if provided', () => { const legendSpec = addLegend(defaultSpec, { + animations: false, descriptions: [{ seriesName: 'test', description: 'test' }], labelLimit: 300, }); @@ -280,7 +287,7 @@ describe('addLegend()', () => { test('should add fields to scales if they have not been added', () => { const legendSpec = addLegend( { ...defaultSpec, scales: [{ name: COLOR_SCALE, type: 'ordinal' }] }, - { color: 'series' } + { animations: false, color: 'series' } ); expect(legendSpec.scales).toEqual([ { @@ -298,6 +305,29 @@ describe('addLegend()', () => { }, ]); }); + + test('should add fields to scales if they have not been added with animations', () => { + const legendSpec = addLegend( + { ...defaultSpec, scales: [{ name: COLOR_SCALE, type: 'ordinal' }] }, + { color: 'series', animations: true } + ); + expect(legendSpec.scales).toEqual([ + { + name: COLOR_SCALE, + type: 'ordinal', + domain: { data: 'table', fields: ['series'] }, + }, + ...defaultAnimationScales, + { + name: 'legend0Entries', + type: 'ordinal', + domain: { + data: 'legend0Aggregate', + field: 'legend0Entries', + }, + }, + ]); + }); }); }); @@ -376,11 +406,26 @@ describe('addSignals()', () => { }); test('should add legendLabels signal if legendLabels are defined', () => { expect( - addSignals(defaultSignals, { ...defaultLegendProps, legendLabels: [] }).find( + addSignals(defaultSignals, { ...defaultLegendProps, legendLabels: []}).find( (signal) => signal.name === 'legendLabels' ) ).toBeDefined(); }); + test('should add animation signals if animations is true', () => { + const signals = addSignals(defaultSignals, { ...defaultLegendProps, animations: true }) + expect(signals).toBeDefined() + expect(signals).toHaveLength(9); + expect(signals[4]).toHaveProperty('name', RSC_ANIMATION); + expect(signals[4].on).toHaveLength(1); + expect(signals[5]).toHaveProperty('name', 'rscColorAnimationDirection'); + expect(signals[5].on).toHaveLength(4); + expect(signals[6]).toHaveProperty('name', 'rscColorAnimation'); + expect(signals[6].on).toHaveLength(1); + expect(signals[7]).toHaveProperty('name', `${HIGHLIGHTED_ITEM}_prev`); + expect(signals[7].on).toHaveLength(2); + expect(signals[8]).toHaveProperty('name', `${HIGHLIGHTED_SERIES}_prev`); + expect(signals[8].on).toHaveLength(1); + }); test('should NOT add hiddenSeries signal if isToggleable is false', () => { expect( addSignals(defaultSignals, { ...defaultLegendProps, isToggleable: false }).find( diff --git a/src/specBuilder/legend/legendSpecBuilder.ts b/src/specBuilder/legend/legendSpecBuilder.ts index 78938eca6..de8de91b4 100644 --- a/src/specBuilder/legend/legendSpecBuilder.ts +++ b/src/specBuilder/legend/legendSpecBuilder.ts @@ -12,13 +12,15 @@ import { COLOR_SCALE, DEFAULT_COLOR_SCHEME, + HIGHLIGHTED_ITEM, LINEAR_COLOR_SCALE, LINE_TYPE_SCALE, OPACITY_SCALE, + RSC_ANIMATION, SYMBOL_SHAPE_SCALE, SYMBOL_SIZE_SCALE, } from '@constants'; -import { addFieldToFacetScaleDomain } from '@specBuilder/scale/scaleSpecBuilder'; +import { addFieldToFacetScaleDomain, addRscAnimationScales } from '@specBuilder/scale/scaleSpecBuilder'; import { getColorValue, getLineWidthPixelsFromLineWidth, @@ -39,8 +41,11 @@ import { import { Data, Legend, Mark, Scale, Signal, Spec } from 'vega'; import { - addHighlighSignalLegendHoverEvents, + addHighlightSignalLegendHoverEvents, getLegendLabelsSeriesSignal, + getRscAnimationSignals, + getRscLegendColorAnimationDirection, + getRscLegendHighlightedItemPrev, hasSignalByName, } from '../signal/signalSpecBuilder'; import { getFacets, getFacetsFromKeys } from './legendFacetUtils'; @@ -49,7 +54,15 @@ import { Facet, getColumns, getEncodings, getHiddenEntriesFilter, getSymbolType export const addLegend = produce< Spec, - [LegendProps & { colorScheme?: ColorScheme; index?: number; hiddenSeries?: string[]; highlightedSeries?: string }] + [ + LegendProps & { + colorScheme?: ColorScheme; + index?: number; + hiddenSeries?: string[]; + highlightedSeries?: string; + animations?: boolean; + } + ] >( ( spec, @@ -120,9 +133,12 @@ export const addLegend = produce< spec.data = addData(spec.data ?? [], { ...legendProps, facets: uniqueFacetFields }); spec.signals = addSignals(spec.signals ?? [], legendProps); spec.marks = addMarks(spec.marks ?? [], legendProps); - // add the legend - legends.push(getCategoricalLegend(ordinalFacets, legendProps)); + /* + marks had to be pushed to getCategoricalLegend to check which marks are being update as + the legend opacity rules change depending on chart type. + */ + legends.push(getCategoricalLegend(ordinalFacets, legendProps, spec.marks)); } // continuous legends cannot be combined with any other legends @@ -190,14 +206,14 @@ export const formatFacetRefsWithPresets = ( * @param props * @returns */ -const getCategoricalLegend = (facets: Facet[], props: LegendSpecProps): Legend => { +const getCategoricalLegend = (facets: Facet[], props: LegendSpecProps, marks: Mark[]): Legend => { const { name, position, title, labelLimit } = props; return { fill: `${name}Entries`, direction: ['top', 'bottom'].includes(position) ? 'horizontal' : 'vertical', orient: position, title, - encode: getEncodings(facets, props), + encode: getEncodings(facets, props, marks), columns: getColumns(position), labelLimit, }; @@ -234,18 +250,25 @@ const getLegendLayout = ({ position, title }: LegendSpecProps): Partial /** * Adds a new scale that is used to create the legend entries */ -const addScales = produce((scales, { color, lineType, opacity, symbolShape }) => { - // it is possible to define fields to facet the data off of on the legend - // if these fields are not already defined on the scales, we need to add them - addFieldToFacetScaleDomain(scales, COLOR_SCALE, color); - addFieldToFacetScaleDomain(scales, LINE_TYPE_SCALE, lineType); - addFieldToFacetScaleDomain(scales, OPACITY_SCALE, opacity); - addFieldToFacetScaleDomain(scales, SYMBOL_SHAPE_SCALE, symbolShape); -}); +const addScales = produce( + (scales, { color, lineType, opacity, symbolShape, animations }) => { + // if animations are enabled, add all necessary animation scales. + + if (animations) { + addRscAnimationScales(scales); + } + // it is possible to define fields to facet the data off of on the legend + // if these fields are not already defined on the scales, we need to add them + addFieldToFacetScaleDomain(scales, COLOR_SCALE, color); + addFieldToFacetScaleDomain(scales, LINE_TYPE_SCALE, lineType); + addFieldToFacetScaleDomain(scales, OPACITY_SCALE, opacity); + addFieldToFacetScaleDomain(scales, SYMBOL_SHAPE_SCALE, symbolShape); + } +); -const addMarks = produce((marks, { highlight, keys, name }) => { +const addMarks = produce((marks, { highlight, keys, name, animations }) => { if (highlight) { - setHoverOpacityForMarks(marks, keys, name); + setHoverOpacityForMarks(marks, animations, keys, name); } }); @@ -278,9 +301,24 @@ export const addData = produce ); export const addSignals = produce( - (signals, { hiddenSeries, highlight, isToggleable, legendLabels, name }) => { + (signals, { hiddenSeries, highlight, isToggleable, legendLabels, name, animations }) => { + // if animations are enabled, add all necessary animation signals. + if (animations) { + // we don't want to add the signals if they already exist. + // Since they come as a package, we only need to check if one animation signal is present. + if (!hasSignalByName(signals, RSC_ANIMATION)) { + signals.push(...getRscAnimationSignals(name)); + } + signals + .find((sig) => sig.name == 'rscColorAnimationDirection') + ?.on?.push(...getRscLegendColorAnimationDirection(name)); + signals + .find((sig) => sig.name == `${HIGHLIGHTED_ITEM}_prev`) + ?.on?.push(...getRscLegendHighlightedItemPrev(name)); + } + if (highlight) { - addHighlighSignalLegendHoverEvents(signals, name, Boolean(isToggleable || hiddenSeries)); + addHighlightSignalLegendHoverEvents(signals, name, Boolean(isToggleable || hiddenSeries)); } if (legendLabels) { @@ -289,4 +327,4 @@ export const addSignals = produce( } } } -); +); \ No newline at end of file diff --git a/src/specBuilder/legend/legendUtils.ts b/src/specBuilder/legend/legendUtils.ts index 3c20e99f9..81700c79b 100644 --- a/src/specBuilder/legend/legendUtils.ts +++ b/src/specBuilder/legend/legendUtils.ts @@ -18,7 +18,7 @@ import { LINE_WIDTH_SCALE, OPACITY_SCALE, SYMBOL_SHAPE_SCALE, - SYMBOL_SIZE_SCALE, + SYMBOL_SIZE_SCALE } from '@constants'; import { getColorValue, getPathFromSymbolShape } from '@specBuilder/specUtils'; import { spectrumColors } from '@themes'; @@ -30,7 +30,7 @@ import { LegendLabel, LegendSpecProps, Position, - SecondaryFacetType, + SecondaryFacetType } from 'types'; import { BaseValueRef, @@ -39,13 +39,15 @@ import { FilterTransform, GuideEncodeEntry, LegendEncode, + Mark, NumericValueRef, ProductionRule, SignalRef, - SymbolEncodeEntry, + SymbolEncodeEntry } from 'vega'; import { ColorValueV6 } from '@react-types/shared'; +import { getLegendMarkOpacityRules, getLegendSeriesOpacityRules } from '@specBuilder/marks/markUtils'; export interface Facet { facetType: FacetType | SecondaryFacetType; @@ -72,8 +74,8 @@ export const getHiddenEntriesFilter = (hiddenEntries: string[], name: string): F return [ { type: 'filter', - expr: `indexof(${JSON.stringify(hiddenEntries)}, datum.${name}Entries) === -1`, - }, + expr: `indexof(${JSON.stringify(hiddenEntries)}, datum.${name}Entries) === -1` + } ]; }; @@ -81,11 +83,12 @@ export const getHiddenEntriesFilter = (hiddenEntries: string[], name: string): F * Get the legend encodings * @param facets * @param legendProps + * @param marks * @returns */ -export const getEncodings = (facets: Facet[], legendProps: LegendSpecProps): LegendEncode => { +export const getEncodings = (facets: Facet[], legendProps: LegendSpecProps, marks: Mark[]): LegendEncode => { const symbolEncodings = getSymbolEncodings(facets, legendProps); - const hoverEncodings = getHoverEncodings(facets, legendProps); + const hoverEncodings = getHoverEncodings(facets, legendProps, marks); const legendLabelsEncodings = getLegendLabelsEncodings(legendProps.legendLabels); const showHideEncodings = getShowHideEncodings(legendProps); // merge the encodings together @@ -100,19 +103,19 @@ const getLegendLabelsEncodings = (legendLabels: LegendLabel[] | undefined): Lege text: [ { // Test whether a legendLabel exists for the seriesName, if not use the seriesName - test: "indexof(pluck(legendLabels, 'seriesName'), datum.value) > -1", - signal: "legendLabels[indexof(pluck(legendLabels, 'seriesName'), datum.value)].label", + test: 'indexof(pluck(legendLabels, \'seriesName\'), datum.value) > -1', + signal: 'legendLabels[indexof(pluck(legendLabels, \'seriesName\'), datum.value)].label' }, - { signal: 'datum.value' }, - ], - }, - }, + { signal: 'datum.value' } + ] + } + } }; } return {}; }; -const getHoverEncodings = (facets: Facet[], props: LegendSpecProps): LegendEncode => { +const getHoverEncodings = (facets: Facet[], props: LegendSpecProps, marks: Mark[]): LegendEncode => { const { highlight, highlightedSeries, name, onMouseOver, onMouseOut, descriptions } = props; if (highlight || highlightedSeries || descriptions) { return { @@ -120,20 +123,20 @@ const getHoverEncodings = (facets: Facet[], props: LegendSpecProps): LegendEncod name: `${name}_legendEntry`, interactive: true, enter: { - tooltip: getTooltip(descriptions, name), // only add tooltip if descriptions exist + tooltip: getTooltip(descriptions, name) // only add tooltip if descriptions exist }, update: { - fill: { value: 'transparent' }, // need something here to trigger the tooltip - }, + fill: { value: 'transparent' } // need something here to trigger the tooltip + } }, labels: { update: { - opacity: getOpacityEncoding(props), + opacity: getOpacityEncoding(props, marks), }, }, symbols: { update: { - opacity: getOpacityEncoding(props), + opacity: getOpacityEncoding(props, marks), }, }, }; @@ -143,9 +146,9 @@ const getHoverEncodings = (facets: Facet[], props: LegendSpecProps): LegendEncod name: `${name}_legendEntry`, interactive: true, enter: { - fill: { value: 'transparent' }, - }, - }, + fill: { value: 'transparent' } + } + } }; } @@ -162,23 +165,41 @@ const getTooltip = (descriptions: LegendDescription[] | undefined, name: string) /** * simple opacity encoding for legend labels and the symbol stroke opacity * @param legendProps - * @returns opactiy encoding + * @returns opacity encoding + * @param marks */ export const getOpacityEncoding = ({ + animations, highlight, highlightedSeries, keys, name, -}: LegendSpecProps): ProductionRule | undefined => { +}: LegendSpecProps, marks: Mark[]): ProductionRule | undefined => { const highlightSignalName = keys ? `${name}_highlight` : HIGHLIGHTED_SERIES; // only add symbol opacity if highlight is true or highlightedSeries is defined if (highlight || highlightedSeries) { + + //If animations are enabled, set legend animation opacity rules + + if (animations) { + /* + this is the check for chart type and is the reason the spec.marks had to be pushed to + getCategoricalLegend in legendSpecBuilder + */ + if (marks.some((mark) => mark.name == 'line0_group' || mark.name == 'area0_group')) { + // get opacity rules for legend if the chart relies on Series ID for highlighting + return getLegendSeriesOpacityRules(); + } else { + // get opacity rules for legend if the chart relies on Mark ID for highlighting + return getLegendMarkOpacityRules(); + } + } return [ { test: `${highlightSignalName} && datum.value !== ${highlightSignalName}`, - value: 1 / HIGHLIGHT_CONTRAST_RATIO, + value: 1 / HIGHLIGHT_CONTRAST_RATIO }, - DEFAULT_OPACITY_RULE, + DEFAULT_OPACITY_RULE ]; } return undefined; @@ -192,52 +213,52 @@ export const getSymbolEncodings = (facets: Facet[], props: LegendSpecProps): Leg facets, facetType: SYMBOL_SHAPE_SCALE, customValue: symbolShape, - name, + name }), size: getSymbolFacetEncoding({ facets, facetType: SYMBOL_SIZE_SCALE, name }), strokeDash: getSymbolFacetEncoding({ facets, facetType: LINE_TYPE_SCALE, customValue: lineType, - name, + name }), strokeWidth: getSymbolFacetEncoding({ facets, facetType: LINE_WIDTH_SCALE, customValue: lineWidth, - name, - }), + name + }) }; const update: SymbolEncodeEntry = { fill: [ ...getHiddenSeriesColorRule(props, 'gray-300'), getSymbolFacetEncoding({ facets, facetType: COLOR_SCALE, customValue: color, name }) ?? { - value: spectrumColors[colorScheme]['categorical-100'], - }, + value: spectrumColors[colorScheme]['categorical-100'] + } ], stroke: [ ...getHiddenSeriesColorRule(props, 'gray-300'), getSymbolFacetEncoding({ facets, facetType: COLOR_SCALE, customValue: color, name }) ?? { - value: spectrumColors[colorScheme]['categorical-100'], - }, - ], + value: spectrumColors[colorScheme]['categorical-100'] + } + ] }; // Remove undefined values const symbols: GuideEncodeEntry = JSON.parse(JSON.stringify({ enter, update })); return { entries: { - name: `${name}_legendEntry`, + name: `${name}_legendEntry` }, - symbols, + symbols }; }; const getSymbolFacetEncoding = ({ - customValue, - facets, - facetType, - name, -}: { + customValue, + facets, + facetType, + name + }: { customValue?: FacetRef; facets?: Facet[]; facetType: FacetType; @@ -269,7 +290,7 @@ const getSymbolFacetEncoding = ({ if (secondaryFacet) { const { scale, signal } = secondaryFacetMapping[facetType]; return { - signal: `scale('${signal}', data('${name}Aggregate')[datum.index].${facet.field})[indexof(domain('${scale}'), data('${name}Aggregate')[datum.index].${secondaryFacet.field})% length(scale('${signal}', data('${name}Aggregate')[datum.index].${facet.field}))]`, + signal: `scale('${signal}', data('${name}Aggregate')[datum.index].${facet.field})[indexof(domain('${scale}'), data('${name}Aggregate')[datum.index].${secondaryFacet.field})% length(scale('${signal}', data('${name}Aggregate')[datum.index].${facet.field}))]` }; } @@ -302,10 +323,10 @@ export const getShowHideEncodings = (props: LegendSpecProps): LegendEncode => { update: { fill: [ ...getHiddenSeriesColorRule(props, 'gray-500'), - { value: getColorValue('gray-700', colorScheme) }, - ], - }, - }, + { value: getColorValue('gray-700', colorScheme) } + ] + } + } }; } @@ -317,9 +338,9 @@ export const getShowHideEncodings = (props: LegendSpecProps): LegendEncode => { interactive: true, enter: { fill: { value: 'transparent' }, - cursor: { value: 'pointer' }, - }, - }, + cursor: { value: 'pointer' } + } + } }; } return mergeLegendEncodings([hiddenSeriesEncode, clickEncode]); diff --git a/src/specBuilder/line/lineMarkUtils.test.ts b/src/specBuilder/line/lineMarkUtils.test.ts index 4aa873440..0f2abbf27 100644 --- a/src/specBuilder/line/lineMarkUtils.test.ts +++ b/src/specBuilder/line/lineMarkUtils.test.ts @@ -14,6 +14,7 @@ import { createElement } from 'react'; import { ChartPopover } from '@components/ChartPopover'; import { ChartTooltip } from '@components/ChartTooltip'; import { + ANIMATION_FUNCTION, COLOR_SCALE, DEFAULT_OPACITY_RULE, DEFAULT_TRANSFORMED_TIME_DIMENSION, @@ -49,6 +50,33 @@ describe('getLineMark()', () => { }); }); + test('should return line mark with animations', () => { + const lineMark = getLineMark({...defaultLineMarkProps, animations: true, animateFromZero: true}, 'line0_facet'); + expect(lineMark).toEqual({ + name: 'line0', + type: 'line', + from: { data: 'line0_facet' }, + interactive: false, + encode: { + enter: { + stroke: { field: 'series', scale: COLOR_SCALE }, + strokeDash: { value: [] }, + strokeOpacity: DEFAULT_OPACITY_RULE, + strokeWidth: { value: 1 }, + y: { field: 'value', scale: 'yLinear' }, + }, + update: { + y: { + scale: 'yLinear', + signal: `datum.value * ${ANIMATION_FUNCTION}` + }, + x: { field: DEFAULT_TRANSFORMED_TIME_DIMENSION, scale: 'xTime' }, + opacity: [DEFAULT_OPACITY_RULE], + }, + }, + }); + }); + test('should have undefined strokeWidth if lineWidth if undefined', () => { const lineMark = getLineMark({ ...defaultLineMarkProps, lineWidth: undefined }, 'line0_facet'); expect(lineMark.encode?.enter?.strokeWidth).toBeUndefined(); diff --git a/src/specBuilder/line/lineMarkUtils.ts b/src/specBuilder/line/lineMarkUtils.ts index a11819aa9..61c4d7f13 100644 --- a/src/specBuilder/line/lineMarkUtils.ts +++ b/src/specBuilder/line/lineMarkUtils.ts @@ -15,11 +15,13 @@ import { getHighlightOpacityValue, getLineWidthProductionRule, getOpacityProductionRule, + getSeriesAnimationOpacityRules, getStrokeDashProductionRule, getVoronoiPath, getXProductionRule, hasPopover, } from '@specBuilder/marks/markUtils'; +import { getAnimationMarks } from '@specBuilder/specUtils'; import { ScaleType } from 'types'; import { LineMark, Mark, NumericValueRef, ProductionRule, RuleMark, SymbolMark } from 'vega'; @@ -37,8 +39,23 @@ import { LineMarkProps } from './lineUtils'; * @param dataSource * @returns LineMark */ + export const getLineMark = (lineMarkProps: LineMarkProps, dataSource: string): LineMark => { - const { color, colorScheme, dimension, lineType, lineWidth, metric, name, opacity, scaleType } = lineMarkProps; + const { + name, + color, + opacity, + metric, + dimension, + scaleType, + lineType, + lineWidth, + colorScheme, + data, + previousData, + animations, + animateFromZero, + } = lineMarkProps; return { name, @@ -58,6 +75,7 @@ export const getLineMark = (lineMarkProps: LineMarkProps, dataSource: string): L // but it may change the x position if it causes the chart to resize x: getXProductionRule(scaleType, dimension), opacity: getLineOpacity(lineMarkProps), + ...(animations && animateFromZero && { y: getAnimationMarks(dimension, metric, data, previousData) }), }, }, }; @@ -67,10 +85,15 @@ export const getLineOpacity = ({ displayOnHover, interactiveMarkName, popoverMarkName, + animations, }: LineMarkProps): ProductionRule => { if (!interactiveMarkName || displayOnHover) return [DEFAULT_OPACITY_RULE]; const strokeOpacityRules: ProductionRule = []; - + //if animations are enabled, set opacity rules for line mark. + + if (animations) { + return getSeriesAnimationOpacityRules(); + } // add a rule that will lower the opacity of the line if there is a hovered series, but this line is not the one hovered strokeOpacityRules.push({ test: `${HIGHLIGHTED_SERIES} && ${HIGHLIGHTED_SERIES} !== datum.${SERIES_ID}`, @@ -101,7 +124,7 @@ export const getLineHoverMarks = ( dataSource: string, secondaryHighlightedMetric?: string ): Mark[] => { - const { children, dimension, metric, name, scaleType } = lineProps; + const { children, dimension, metric, name, scaleType, animations, animateFromZero } = lineProps; return [ // vertical rule shown for the hovered or selected point getHoverRule(dimension, name, scaleType), @@ -116,7 +139,7 @@ export const getLineHoverMarks = ( // points used for the voronoi transform getPointsForVoronoi(dataSource, dimension, metric, name, scaleType), // voronoi transform used to get nearest point paths - getVoronoiPath(children, `${name}_pointsForVoronoi`, name), + getVoronoiPath(children, `${name}_pointsForVoronoi`, name, animations, animateFromZero), ]; }; diff --git a/src/specBuilder/line/linePointUtils.ts b/src/specBuilder/line/linePointUtils.ts index f71a67fc2..a9e7db03d 100644 --- a/src/specBuilder/line/linePointUtils.ts +++ b/src/specBuilder/line/linePointUtils.ts @@ -17,7 +17,7 @@ import { getXProductionRule, hasPopover, } from '@specBuilder/marks/markUtils'; -import { getColorValue } from '@specBuilder/specUtils'; +import { getAnimationMarks, getColorValue } from '@specBuilder/specUtils'; import { LineSpecProps, ProductionRuleTests } from 'types'; import { ColorValueRef, NumericValueRef, SymbolMark } from 'vega'; @@ -33,6 +33,9 @@ const selectedTest = `${SELECTED_ITEM} && ${SELECTED_ITEM} === datum.${MARK_ID}` */ export const getLineStaticPoint = ({ name, + data, + previousData, + animations, metric, color, colorScheme, @@ -52,6 +55,7 @@ export const getLineStaticPoint = ({ }, update: { x: getXProductionRule(scaleType, dimension), + ...(animations && { y: getAnimationMarks(dimension, metric, data, previousData) }), }, }, }; diff --git a/src/specBuilder/line/lineSpecBuilder.test.ts b/src/specBuilder/line/lineSpecBuilder.test.ts index 4a8ed6474..f62020bcb 100644 --- a/src/specBuilder/line/lineSpecBuilder.test.ts +++ b/src/specBuilder/line/lineSpecBuilder.test.ts @@ -11,9 +11,11 @@ */ import { createElement } from 'react'; +import { ChartTooltip } from '@components/ChartTooltip'; import { MetricRange } from '@components/MetricRange'; import { Trendline } from '@components/Trendline'; import { + ANIMATION_FUNCTION, BACKGROUND_COLOR, COLOR_SCALE, DEFAULT_COLOR, @@ -22,14 +24,18 @@ import { DEFAULT_OPACITY_RULE, DEFAULT_TIME_DIMENSION, DEFAULT_TRANSFORMED_TIME_DIMENSION, + FILTERED_PREVIOUS_TABLE, FILTERED_TABLE, HIGHLIGHTED_ITEM, HIGHLIGHTED_SERIES, MARK_ID, + PREVIOUS_TABLE, + RSC_ANIMATION, SERIES_ID, TABLE, TRENDLINE_VALUE, } from '@constants'; +import { defaultAnimationScales } from '@specBuilder/scale/scaleSpecBuilder.test'; import { defaultSignals } from '@specBuilder/specTestUtils'; import { LineSpecProps, MetricRangeElement, MetricRangeProps } from 'types'; import { Data, Spec } from 'vega'; @@ -51,6 +57,7 @@ const defaultLineProps: LineSpecProps = { colorScheme: DEFAULT_COLOR_SCHEME, interactiveMarkName: undefined, popoverMarkName: undefined, + animations: false, }; const getMetricRangeElement = (props?: Partial): MetricRangeElement => @@ -83,6 +90,15 @@ const defaultSpec = initializeSpec({ name: FILTERED_TABLE, source: TABLE, }, + { + name: PREVIOUS_TABLE, + transform: [{ type: 'identifier', as: MARK_ID }], + values: [], + }, + { + name: FILTERED_PREVIOUS_TABLE, + source: PREVIOUS_TABLE, + }, ], marks: [ { @@ -340,10 +356,84 @@ const displayPointMarks = [ }, ]; +const displayPointWithAnimationMarks = [ + { + name: 'line0_group', + type: 'group', + from: { + facet: { + name: 'line0_facet', + data: FILTERED_TABLE, + groupby: ['series'], + }, + }, + marks: [ + { + name: 'line0', + type: 'line', + from: { + data: 'line0_facet', + }, + interactive: false, + encode: { + enter: { + y: { scale: 'yLinear', field: 'value' }, + stroke: { scale: COLOR_SCALE, field: 'series' }, + strokeDash: { value: [] }, + strokeOpacity: DEFAULT_OPACITY_RULE, + strokeWidth: undefined, + }, + update: { + x: { scale: 'xTime', field: DEFAULT_TRANSFORMED_TIME_DIMENSION }, + y: { + scale: 'yLinear', + signal: `datum.value * ${ANIMATION_FUNCTION}`, + }, + opacity: [DEFAULT_OPACITY_RULE], + }, + }, + }, + ], + }, + { + name: 'line0_staticPoints', + type: 'symbol', + from: { + data: 'line0_staticPointData', + }, + interactive: false, + encode: { + enter: { + y: { + scale: 'yLinear', + field: 'value', + }, + fill: { + scale: COLOR_SCALE, + field: 'series', + }, + stroke: { + signal: BACKGROUND_COLOR, + }, + }, + update: { + x: { + scale: 'xTime', + field: DEFAULT_TRANSFORMED_TIME_DIMENSION, + }, + y: { + scale: 'yLinear', + signal: `datum.value * ${ANIMATION_FUNCTION}`, + }, + }, + }, + }, +]; + describe('lineSpecBuilder', () => { describe('addLine()', () => { test('should add line', () => { - expect(addLine(startingSpec, { color: DEFAULT_COLOR })).toStrictEqual(defaultSpec); + expect(addLine(startingSpec, { color: DEFAULT_COLOR, animations: false })).toStrictEqual(defaultSpec); }); }); @@ -364,11 +454,15 @@ describe('lineSpecBuilder', () => { }); test('should add trendline transform', () => { + addData(baseData, { + ...defaultLineProps, + children: [createElement(Trendline, { method: 'average' })], + }); expect( addData(baseData, { ...defaultLineProps, children: [createElement(Trendline, { method: 'average' })], - })[2].transform + })[4].transform ).toStrictEqual([ { as: [TRENDLINE_VALUE, `${DEFAULT_TIME_DIMENSION}Min`, `${DEFAULT_TIME_DIMENSION}Max`], @@ -378,6 +472,10 @@ describe('lineSpecBuilder', () => { type: 'aggregate', }, { as: SERIES_ID, expr: `datum.${DEFAULT_COLOR}`, type: 'formula' }, + { + type: 'identifier', + as: MARK_ID, + }, ]); }); @@ -408,6 +506,22 @@ describe('lineSpecBuilder', () => { expect(setScales(startingSpec.scales ?? [], defaultLineProps)).toStrictEqual(defaultSpec.scales); }); + test('linear trenline with hover and animations', () => { + expect( + setScales(startingSpec.scales ?? [], { + ...defaultLineProps, + scaleType: 'linear', + children: [createElement(Trendline, { displayOnHover: true })], + animations: true, + }) + ).toStrictEqual([ + defaultSpec.scales?.[0], + ...defaultAnimationScales, + defaultLinearScale, + defaultSpec.scales?.[2], + ]); + }); + test('linear', () => { expect( setScales(startingSpec.scales ?? [], { @@ -426,6 +540,22 @@ describe('lineSpecBuilder', () => { ).toStrictEqual([defaultSpec.scales?.[0], defaultPointScale, defaultSpec.scales?.[2]]); }); + test('point with animations and tooltip', () => { + expect( + setScales(startingSpec.scales ?? [], { + ...defaultLineProps, + scaleType: 'point', + animations: true, + children: [createElement(ChartTooltip)], + }) + ).toStrictEqual([ + defaultSpec.scales?.[0], + ...defaultAnimationScales, + defaultPointScale, + defaultSpec.scales?.[2], + ]); + }); + test('with metric range fields', () => { const [metricStart, metricEnd] = ['metricStart', 'metricEnd']; const metricRangeMetricScale = { @@ -442,6 +572,36 @@ describe('lineSpecBuilder', () => { }) ).toStrictEqual([defaultSpec.scales?.[0], defaultSpec.scales?.[1], metricRangeMetricScale]); }); + + test('with metric range fields and animations', () => { + const [metricStart, metricEnd] = ['metricStart', 'metricEnd']; + const metricRangeMetricScale = { + ...defaultSpec.scales?.[2], + domain: { + ...defaultSpec.scales?.[2].domain, + fields: ['value', metricStart, metricEnd], + }, + }; + expect( + setScales(startingSpec.scales ?? [], { + ...defaultLineProps, + children: [ + createElement(MetricRange, { + scaleAxisToFit: true, + metricEnd, + metricStart, + displayOnHover: true, + }), + ], + animations: true, + }) + ).toStrictEqual([ + defaultSpec.scales?.[0], + ...defaultAnimationScales, + defaultSpec.scales?.[1], + metricRangeMetricScale, + ]); + }); }); describe('addLineMarks()', () => { @@ -492,6 +652,17 @@ describe('lineSpecBuilder', () => { ); }); + test('with displayPointMark with animations', () => { + expect( + addLineMarks([], { + ...defaultLineProps, + animateFromZero: true, + animations: true, + staticPoint: 'staticPoint', + }) + ).toStrictEqual(displayPointWithAnimationMarks); + }); + test('with displayPointMark and metric range', () => { expect( addLineMarks([], { @@ -542,6 +713,29 @@ describe('lineSpecBuilder', () => { expect(signals[1].on).toHaveLength(2); }); + test('hover signals with metric range with animations', () => { + const signals = addSignals(defaultSignals, { + ...defaultLineProps, + animations: true, + children: [getMetricRangeElement({ displayOnHover: true })], + }); + expect(signals).toHaveLength(9); + expect(signals[0]).toHaveProperty('name', HIGHLIGHTED_ITEM); + expect(signals[0].on).toHaveLength(2); + expect(signals[1]).toHaveProperty('name', HIGHLIGHTED_SERIES); + expect(signals[1].on).toHaveLength(2); + expect(signals[4]).toHaveProperty('name', RSC_ANIMATION); + expect(signals[4].on).toHaveLength(1); + expect(signals[5]).toHaveProperty('name', 'rscColorAnimationDirection'); + expect(signals[5].on).toHaveLength(2); + expect(signals[6]).toHaveProperty('name', 'rscColorAnimation'); + expect(signals[6].on).toHaveLength(1); + expect(signals[7]).toHaveProperty('name', `${HIGHLIGHTED_ITEM}_prev`); + expect(signals[7].on).toHaveLength(1); + expect(signals[8]).toHaveProperty('name', `${HIGHLIGHTED_SERIES}_prev`); + expect(signals[8].on).toHaveLength(1); + }); + test('adds hover signals when displayPointMark is not undefined', () => { expect(addSignals([], { ...defaultLineProps, staticPoint: 'staticPoint' })).toStrictEqual([]); }); @@ -558,5 +752,29 @@ describe('lineSpecBuilder', () => { expect(signals[1]).toHaveProperty('name', HIGHLIGHTED_SERIES); expect(signals[1].on).toHaveLength(2); }); + + test('adds hover signals with metric range when displayPointMark with animations', () => { + const signals = addSignals(defaultSignals, { + ...defaultLineProps, + staticPoint: 'staticPoint', + animations: true, + children: [getMetricRangeElement({ displayOnHover: true })], + }); + expect(signals).toHaveLength(9); + expect(signals[0]).toHaveProperty('name', HIGHLIGHTED_ITEM); + expect(signals[0].on).toHaveLength(2); + expect(signals[1]).toHaveProperty('name', HIGHLIGHTED_SERIES); + expect(signals[1].on).toHaveLength(2); + expect(signals[4]).toHaveProperty('name', RSC_ANIMATION); + expect(signals[4].on).toHaveLength(1); + expect(signals[5]).toHaveProperty('name', 'rscColorAnimationDirection'); + expect(signals[5].on).toHaveLength(2); + expect(signals[6]).toHaveProperty('name', 'rscColorAnimation'); + expect(signals[6].on).toHaveLength(1); + expect(signals[7]).toHaveProperty('name', `${HIGHLIGHTED_ITEM}_prev`); + expect(signals[7].on).toHaveLength(1); + expect(signals[8]).toHaveProperty('name', `${HIGHLIGHTED_SERIES}_prev`); + expect(signals[8].on).toHaveLength(1); + }); }); }); diff --git a/src/specBuilder/line/lineSpecBuilder.ts b/src/specBuilder/line/lineSpecBuilder.ts index aafa4df91..43bcf5678 100644 --- a/src/specBuilder/line/lineSpecBuilder.ts +++ b/src/specBuilder/line/lineSpecBuilder.ts @@ -23,24 +23,51 @@ import { getMetricRangeData, getMetricRangeGroupMarks, getMetricRangeSignals, - getMetricRanges, + getMetricRanges } from '@specBuilder/metricRange/metricRangeUtils'; import { getFacetsFromProps } from '@specBuilder/specUtils'; -import { addTrendlineData, getTrendlineMarks, getTrendlineScales, setTrendlineSignals } from '@specBuilder/trendline'; +import { + addTrendlineData, + checkTrendlineAnimationScales, + getTrendlineMarks, + getTrendlineScales, + setTrendlineSignals, +} from '@specBuilder/trendline'; import { sanitizeMarkChildren, toCamelCase } from '@utils'; import { produce } from 'immer'; -import { ColorScheme, LineProps, LineSpecProps, MarkChildElement } from 'types'; +import { ChartData, ColorScheme, LineProps, LineSpecProps, MarkChildElement } from 'types'; import { Data, Mark, Scale, Signal, Spec } from 'vega'; import { addTimeTransform, getTableData } from '../data/dataUtils'; -import { addContinuousDimensionScale, addFieldToFacetScaleDomain, addMetricScale } from '../scale/scaleSpecBuilder'; -import { addHighlightedItemSignalEvents, addHighlightedSeriesSignalEvents } from '../signal/signalSpecBuilder'; +import { + addContinuousDimensionScale, + addFieldToFacetScaleDomain, + addMetricScale, + addRscAnimationScales, +} from '../scale/scaleSpecBuilder'; +import { + addHighlightedItemSignalEvents, + addHighlightedSeriesSignalEvents, + getRscAnimationSignals, +} from '../signal/signalSpecBuilder'; import { getLineHighlightedData, getLineStaticPointData } from './lineDataUtils'; import { getLineHoverMarks, getLineMark } from './lineMarkUtils'; import { getLineStaticPoint } from './linePointUtils'; import { getInteractiveMarkName, getPopoverMarkName } from './lineUtils'; -export const addLine = produce( +export const addLine = produce< + Spec, + [ + LineProps & { + animateFromZero?: boolean; + animations?: boolean; + colorScheme?: ColorScheme; + data?: ChartData[]; + index?: number; + previousData?: ChartData[]; + } + ] +>( ( spec, { @@ -73,7 +100,7 @@ export const addLine = produce((data, props) => { }); export const addSignals = produce((signals, props) => { - const { children, name } = props; + const { children, name, animations, animateFromZero } = props; setTrendlineSignals(signals, props); signals.push(...getMetricRangeSignals(props)); if (!hasInteractiveChildren(children)) return; - addHighlightedItemSignalEvents(signals, `${name}_voronoi`, 2); + // if animations are enabled, push all necessary animation signals. Line charts have voronoi points and have nested datum + if (animations) { + signals.push(...getRscAnimationSignals(name, true)); + } + + addHighlightedItemSignalEvents({ + signals, + markName: `${name}_voronoi`, + datumOrder: 2, + animations, + animateFromZero, + }); addHighlightedSeriesSignalEvents(signals, `${name}_voronoi`, 2); }); export const setScales = produce((scales, props) => { - const { metric, dimension, color, lineType, opacity, padding, scaleType, children, name } = props; + const { metric, dimension, color, lineType, opacity, padding, scaleType, children, name, animations } = props; + // if animations are enabled, add all necessary animation scales. + if (animations && hasInteractiveChildren(children)) { + addRscAnimationScales(scales); + } // add dimension scale addContinuousDimensionScale(scales, { scaleType, dimension, padding }); // add color to the color domain @@ -121,6 +163,9 @@ export const setScales = produce((scales, props) => { addFieldToFacetScaleDomain(scales, OPACITY_SCALE, opacity); // find the linear scale and add our fields to it addMetricScale(scales, getMetricKeys(metric, children, name)); + // check to see if trend lines have interactive children and if animation scales are already added. + checkTrendlineAnimationScales(name, scales, props); + // add trendline scales scales.push(...getTrendlineScales(props)); return scales; @@ -139,10 +184,10 @@ export const addLineMarks = produce((marks, props) => { facet: { name: `${name}_facet`, data: FILTERED_TABLE, - groupby: facets, - }, + groupby: facets + } }, - marks: [getLineMark(props, `${name}_facet`)], + marks: [getLineMark(props, `${name}_facet`)] }); if (staticPoint) marks.push(getLineStaticPoint(props)); marks.push(...getMetricRangeGroupMarks(props)); diff --git a/src/specBuilder/line/lineTestUtils.ts b/src/specBuilder/line/lineTestUtils.ts index f636d3893..e59f5ab8c 100644 --- a/src/specBuilder/line/lineTestUtils.ts +++ b/src/specBuilder/line/lineTestUtils.ts @@ -14,6 +14,7 @@ import { DEFAULT_COLOR, DEFAULT_COLOR_SCHEME, DEFAULT_METRIC, DEFAULT_TIME_DIMEN import { LineMarkProps } from './lineUtils'; export const defaultLineMarkProps: LineMarkProps = { + animations: false, children: [], color: DEFAULT_COLOR, colorScheme: DEFAULT_COLOR_SCHEME, diff --git a/src/specBuilder/line/lineUtils.ts b/src/specBuilder/line/lineUtils.ts index a33ff6a00..e4bbf1a79 100644 --- a/src/specBuilder/line/lineUtils.ts +++ b/src/specBuilder/line/lineUtils.ts @@ -13,13 +13,14 @@ import { Trendline } from '@components/Trendline'; import { hasInteractiveChildren, hasPopover } from '@specBuilder/marks/markUtils'; import { sanitizeMarkChildren } from '@utils'; import { + ChartData, ColorFacet, ColorScheme, LineTypeFacet, LineWidthFacet, MarkChildElement, OpacityFacet, - ScaleType, + ScaleType } from 'types'; export const getInteractiveMarkName = (children: MarkChildElement[], name: string): string | undefined => { @@ -59,9 +60,12 @@ export const getPopoverMarkName = (children: MarkChildElement[], name: string): }; export interface LineMarkProps { + animateFromZero?: boolean, + animations?: boolean; children: MarkChildElement[]; color: ColorFacet; colorScheme: ColorScheme; + data?: ChartData[], dimension: string; displayOnHover?: boolean; interactiveMarkName?: string; // optional name of the mark that is used for hover and click interactions @@ -71,6 +75,7 @@ export interface LineMarkProps { name: string; opacity: OpacityFacet; popoverMarkName?: string; + previousData?: ChartData[], scaleType: ScaleType; staticPoint?: string; } diff --git a/src/specBuilder/marks/markUtils.test.ts b/src/specBuilder/marks/markUtils.test.ts index 7dda6b545..6d9afe8f6 100644 --- a/src/specBuilder/marks/markUtils.test.ts +++ b/src/specBuilder/marks/markUtils.test.ts @@ -19,23 +19,34 @@ import { COLOR_SCALE, DEFAULT_COLOR, DEFAULT_COLOR_SCHEME, + DEFAULT_OPACITY_RULE, DEFAULT_SECONDARY_COLOR, DEFAULT_TIME_DIMENSION, DEFAULT_TRANSFORMED_TIME_DIMENSION, + HIGHLIGHTED_ITEM, + HIGHLIGHTED_SERIES, HIGHLIGHT_CONTRAST_RATIO, LINEAR_COLOR_SCALE, LINE_TYPE_SCALE, LINE_WIDTH_SCALE, + MARK_ID, OPACITY_SCALE, + SERIES_ID, SYMBOL_SIZE_SCALE, } from '@constants'; import { getColorProductionRule, getColorProductionRuleSignalString, + getHighlightOpacityAnimationValue, getHighlightOpacityValue, + getLegendMarkOpacityRules, + getLegendSeriesOpacityRules, getLineWidthProductionRule, + getMarkHighlightOpacityRules, + getMarkWithLegendHighlightOpacityRules, getOpacityProductionRule, + getSeriesAnimationOpacityRules, getStrokeDashProductionRule, getSymbolSizeProductionRule, getTooltip, @@ -155,15 +166,15 @@ describe('hasMetricRange()', () => { describe('getTooltip()', () => { test('should return undefined if there are not any interactive children', () => { - expect(getTooltip([createElement(Annotation)], 'line0')).toBeUndefined(); - expect(getTooltip([], 'line0')).toBeUndefined(); + expect(getTooltip({ children: [createElement(Annotation)], name: 'line0' })).toBeUndefined(); + expect(getTooltip({ children: [], name: 'line0' })).toBeUndefined(); }); test('should return signal ref if there are interactive children', () => { - const rule = getTooltip([createElement(ChartTooltip)], 'line0'); + const rule = getTooltip({ children: [createElement(ChartTooltip)], name: 'line0' }); expect(rule).toHaveProperty('signal'); }); test('should reference a nested datum if nestedDatum is true', () => { - const rule = getTooltip([createElement(ChartTooltip)], 'line0', true); + const rule = getTooltip({ children: [createElement(ChartTooltip)], name: 'line0', nestedDatum: true }); expect(rule?.signal).toContain('datum.datum'); }); }); @@ -216,3 +227,121 @@ describe('getColorProductionRuleSignalString()', () => { expect(getColorProductionRuleSignalString({ value: color }, DEFAULT_COLOR_SCHEME)).toStrictEqual(`'${color}'`); }); }); + +describe('getHighlightOpacityAnimationValue()', () => { + test('should receive signal highlight opacity animation value', () => { + expect(getHighlightOpacityAnimationValue({signal: "test"})).toStrictEqual({ signal: `max(1-rscColorAnimation, test / ${HIGHLIGHT_CONTRAST_RATIO})` }) + }) + + test('should receive value hightlight opacity animation value', () => { + expect(getHighlightOpacityAnimationValue({value: 1})).toStrictEqual({ signal: `max(1-rscColorAnimation, 1 / ${HIGHLIGHT_CONTRAST_RATIO})` }) + }) +}) + +describe(' getSeriesAnimationOpacityRules()', () => { + test('should receive signal series opacity animation rules', () => { + expect( getSeriesAnimationOpacityRules({signal: "test"})).toStrictEqual([ + { + test: `${HIGHLIGHTED_SERIES} && ${HIGHLIGHTED_SERIES} !== datum.${SERIES_ID}`, + signal: `max(1-rscColorAnimation, test / ${HIGHLIGHT_CONTRAST_RATIO})` + }, + { + test: `!${HIGHLIGHTED_SERIES} && ${HIGHLIGHTED_SERIES}_prev !== datum.${SERIES_ID}`, + signal: `max(1-rscColorAnimation, test / ${HIGHLIGHT_CONTRAST_RATIO})` + }, + DEFAULT_OPACITY_RULE + ]) + }) + + test('should receive default series opacity animation rules', () => { + expect( getSeriesAnimationOpacityRules()).toStrictEqual([ + { + test: `${HIGHLIGHTED_SERIES} && ${HIGHLIGHTED_SERIES} !== datum.${SERIES_ID}`, + signal: `max(1-rscColorAnimation, 1 / ${HIGHLIGHT_CONTRAST_RATIO})` + }, + { + test: `!${HIGHLIGHTED_SERIES} && ${HIGHLIGHTED_SERIES}_prev !== datum.${SERIES_ID}`, + signal: `max(1-rscColorAnimation, 1 / ${HIGHLIGHT_CONTRAST_RATIO})` + }, + DEFAULT_OPACITY_RULE + ]) + }) +}) + +describe(' getMarkHighlightOpacityRules()', () => { + test('should receive default mark highlight opacity rules', () => { + expect( getMarkHighlightOpacityRules()).toStrictEqual([ + { + test: `${HIGHLIGHTED_ITEM} && ${HIGHLIGHTED_ITEM} !== datum.${MARK_ID}`, + signal: `max(1-rscColorAnimation, 1 / ${HIGHLIGHT_CONTRAST_RATIO})` + }, + { + test: `!${HIGHLIGHTED_ITEM} && ${HIGHLIGHTED_ITEM}_prev !== datum.${MARK_ID}`, + signal: `max(1-rscColorAnimation, 1 / ${HIGHLIGHT_CONTRAST_RATIO})` + }, + ]) + }) + +}) + +describe('getMarkWithLegendHighlightOpacityRules()', () => { + test('should receive default mark and highlight legend opacity animation rules', () => { + expect(getMarkWithLegendHighlightOpacityRules()).toStrictEqual([ + { + // If there is no current selection, but there is a hover and the hover is NOT for the current bar + test: `${HIGHLIGHTED_ITEM} && ${HIGHLIGHTED_ITEM} !== datum.${MARK_ID}`, + signal: `max(1-rscColorAnimation, 1 / ${HIGHLIGHT_CONTRAST_RATIO})` + }, + { + // If there is a highlighted series and the highlighted series is NOT the series of the current bar + test: `${HIGHLIGHTED_SERIES} && ${HIGHLIGHTED_SERIES} !== datum.${SERIES_ID}`, + signal: `max(1-rscColorAnimation, 1 / ${HIGHLIGHT_CONTRAST_RATIO})` + }, + { + // If there is no highlighted series and the previously highlighted series is the series of the current bar + test: `!${HIGHLIGHTED_SERIES} && ${HIGHLIGHTED_SERIES}_prev == datum.${SERIES_ID}`, + value: 1 + }, + { + // If the previously hovered bar is NOT the current bar and the color animation direction is reversed (fading in) + test: `${HIGHLIGHTED_ITEM}_prev !== datum.${MARK_ID} && rscColorAnimationDirection === -1`, + signal: `max(1-rscColorAnimation, 1 / ${HIGHLIGHT_CONTRAST_RATIO})` + }, + { value: 1 } + ]) + }) +}) + +describe('getLegendSeriesOpacityRules()', () => { + test('should receive default series legend opacity animation rules', () => { + expect(getLegendSeriesOpacityRules()).toStrictEqual([ + { + test: `${HIGHLIGHTED_SERIES} && ${HIGHLIGHTED_SERIES} !== datum.value`, + signal: `max(1-rscColorAnimation, 1 / ${HIGHLIGHT_CONTRAST_RATIO})` + }, + { + test: `!${HIGHLIGHTED_SERIES} && ${HIGHLIGHTED_SERIES}_prev !== datum.value`, + signal: `max(1-rscColorAnimation, 1 / ${HIGHLIGHT_CONTRAST_RATIO})` + }, + DEFAULT_OPACITY_RULE + ]) + }) +}) + +describe('getLegendMarkOpacityRules()', () => { + test('should receive default mark legend opacity animation rules', () => { + expect(getLegendMarkOpacityRules()).toStrictEqual([ + { + // If there is a highlighted series, and it is NOT equal to the current series + test: `${HIGHLIGHTED_SERIES} && ${HIGHLIGHTED_SERIES} !== datum.value`, + signal: `max(1-rscColorAnimation, 1 / ${HIGHLIGHT_CONTRAST_RATIO})` + }, + { + // If there is NOT a highlighted series and NOT a previously highlighted bar, and the previously highlighted series is NOT equal to the current series + test: `!${HIGHLIGHTED_SERIES} && !${HIGHLIGHTED_ITEM}_prev && datum.value !== ${HIGHLIGHTED_SERIES}_prev`, + signal: `max(1-rscColorAnimation, 1 / ${HIGHLIGHT_CONTRAST_RATIO})` + }, + DEFAULT_OPACITY_RULE + ]) + }) +}) diff --git a/src/specBuilder/marks/markUtils.ts b/src/specBuilder/marks/markUtils.ts index e6108ec32..1e4ccaf9a 100644 --- a/src/specBuilder/marks/markUtils.ts +++ b/src/specBuilder/marks/markUtils.ts @@ -26,6 +26,10 @@ import { LINE_WIDTH_SCALE, OPACITY_SCALE, SYMBOL_SIZE_SCALE, + SERIES_ID, + HIGHLIGHTED_SERIES, + HIGHLIGHTED_ITEM, + MARK_ID } from '@constants'; import { getScaleName } from '@specBuilder/scale/scaleSpecBuilder'; import { @@ -52,6 +56,7 @@ import { Cursor, NumericValueRef, PathMark, + ProductionRule, ScaledValueRef, SignalRef, } from 'vega'; @@ -77,10 +82,26 @@ export function getInteractive(children: MarkChildElement[]): boolean { /** * If a tooltip or popover exists on the mark, then set tooltip to true. */ -export function getTooltip(children: MarkChildElement[], name: string, nestedDatum?: boolean): SignalRef | undefined { +export function getTooltip( + { + children, + name, + nestedDatum = false, + animations = false, + isBar = false + }: { + children: MarkChildElement[], + name: string, + nestedDatum?: boolean, + animations?: boolean, + isBar?: boolean +}): SignalRef | undefined { // skip annotations if (hasTooltip(children)) { - return { signal: `merge(datum${nestedDatum ? '.datum' : ''}, {'rscComponentName': '${name}'})` }; + const merge = `merge(datum${nestedDatum ? '.datum' : ''}, {'rscComponentName': '${name}'})`; + const animatedTooltipSignal = `timerValue === 1 ? ${merge} : null`; + const signal = animations && !isBar ? animatedTooltipSignal : merge; + return { signal }; } } @@ -246,9 +267,11 @@ export const getXProductionRule = (scaleType: ScaleType, dimension: string): Num * @param children * @param dataSource name of the point data source the voronoi is based on * @param markName + * @param animations whether animations are currently on for the chart + * @param animateFromZero whether an animation from zero is occurring (false if a popover was just closed) * @returns PathMark */ -export const getVoronoiPath = (children: MarkChildElement[], dataSource: string, markName: string): PathMark => ({ +export const getVoronoiPath = (children: MarkChildElement[], dataSource: string, markName: string, animations?: boolean, animateFromZero?: boolean): PathMark => ({ name: `${markName}_voronoi`, type: 'path', from: { data: dataSource }, @@ -257,10 +280,15 @@ export const getVoronoiPath = (children: MarkChildElement[], dataSource: string, fill: { value: 'transparent' }, stroke: { value: 'transparent' }, isVoronoi: { value: true }, - tooltip: getTooltip(children, markName, true), + ...(!animations && { + tooltip: getTooltip({ children, name: markName, nestedDatum: true }) + }), }, update: { cursor: getCursor(children), + ...(animations && animateFromZero && { + tooltip: getTooltip({ children, name: markName, nestedDatum: true, animations }) + }), }, }, transform: [ @@ -273,3 +301,120 @@ export const getVoronoiPath = (children: MarkChildElement[], dataSource: string, }, ], }); +/** + * the signal that triggers the opacity ease in and out + * @param opacityValue + * @returns { signal: string} + */ +export const getHighlightOpacityAnimationValue = (opacityValue: { signal: string } | { value: number }): { signal: string } => { + const opacity = 'signal' in opacityValue ? opacityValue.signal : opacityValue.value + return { signal: `max(1-rscColorAnimation, ${opacity} / ${HIGHLIGHT_CONTRAST_RATIO})` } +}; +/** + * animation opacity rules for charts that highlight from series ID + * @param opacityValue + * @returns ProductionRule + */ +export const getSeriesAnimationOpacityRules = ( + opacityValue?: { signal: string } | { value: number }, +): ProductionRule => { + opacityValue = opacityValue ?? DEFAULT_OPACITY_RULE; + + return [ + { + test: `${HIGHLIGHTED_SERIES} && ${HIGHLIGHTED_SERIES} !== datum.${SERIES_ID}`, + ...getHighlightOpacityAnimationValue(opacityValue) + }, + { + test: `!${HIGHLIGHTED_SERIES} && ${HIGHLIGHTED_SERIES}_prev !== datum.${SERIES_ID}`, + ...getHighlightOpacityAnimationValue(opacityValue) + }, + DEFAULT_OPACITY_RULE + ] +}; +/** + * animation opacity rules for charts that highlight from mark ID + * @returns ProductionRule + */ +export const getMarkHighlightOpacityRules = (): ProductionRule => { + return [ + { + test: `${HIGHLIGHTED_ITEM} && ${HIGHLIGHTED_ITEM} !== datum.${MARK_ID}`, + ...getHighlightOpacityAnimationValue(DEFAULT_OPACITY_RULE) + }, + { + test: `!${HIGHLIGHTED_ITEM} && ${HIGHLIGHTED_ITEM}_prev !== datum.${MARK_ID}`, + ...getHighlightOpacityAnimationValue(DEFAULT_OPACITY_RULE) + } + ] +} +/** + * Opacity animation rules for marks when the chart marks are highlighted with the mark ID and legends are present + * with highlight enabled + * @returns ProductionRule + */ +export const getMarkWithLegendHighlightOpacityRules = (): ProductionRule => { + return [ + { + // If there is no current selection, but there is a hover and the hover is NOT for the current bar + test: `${HIGHLIGHTED_ITEM} && ${HIGHLIGHTED_ITEM} !== datum.${MARK_ID}`, + ...getHighlightOpacityAnimationValue(DEFAULT_OPACITY_RULE) + }, + { + // If there is a highlighted series and the highlighted series is NOT the series of the current bar + test: `${HIGHLIGHTED_SERIES} && ${HIGHLIGHTED_SERIES} !== datum.${SERIES_ID}`, + ...getHighlightOpacityAnimationValue(DEFAULT_OPACITY_RULE) + }, + { + // If there is no highlighted series and the previously highlighted series is the series of the current bar + test: `!${HIGHLIGHTED_SERIES} && ${HIGHLIGHTED_SERIES}_prev == datum.${SERIES_ID}`, + value: 1 + }, + { + // If the previously hovered bar is NOT the current bar and the color animation direction is reversed (fading in) + test: `${HIGHLIGHTED_ITEM}_prev !== datum.${MARK_ID} && rscColorAnimationDirection === -1`, + ...getHighlightOpacityAnimationValue(DEFAULT_OPACITY_RULE) + }, + { value: 1 } + ]; +} +/** + * Opacity animation rules for the legend symbols and labels when marks are highlighted by series ID + * @returns ProductionRule + */ + +export const getLegendSeriesOpacityRules = (): ProductionRule => { + return [ + { + test: `${HIGHLIGHTED_SERIES} && ${HIGHLIGHTED_SERIES} !== datum.value`, + ...getHighlightOpacityAnimationValue(DEFAULT_OPACITY_RULE) + }, + { + test: `!${HIGHLIGHTED_SERIES} && ${HIGHLIGHTED_SERIES}_prev !== datum.value`, + ...getHighlightOpacityAnimationValue(DEFAULT_OPACITY_RULE) + }, + DEFAULT_OPACITY_RULE + ] +} +/** + * Opacity animation rules for legend symbols and labels if the chart marks are highlighted by mark ID + * @returns ProductionRule + */ + +export const getLegendMarkOpacityRules = (): ProductionRule => { + return [ + { + // If there is a highlighted series, and it is NOT equal to the current series + test: `${HIGHLIGHTED_SERIES} && ${HIGHLIGHTED_SERIES} !== datum.value`, + ...getHighlightOpacityAnimationValue(DEFAULT_OPACITY_RULE) + }, + { + // If there is NOT a highlighted series and NOT a previously highlighted bar, and the previously highlighted series is NOT equal to the current series + test: `!${HIGHLIGHTED_SERIES} && !${HIGHLIGHTED_ITEM}_prev && datum.value !== ${HIGHLIGHTED_SERIES}_prev`, + ...getHighlightOpacityAnimationValue(DEFAULT_OPACITY_RULE) + }, + DEFAULT_OPACITY_RULE + ] +}; + + diff --git a/src/specBuilder/metricRange/metricRangeUtils.test.ts b/src/specBuilder/metricRange/metricRangeUtils.test.ts index 24e758699..d3e1683e8 100644 --- a/src/specBuilder/metricRange/metricRangeUtils.test.ts +++ b/src/specBuilder/metricRange/metricRangeUtils.test.ts @@ -20,6 +20,7 @@ import { DEFAULT_OPACITY_RULE, DEFAULT_TIME_DIMENSION, DEFAULT_TRANSFORMED_TIME_DIMENSION, + ANIMATION_FUNCTION, FILTERED_TABLE, } from '@constants'; import { LineSpecProps, MetricRangeProps, MetricRangeSpecProps } from 'types'; @@ -65,6 +66,7 @@ const defaultLineProps: LineSpecProps = { colorScheme: DEFAULT_COLOR_SCHEME, interactiveMarkName: undefined, popoverMarkName: undefined, + animations: false }; const basicMetricRangeMarks = [ @@ -115,6 +117,64 @@ const basicMetricRangeMarks = [ }, ]; +const basicMetricRangeMarksWithAnimatons = [ + { + name: 'line0MetricRange0_line', + type: 'line', + from: { + data: 'line0MetricRange0_facet', + }, + interactive: false, + encode: { + enter: { + y: { scale: 'yLinear', field: 'metric' }, + stroke: { scale: COLOR_SCALE, field: 'series' }, + strokeDash: { value: [3, 4] }, + strokeOpacity: DEFAULT_OPACITY_RULE, + strokeWidth: { value: 1.5 }, + }, + update: { + x: { + scale: 'xTime', + field: DEFAULT_TRANSFORMED_TIME_DIMENSION, + }, + y: { + scale: 'yLinear', + signal: `datum.metric * ${ANIMATION_FUNCTION}`, + }, + opacity: [DEFAULT_OPACITY_RULE], + }, + }, + }, + { + name: 'line0MetricRange0_area', + type: 'area', + from: { + data: 'line0MetricRange0_facet', + }, + interactive: false, + encode: { + enter: { + tooltip: undefined, + fill: { scale: COLOR_SCALE, field: 'series' }, + }, + update: { + cursor: undefined, + x: { scale: 'xTime', field: DEFAULT_TRANSFORMED_TIME_DIMENSION }, + y: { + scale: 'yLinear', + signal: `datum.metricStart * ${ANIMATION_FUNCTION}`, + }, + y2: { + scale: 'yLinear', + signal: `datum.metricEnd * ${ANIMATION_FUNCTION}`, + }, + fillOpacity: [{ value: 0.2 }], + }, + }, + }, +]; + describe('applyMetricRangePropDefaults', () => { test('applies defaults', () => { expect( @@ -166,6 +226,12 @@ describe('getMetricRangeMark', () => { }); }); +describe('getMetricRangeMarkWithAnimations', () => { + test('creates MetricRange mark from basic input', () => { + expect(getMetricRangeMark({...defaultLineProps, animations: true, animateFromZero: true}, defaultMetricRangeSpecProps)).toEqual(basicMetricRangeMarksWithAnimatons); + }); +}); + describe('getMetricRangeGroupMarks', () => { test('creates MetricRange group mark from basic input', () => { expect(getMetricRangeGroupMarks(defaultLineProps)).toEqual([ @@ -186,6 +252,26 @@ describe('getMetricRangeGroupMarks', () => { }); }); +describe('getMetricRangeGroupMarksWithAnimatons', () => { + test('creates MetricRange group mark from basic input', () => { + expect(getMetricRangeGroupMarks({...defaultLineProps, animations: true, animateFromZero: true })).toEqual([ + { + name: 'line0MetricRange0_group', + type: 'group', + clip: true, + from: { + facet: { + name: 'line0MetricRange0_facet', + data: FILTERED_TABLE, + groupby: ['series'], + }, + }, + marks: basicMetricRangeMarksWithAnimatons, + }, + ]); + }); +}); + describe('getMetricRangeData', () => { test('creates metric range data from basic input', () => { const data = getMetricRangeData({ diff --git a/src/specBuilder/metricRange/metricRangeUtils.ts b/src/specBuilder/metricRange/metricRangeUtils.ts index 43747a50f..6a0c3c521 100644 --- a/src/specBuilder/metricRange/metricRangeUtils.ts +++ b/src/specBuilder/metricRange/metricRangeUtils.ts @@ -91,28 +91,30 @@ export const getMetricRangeMark = ( metricRangeProps: MetricRangeSpecProps ): (LineMark | AreaMark)[] => { const areaProps: AreaMarkProps = { - name: `${metricRangeProps.name}_area`, + animations: lineMarkProps.animations, + animateFromZero: lineMarkProps.animateFromZero, + children: [], color: lineMarkProps.color, colorScheme: lineMarkProps.colorScheme, - opacity: metricRangeProps.rangeOpacity, - children: [], - metricStart: metricRangeProps.metricStart, - metricEnd: metricRangeProps.metricEnd, - isStacked: false, - scaleType: 'time', dimension: lineMarkProps.dimension, + displayOnHover: metricRangeProps.displayOnHover, isMetricRange: true, + isStacked: false, + metricStart: metricRangeProps.metricStart, + metricEnd: metricRangeProps.metricEnd, + name: `${metricRangeProps.name}_area`, + opacity: metricRangeProps.rangeOpacity, parentName: lineMarkProps.name, - displayOnHover: metricRangeProps.displayOnHover, + scaleType: 'time', }; const lineProps: LineMarkProps = { ...lineMarkProps, - name: `${metricRangeProps.name}_line`, color: metricRangeProps.color ? { value: metricRangeProps.color } : lineMarkProps.color, - metric: metricRangeProps.metric, + displayOnHover: metricRangeProps.displayOnHover, lineType: { value: metricRangeProps.lineType }, lineWidth: { value: metricRangeProps.lineWidth }, - displayOnHover: metricRangeProps.displayOnHover, + metric: metricRangeProps.metric, + name: `${metricRangeProps.name}_line`, }; const dataSource = `${metricRangeProps.name}_facet`; diff --git a/src/specBuilder/scale/scaleSpecBuilder.test.ts b/src/specBuilder/scale/scaleSpecBuilder.test.ts index 00d074dac..e0e9c1f5e 100644 --- a/src/specBuilder/scale/scaleSpecBuilder.test.ts +++ b/src/specBuilder/scale/scaleSpecBuilder.test.ts @@ -16,6 +16,7 @@ import { addContinuousDimensionScale, addDomainFields, addFieldToFacetScaleDomain, + addRscAnimationScales, getPadding, getScaleName, } from './scaleSpecBuilder'; @@ -26,6 +27,23 @@ const defaultColorScale: OrdinalScale = { type: 'ordinal', }; +export const defaultAnimationScales = [ + { + name: 'rscAnimationCurve', + type: 'linear', + domain: [0, 1], + range: [0, 1], + clamp: true + }, + { + name: 'rscAnimationCurveInverse', + type: 'linear', + domain: [0, 1], + range: [0, 1], + clamp: true + } +] + describe('addDomainFields()', () => { test('no domain fields', () => { expect(addDomainFields({ name: COLOR_SCALE, type: 'ordinal' }, [DEFAULT_COLOR])).toStrictEqual({ @@ -105,3 +123,11 @@ describe('getScaleName()', () => { expect(getScaleName('y', 'band')).toBe('yBand'); }); }); + +describe('addRscAnimationScales()', () => { + test('should return correct scale name', () => { + const scales = [] + addRscAnimationScales(scales) + expect(scales).toStrictEqual(defaultAnimationScales) + }); +}); diff --git a/src/specBuilder/scale/scaleSpecBuilder.ts b/src/specBuilder/scale/scaleSpecBuilder.ts index b09c45c59..6921a4586 100644 --- a/src/specBuilder/scale/scaleSpecBuilder.ts +++ b/src/specBuilder/scale/scaleSpecBuilder.ts @@ -215,3 +215,53 @@ const isScaleMultiFieldsRef = ( ): domain is ScaleMultiFieldsRef => { return Boolean(domain && !Array.isArray(domain) && 'data' in domain && 'fields' in domain); }; + +/** + * push all Opacity animation scales + * @param scales + */ +export const addRscAnimationScales = (scales: Scale[]) => { + //if the scales are already present, no need to add them. + if (!hasScaleByName(scales, 'rscAnimationCurve')) { + scales.push(getRscAnimationCurve()); + scales.push(getRscAnimationCurveInverse()); + } +}; + +/** + * The ease-in curves for opacity animations + * @returns Scale + */ +const getRscAnimationCurve = (): Scale => { + return { + name: 'rscAnimationCurve', + type: 'linear', + domain: [0, 1], + range: [0, 1], + clamp: true, + }; +}; + +/** + * The ease-out curves for opacity animations + * @returns Scale + */ +const getRscAnimationCurveInverse = (): Scale => { + return { + name: 'rscAnimationCurveInverse', + type: 'linear', + domain: [0, 1], + range: [0, 1], + clamp: true, + }; +}; + +/** + * checks if a particular scale is in the spec. + * @param scales + * @param name + * @returns boolean + */ +export const hasScaleByName = (scales: Scale[], name: string) => { + return scales.some((scale) => scale.name == name); +}; diff --git a/src/specBuilder/scatter/scatterMarkUtils.ts b/src/specBuilder/scatter/scatterMarkUtils.ts index 3996b0b6e..20a3316c0 100644 --- a/src/specBuilder/scatter/scatterMarkUtils.ts +++ b/src/specBuilder/scatter/scatterMarkUtils.ts @@ -12,28 +12,28 @@ import { DEFAULT_OPACITY_RULE, FILTERED_TABLE, - HIGHLIGHTED_ITEM, - HIGHLIGHT_CONTRAST_RATIO, + HIGHLIGHT_CONTRAST_RATIO, HIGHLIGHTED_ITEM, MARK_ID, - SELECTED_ITEM, + SELECTED_ITEM } from '@constants'; import { getColorProductionRule, getLineWidthProductionRule, + getMarkHighlightOpacityRules, getOpacityProductionRule, getStrokeDashProductionRule, getSymbolSizeProductionRule, getVoronoiPath, getXProductionRule, hasInteractiveChildren, - hasPopover, + hasPopover } from '@specBuilder/marks/markUtils'; import { getScatterPathMarks } from '@specBuilder/scatterPath/scatterPathUtils'; import { getTrendlineMarks } from '@specBuilder/trendline'; import { spectrumColors } from '@themes'; import { produce } from 'immer'; import { ScatterSpecProps, SymbolSizeFacet } from 'types'; -import { GroupMark, Mark, NumericValueRef, PathMark, SymbolMark } from 'vega'; +import { GroupMark, Mark, NumericValueRef, PathMark, ProductionRule, SymbolMark } from 'vega'; export const addScatterMarks = produce((marks, props) => { const { name } = props; @@ -105,10 +105,15 @@ export const getScatterMark = (props: ScatterSpecProps): SymbolMark => { * @param scatterProps ScatterSpecProps * @returns opacity production rule */ -export const getOpacity = ({ children }: ScatterSpecProps): ({ test?: string } & NumericValueRef)[] => { +export const getOpacity = ({ animations, children }: ScatterSpecProps): ProductionRule => { if (!hasInteractiveChildren(children)) { return [DEFAULT_OPACITY_RULE]; } + // if animations are enabled, set opacity animation rules for scatter mark + + if (animations) { + return getMarkHighlightOpacityRules(); + } // if a point is hovered or selected, all other points should be reduced opacity const fadedValue = 1 / HIGHLIGHT_CONTRAST_RATIO; diff --git a/src/specBuilder/scatter/scatterSpecBuilder.test.ts b/src/specBuilder/scatter/scatterSpecBuilder.test.ts index 979bd964f..b19cee51a 100644 --- a/src/specBuilder/scatter/scatterSpecBuilder.test.ts +++ b/src/specBuilder/scatter/scatterSpecBuilder.test.ts @@ -33,7 +33,7 @@ import { defaultScatterProps } from './scatterTestUtils'; describe('addData()', () => { test('should add time transform is dimensionScaleType === "time"', () => { const data = addData(initializeSpec().data ?? [], { ...defaultScatterProps, dimensionScaleType: 'time' }); - expect(data).toHaveLength(2); + expect(data).toHaveLength(4); expect(data[0].transform).toHaveLength(2); expect(data[0].transform?.[1].type).toBe('timeunit'); }); @@ -42,17 +42,18 @@ describe('addData()', () => { ...defaultScatterProps, children: [createElement(ChartPopover)], }); - expect(data).toHaveLength(3); - expect(data[2].name).toBe('scatter0_selectedData'); + expect(data).toHaveLength(5); + expect(data[4].name).toBe('scatter0_selectedData'); }); test('should add trendline data if trendline exists as a child', () => { const data = addData(initializeSpec().data ?? [], { ...defaultScatterProps, + animations: false, children: [createElement(Trendline)], }); - expect(data).toHaveLength(3); - expect(data[2].transform).toHaveLength(2); - expect(data[2].transform?.[0].type).toBe('regression'); + expect(data).toHaveLength(5); + expect(data[4].transform).toHaveLength(3); + expect(data[4].transform?.[0].type).toBe('regression'); }); }); diff --git a/src/specBuilder/scatter/scatterSpecBuilder.ts b/src/specBuilder/scatter/scatterSpecBuilder.ts index 929aab829..e60411511 100644 --- a/src/specBuilder/scatter/scatterSpecBuilder.ts +++ b/src/specBuilder/scatter/scatterSpecBuilder.ts @@ -31,9 +31,10 @@ import { addContinuousDimensionScale, addFieldToFacetScaleDomain, addMetricScale, + addRscAnimationScales, } from '@specBuilder/scale/scaleSpecBuilder'; import { setScatterPathScales } from '@specBuilder/scatterPath'; -import { addHighlightedItemSignalEvents } from '@specBuilder/signal/signalSpecBuilder'; +import { addHighlightedItemSignalEvents, getRscAnimationSignals } from '@specBuilder/signal/signalSpecBuilder'; import { addTrendlineData, getTrendlineScales, setTrendlineSignals } from '@specBuilder/trendline'; import { sanitizeMarkChildren, toCamelCase } from '@utils'; import { produce } from 'immer'; @@ -51,6 +52,7 @@ export const addScatter = produce((data, props) => { * @param scatterProps ScatterSpecProps */ export const addSignals = produce((signals, props) => { - const { children, name } = props; + const { animations, children, name } = props; + // if animations are enabled, push opacity animation signals to spec + if (animations) { + signals.push(...getRscAnimationSignals(name, true, true)); + } // trendline signals setTrendlineSignals(signals, props); if (!hasInteractiveChildren(children)) return; // interactive signals - addHighlightedItemSignalEvents(signals, `${name}_voronoi`, 2); + addHighlightedItemSignalEvents({ signals, markName: `${name}_voronoi`, datumOrder: 2 }); }); /** @@ -137,7 +144,22 @@ export const addSignals = produce((signals, props) * @param scatterProps ScatterSpecProps */ export const setScales = produce((scales, props) => { - const { color, colorScaleType, dimension, dimensionScaleType, lineType, lineWidth, metric, opacity, size } = props; + const { + animations, + color, + colorScaleType, + dimension, + dimensionScaleType, + lineType, + lineWidth, + metric, + opacity, + size, + } = props; + // if animations are enabled, add Opacity animation scales to spec + if (animations) { + addRscAnimationScales(scales); + } // add dimension scale addContinuousDimensionScale(scales, { scaleType: dimensionScaleType, dimension }); // add metric scale diff --git a/src/specBuilder/scatter/scatterTestUtils.ts b/src/specBuilder/scatter/scatterTestUtils.ts index db259cba2..edb3b09bb 100644 --- a/src/specBuilder/scatter/scatterTestUtils.ts +++ b/src/specBuilder/scatter/scatterTestUtils.ts @@ -18,6 +18,7 @@ import { import { ScatterSpecProps } from 'types'; export const defaultScatterProps: ScatterSpecProps = { + animations: false, children: [], color: { value: 'categorical-100' }, colorScaleType: 'ordinal', diff --git a/src/specBuilder/signal/signalSpecBuilder.test.ts b/src/specBuilder/signal/signalSpecBuilder.test.ts index 8cdc136d3..7cfb93d3c 100644 --- a/src/specBuilder/signal/signalSpecBuilder.test.ts +++ b/src/specBuilder/signal/signalSpecBuilder.test.ts @@ -9,7 +9,7 @@ * OF ANY KIND, either express or implied. See the License for the specific language * governing permissions and limitations under the License. */ -import { HIGHLIGHTED_ITEM, HIGHLIGHTED_SERIES } from '@constants'; +import { HIGHLIGHTED_ITEM, HIGHLIGHTED_SERIES, RSC_ANIMATION } from '@constants'; import { defaultHighlightedItemSignal, defaultHighlightedSeriesSignal, @@ -17,7 +17,7 @@ import { } from '@specBuilder/specTestUtils'; import { Signal } from 'vega'; -import { addHighlightedItemSignalEvents, addHighlightedSeriesSignalEvents } from './signalSpecBuilder'; +import { addHighlightedItemSignalEvents, addHighlightedSeriesSignalEvents, getRscAnimationSignals } from './signalSpecBuilder'; describe('signalSpecBuilder', () => { let signals: Signal[]; @@ -26,7 +26,7 @@ describe('signalSpecBuilder', () => { }); describe('addHighlightedItemSignalEvents()', () => { test('should add on events', () => { - addHighlightedItemSignalEvents(signals, 'line0'); + addHighlightedItemSignalEvents({ signals, markName: 'line0' }); expect(signals).toHaveLength(4); expect(signals[0]).toHaveProperty('name', HIGHLIGHTED_ITEM); expect(signals[0].on).toHaveLength(2); @@ -39,7 +39,7 @@ describe('signalSpecBuilder', () => { test('should not do anything if the highlight signal is not found', () => { const signals = JSON.parse(JSON.stringify([defaultHighlightedSeriesSignal])); const signalsCopy = JSON.parse(JSON.stringify(signals)); - addHighlightedItemSignalEvents(signals, 'line0'); + addHighlightedItemSignalEvents({ signals, markName: 'line0' }); expect(signals).toEqual(signalsCopy); }); }); @@ -63,4 +63,27 @@ describe('signalSpecBuilder', () => { expect(signals).toEqual(signalsCopy); }); }); + + describe('getRscAnimationSignals()', () => { + test('should add on events', () => { + const signals = getRscAnimationSignals('line0'); + expect(signals).toHaveLength(5); + expect(signals[0]).toHaveProperty('name', RSC_ANIMATION); + expect(signals[0].on).toHaveLength(1); + expect(signals[1]).toHaveProperty('name', 'rscColorAnimationDirection'); + expect(signals[1].on).toHaveLength(2); + expect(signals[2]).toHaveProperty('name', 'rscColorAnimation'); + expect(signals[2].on).toHaveLength(1); + expect(signals[3]).toHaveProperty('name', `${HIGHLIGHTED_ITEM}_prev`); + expect(signals[3].on).toHaveLength(1); + expect(signals[4]).toHaveProperty('name', `${HIGHLIGHTED_SERIES}_prev`); + expect(signals[4].on).toHaveLength(1); + }); + test('should not do anything if the highlight signal is not found', () => { + const signals = JSON.parse(JSON.stringify([defaultHighlightedItemSignal])); + const signalsCopy = JSON.parse(JSON.stringify(signals)); + addHighlightedSeriesSignalEvents(signals, 'line0'); + expect(signals).toEqual(signalsCopy); + }); + }); }); diff --git a/src/specBuilder/signal/signalSpecBuilder.ts b/src/specBuilder/signal/signalSpecBuilder.ts index 9834cbf59..6b42f852d 100644 --- a/src/specBuilder/signal/signalSpecBuilder.ts +++ b/src/specBuilder/signal/signalSpecBuilder.ts @@ -9,7 +9,14 @@ * OF ANY KIND, either express or implied. See the License for the specific language * governing permissions and limitations under the License. */ -import { HIGHLIGHTED_ITEM, HIGHLIGHTED_SERIES, MARK_ID, SERIES_ID } from '@constants'; +import { + DATA_ANIMATION_MILLISECONDS_PER_FRAME, + HIGHLIGHTED_ITEM, + HIGHLIGHTED_SERIES, + MARK_ID, OPACITY_ANIMATION_FRAMES, + RSC_ANIMATION, + SERIES_ID +} from '@constants'; import { Signal } from 'vega'; /** @@ -34,26 +41,30 @@ export const getControlledHoverSignal = (name: string): Signal => { /** * Returns the highlighted series signal */ -export const addHighlighSignalLegendHoverEvents = ( +export const addHighlightSignalLegendHoverEvents = ( signals: Signal[], legendName: string, - includeHiddenSeries: boolean + includeHiddenSeries: boolean, ) => { - const highlightedItemSignal = signals.find((signal) => signal.name === HIGHLIGHTED_SERIES); - if (highlightedItemSignal) { - if (highlightedItemSignal.on === undefined) { - highlightedItemSignal.on = []; + const highlightedSeriesSignal = signals.find((signal) => signal.name === HIGHLIGHTED_SERIES); + if (highlightedSeriesSignal) { + if (highlightedSeriesSignal.on === undefined) { + highlightedSeriesSignal.on = []; } + const highlightedSeriesSignalPrev = signals.find((signal) => signal.name === `${HIGHLIGHTED_SERIES}_prev`); const hoveredSeries = `domain("${legendName}Entries")[datum.index]`; const update = includeHiddenSeries ? `indexof(hiddenSeries, ${hoveredSeries}) === -1 ? ${hoveredSeries} : null` : hoveredSeries; - highlightedItemSignal.on.push( + highlightedSeriesSignal.on.push( ...[ { events: `@${legendName}_legendEntry:mouseover`, update }, { events: `@${legendName}_legendEntry:mouseout`, update: 'null' }, ] ); + if (highlightedSeriesSignalPrev) { + highlightedSeriesSignalPrev.on?.push({ events: `@${legendName}_legendEntry:mouseover`, update }); + } } }; @@ -79,9 +90,27 @@ export const getGenericSignal = (name: string, value: unknown = null): Signal => * @param signals * @param markName * @param datumOrder how deep the datum is nested (i.e. 1 becomes datum.rscMarkId, 2 becomes datum.datum.rscMarkId, etc.) + * @param animations + * @param animateFromZero + * @param needsDisable */ -export const addHighlightedItemSignalEvents = (signals: Signal[], markName: string, datumOrder = 1) => { +export const addHighlightedItemSignalEvents = ({ + signals, + markName, + datumOrder = 1, + animations = false, + animateFromZero = false, + isEnabled = true, +}: { + signals: Signal[]; + markName: string; + datumOrder?: number; + animations?: boolean; + animateFromZero?: boolean; + isEnabled?: boolean; +}) => { const highlightedItemSignal = signals.find((signal) => signal.name === HIGHLIGHTED_ITEM); + const highlightedUpdate = `${new Array(datumOrder).fill('datum.').join('')}${MARK_ID}`; if (highlightedItemSignal) { if (highlightedItemSignal.on === undefined) { highlightedItemSignal.on = []; @@ -90,7 +119,9 @@ export const addHighlightedItemSignalEvents = (signals: Signal[], markName: stri ...[ { events: `@${markName}:mouseover`, - update: `${new Array(datumOrder).fill('datum.').join('')}${MARK_ID}`, + update: + animations && animateFromZero && isEnabled ? `timerValue === 1 ? ${highlightedUpdate} : null` + : highlightedUpdate, }, { events: `@${markName}:mouseout`, update: 'null' }, ] @@ -121,3 +152,146 @@ export const addHighlightedSeriesSignalEvents = (signals: Signal[], markName: st ); } }; +/** + * get all signals required for opacity animations + * @param name + * @param nestedDatum + * @param isNull + * @returns Signal[] + */ +export const getRscAnimationSignals = (name: string, nestedDatum?: boolean, isNull?: boolean): Signal[] => { + return [ + getRscAnimation(), + getRscColorAnimationDirection(name), + getRscColorAnimation(), + getRscHighlightedItemPrevSignal(name, nestedDatum), + getRscHighlightedSeriesPrevSignal(name, nestedDatum, isNull), + ]; +}; +/** + * gets the animations direction signal events for legends + * @param name + */ + +export const getRscLegendColorAnimationDirection = (name: string): { update: string; events: string }[] => { + return [ + { events: `@${name}_legendEntry:mouseover`, update: '1' }, + { events: `@${name}_legendEntry:mouseout`, update: '-1' }, + ]; +}; +/** + * gets the animations direction signal events for trend lines + * @param name + */ + +export const getRscTrendlineColorAnimationDirection = (name: string): { events: string; update: string }[] => { + return [ + { events: `@${name}_voronoi:mouseover`, update: '1' }, + { events: `@${name}_voronoi:mouseout`, update: '-1' }, + ]; +}; +/** + * gets the previous highlighted item events for legends + * @param name + */ + +export const getRscLegendHighlightedItemPrev = (name: string): { events: string; update: string }[] => { + return [{ events: `@${name}_legendEntry:mouseover`, update: 'null' }]; +}; +/** + * gets the animation signal for Opacity animations + * @returns Signal + */ +const getRscAnimation = (): Signal => { + return { + name: RSC_ANIMATION, + value: 0, + on: [ + { + events: `timer{${DATA_ANIMATION_MILLISECONDS_PER_FRAME}}`, + update: `scale('rscAnimationCurve', scale('rscAnimationCurveInverse', ${RSC_ANIMATION}) + ${OPACITY_ANIMATION_FRAMES})`, + }, + ], + }; +}; +/** + * gets the Color animation direction signal for Opacity animations + * @param name + * @returns Signal + */ +const getRscColorAnimationDirection = (name: string): Signal => { + return { + name: 'rscColorAnimationDirection', + value: -1, + on: [ + { events: `@${concatName(name)}:mouseover`, update: '1' }, + { events: `@${concatName(name)}:mouseout`, update: '-1' }, + ], + }; +}; +/** + * gets the color animation signal for Opacity animations + * @returns Signal + */ +const getRscColorAnimation = (): Signal => { + return { + name: 'rscColorAnimation', + value: 0, + on: [ + { + events: `timer{${DATA_ANIMATION_MILLISECONDS_PER_FRAME}}`, + update: + "scale('rscAnimationCurve', scale('rscAnimationCurveInverse', rscColorAnimation) " + + '+ 0.06 * rscColorAnimationDirection)', + }, + ], + }; +}; +/** + * Gets the previous highlighted item signal for opacity animations. This signal is important for easing out + * when there is no hovering over the chart. + * @param name + * @param nestedDatum + * @returns Signal + */ +const getRscHighlightedItemPrevSignal = (name: string, nestedDatum?: boolean): Signal => { + const nestedPrefix = nestedDatum ? 'datum.' : '' + return { + name: `${HIGHLIGHTED_ITEM}_prev`, + value: null, + on: [{ events: `@${concatName(name)}:mouseover`, update: `${nestedPrefix}datum.${MARK_ID}` }], + }; +}; +/** + * Gets the previous highlighted Series signal. Important for when there is no hovering over + * legends with highlighting enabled. + * @param name + * @param nestedDatum + * @param isNull + * @returns Signal + */ +const getRscHighlightedSeriesPrevSignal = (name: string, nestedDatum?: boolean, isNull?: boolean): Signal => { + const nestedPrefix = nestedDatum ? 'datum.' : '' + return { + name: `${HIGHLIGHTED_SERIES}_prev`, + value: null, + on: [ + { + events: `@${concatName(name)}:mouseover`, + update: isNull ? 'null' : `${nestedPrefix}datum.${SERIES_ID}`, + }, + ], + }; +}; + +/** + * Concatenates the name if the mark has voronoi marks + * @param name + * @returns string + */ +const concatName = (name: string): string => { + if (name == 'line0' || name == 'scatter0' || name.includes('Trendline')) { + name = name.concat('_voronoi'); + } + return name; +}; diff --git a/src/specBuilder/specUtils.test.ts b/src/specBuilder/specUtils.test.ts index f3eccc8b0..15d4146c2 100644 --- a/src/specBuilder/specUtils.test.ts +++ b/src/specBuilder/specUtils.test.ts @@ -10,18 +10,24 @@ * governing permissions and limitations under the License. */ import { + ANIMATION_FUNCTION, COLOR_SCALE, DEFAULT_CATEGORICAL_DIMENSION, DEFAULT_COLOR, DEFAULT_SECONDARY_COLOR, DEFAULT_TRANSFORMED_TIME_DIMENSION, + FILTERED_PREVIOUS_TABLE, LINE_TYPE_SCALE, + MARK_ID, TABLE, } from '@constants'; +import { areaData } from '@stories/data/data'; +import { manipulateData } from '@test-utils'; import { ROUNDED_SQUARE_PATH } from 'svgPaths'; import { BandScale, OrdinalScale } from 'vega'; import { + getAnimationMarks, getColorValue, getD3FormatSpecifierFromNumberFormat, getDimensionField, @@ -204,4 +210,69 @@ describe('specUtils', () => { expect(getD3FormatSpecifierFromNumberFormat(',.2f')).toEqual(',.2f'); }); }); + + describe('getAnimationMarks()', () => { + test('should return default', () => { + const animationMarks = getAnimationMarks('dimension', 'maxTemperature', areaData, [], 'linear'); + expect(animationMarks).toStrictEqual({ + scale: 'linear', + signal: `datum.maxTemperature * ${ANIMATION_FUNCTION}`, + }); + }); + test('should return same dimensions', () => { + const animationMarks = getAnimationMarks( + 'dimension', + 'maxTemperature', + areaData, + areaData.map((data) => { + return { + ...data, + minTemperature: manipulateData(data.minTemperature), + maxTemperature: manipulateData(data.maxTemperature), + }; + }), + 'linear' + ); + expect(animationMarks).toStrictEqual({ + scale: 'linear', + signal: `(data('${FILTERED_PREVIOUS_TABLE}')[indexof(pluck(data('${FILTERED_PREVIOUS_TABLE}'), '${MARK_ID}'), (datum.${MARK_ID} + length(data('${FILTERED_PREVIOUS_TABLE}'))))].maxTemperature * (1 - ${ANIMATION_FUNCTION})) + (datum.maxTemperature * ${ANIMATION_FUNCTION})`, + }); + }); + test('should return different dimensions (one extra data point)', () => { + const animationMarks = getAnimationMarks( + 'dimension', + 'maxTemperature', + areaData, + areaData.concat({ + datetime: 1668509200000, + minTemperature: 5, + maxTemperature: 32, + series: 'Add Fallout', + }), + 'linear' + ); + expect(animationMarks).toStrictEqual({ + scale: 'linear', + signal: `datum.maxTemperature * ${ANIMATION_FUNCTION}`, + }); + }); + test('should return different dimensions (one less data point)', () => { + const animationMarks = getAnimationMarks( + 'dimension', + 'maxTemperature', + areaData.concat({ + datetime: 1668509200000, + minTemperature: 5, + maxTemperature: 32, + series: 'Add Fallout', + }), + areaData, + 'linear' + ); + expect(animationMarks).toStrictEqual({ + scale: 'linear', + signal: `datum.maxTemperature * ${ANIMATION_FUNCTION}`, + }); + }); + }); }); diff --git a/src/specBuilder/specUtils.ts b/src/specBuilder/specUtils.ts index ed2db1cf7..d4bb4ca89 100644 --- a/src/specBuilder/specUtils.ts +++ b/src/specBuilder/specUtils.ts @@ -12,6 +12,7 @@ import { spectrumColors } from '@themes'; import { DATE_PATH, ROUNDED_SQUARE_PATH } from 'svgPaths'; import { + ChartData, ChartSymbolShape, ColorFacet, ColorScheme, @@ -29,15 +30,19 @@ import { import { Data, Scale, ScaleType, Spec, ValuesData } from 'vega'; import { + ANIMATION_FUNCTION, COLOR_SCALE, DEFAULT_TRANSFORMED_TIME_DIMENSION, + FILTERED_PREVIOUS_TABLE, FILTERED_TABLE, LINE_TYPE_SCALE, MARK_ID, OPACITY_SCALE, + PREVIOUS_TABLE, TABLE, } from '../constants'; import { SanitizedSpecProps } from '../types'; +import { getIdentifierTransform } from './data/dataUtils'; /** * gets all the keys that are used to facet by @@ -222,8 +227,10 @@ export const getSymbolWidthFromRscSymbolSize = (symbolSize: SymbolSize): number * base data that gets initialized with every uncontrolled spec */ export const baseData: Data[] = [ - { name: TABLE, values: [], transform: [{ type: 'identifier', as: MARK_ID }] }, + { name: TABLE, values: [], transform: [getIdentifierTransform()] }, { name: FILTERED_TABLE, source: TABLE }, + { name: PREVIOUS_TABLE, values: [], transform: [getIdentifierTransform()] }, + { name: FILTERED_PREVIOUS_TABLE, source: PREVIOUS_TABLE }, ]; /** @@ -323,3 +330,34 @@ export const getD3FormatSpecifierFromNumberFormat = (numberFormat: NumberFormat return numberFormat; } }; + +export const getAnimationMarks = ( + dimension: string, + metric: string, + data?: ChartData[], + previousData?: ChartData[], + scale = 'yLinear' +) => { + const easingFunction = ANIMATION_FUNCTION; + + let markUpdate = { + scale, + signal: `datum.${metric} * ${easingFunction}`, + }; + + if (data && previousData) { + const hasSameDimensions = + data !== previousData && + data.every((d) => previousData.some((pd) => d[dimension] === pd[dimension])) && + data.length == previousData.length; + if (hasSameDimensions) { + // If data isn't similar enough, keep the animation from zero as shown above + const datumVal = `(datum.${MARK_ID} + length(data('${FILTERED_PREVIOUS_TABLE}')))`; + markUpdate = { + scale, + signal: `(data('${FILTERED_PREVIOUS_TABLE}')[indexof(pluck(data('${FILTERED_PREVIOUS_TABLE}'), '${MARK_ID}'), ${datumVal})].${metric} * (1 - ${easingFunction})) + (datum.${metric} * ${easingFunction})`, + }; + } + } + return markUpdate; +}; diff --git a/src/specBuilder/trendline/trendlineDataUtils.test.ts b/src/specBuilder/trendline/trendlineDataUtils.test.ts index 8644efaf1..017008b05 100644 --- a/src/specBuilder/trendline/trendlineDataUtils.test.ts +++ b/src/specBuilder/trendline/trendlineDataUtils.test.ts @@ -17,6 +17,7 @@ import { DEFAULT_COLOR, DEFAULT_TIME_DIMENSION, FILTERED_TABLE, + MARK_ID, MS_PER_DAY, SERIES_ID, TRENDLINE_VALUE, @@ -64,10 +65,10 @@ describe('addTrendlineData()', () => { test('should add datasource for trendline', () => { const trendlineData = getDefaultData(); - expect(trendlineData).toHaveLength(2); + expect(trendlineData).toHaveLength(4); addTrendlineData(trendlineData, defaultLineProps); - expect(trendlineData).toHaveLength(3); - expect(trendlineData[2]).toStrictEqual({ + expect(trendlineData).toHaveLength(5); + expect(trendlineData[4]).toStrictEqual({ name: 'line0Trendline0_highResolutionData', source: FILTERED_TABLE, transform: [ @@ -83,6 +84,10 @@ describe('addTrendlineData()', () => { expr: `datum.${DEFAULT_COLOR}`, as: SERIES_ID, }, + { + type: 'identifier', + as: MARK_ID, + }, ], }); }); @@ -91,11 +96,12 @@ describe('addTrendlineData()', () => { const trendlineData = getDefaultData(); addTrendlineData(trendlineData, { ...defaultLineProps, + animations: false, children: [createElement(Trendline, {}, createElement(ChartTooltip))], }); - expect(trendlineData).toHaveLength(7); - expect(trendlineData[5]).toHaveProperty('name', 'line0_allTrendlineData'); - expect(trendlineData[6]).toHaveProperty('name', 'line0Trendline_highlightedData'); + expect(trendlineData).toHaveLength(9); + expect(trendlineData[7]).toHaveProperty('name', 'line0_allTrendlineData'); + expect(trendlineData[8]).toHaveProperty('name', 'line0Trendline_highlightedData'); }); test('should add _highResolutionData if doing a regression method', () => { @@ -103,10 +109,11 @@ describe('addTrendlineData()', () => { addTrendlineData(trendlineData, { ...defaultLineProps, + animations: false, children: [createElement(Trendline, { method: 'linear' })], }); - expect(trendlineData).toHaveLength(3); - expect(trendlineData[2]).toHaveProperty('name', 'line0Trendline0_highResolutionData'); + expect(trendlineData).toHaveLength(5); + expect(trendlineData[4]).toHaveProperty('name', 'line0Trendline0_highResolutionData'); }); test('should add _params and _data if doing a regression method and there is a tooltip on the trendline', () => { @@ -114,25 +121,27 @@ describe('addTrendlineData()', () => { addTrendlineData(trendlineData, { ...defaultLineProps, + animations: false, children: [createElement(Trendline, { method: 'linear' }, createElement(ChartTooltip))], }); - expect(trendlineData).toHaveLength(7); - expect(trendlineData[3]).toHaveProperty('name', 'line0Trendline0_params'); - expect(trendlineData[4]).toHaveProperty('name', 'line0Trendline0_data'); + expect(trendlineData).toHaveLength(9); + expect(trendlineData[5]).toHaveProperty('name', 'line0Trendline0_params'); + expect(trendlineData[6]).toHaveProperty('name', 'line0Trendline0_data'); }); test('should add sort transform, then window trandform, and then dimension range filter transform for movingAverage', () => { const trendlineData = getDefaultData(); addTrendlineData(trendlineData, { ...defaultLineProps, + animations: false, children: [createElement(Trendline, { method: 'movingAverage-3', dimensionRange: [1, 2] })], }); - expect(trendlineData).toHaveLength(3); - expect(trendlineData[2]).toHaveProperty('name', 'line0Trendline0_data'); - expect(trendlineData[2].transform).toHaveLength(3); - expect(trendlineData[2].transform?.[0]).toHaveProperty('type', 'collect'); - expect(trendlineData[2].transform?.[1]).toHaveProperty('type', 'window'); - expect(trendlineData[2].transform?.[2]).toHaveProperty('type', 'filter'); + expect(trendlineData).toHaveLength(5); + expect(trendlineData[4]).toHaveProperty('name', 'line0Trendline0_data'); + expect(trendlineData[4].transform).toHaveLength(3); + expect(trendlineData[4].transform?.[0]).toHaveProperty('type', 'collect'); + expect(trendlineData[4].transform?.[1]).toHaveProperty('type', 'window'); + expect(trendlineData[4].transform?.[2]).toHaveProperty('type', 'filter'); }); }); @@ -155,7 +164,13 @@ describe('getAggregateTrendlineData()', () => { describe('getRegressionTrendlineData()', () => { test('should return one data source if there are not any interactive children', () => { - const data = getRegressionTrendlineData(defaultLineProps, defaultTrendlineProps, [DEFAULT_COLOR]); + const data = getRegressionTrendlineData( + defaultLineProps, + defaultTrendlineProps, + [DEFAULT_COLOR], + defaultTrendlineProps.name, + FILTERED_TABLE + ); expect(data).toHaveLength(1); expect(data[0]).toHaveProperty('name', 'line0Trendline0_highResolutionData'); }); @@ -163,7 +178,9 @@ describe('getRegressionTrendlineData()', () => { const data = getRegressionTrendlineData( defaultLineProps, { ...defaultTrendlineProps, children: [createElement(ChartTooltip)] }, - [DEFAULT_COLOR] + [DEFAULT_COLOR], + defaultTrendlineProps.name, + FILTERED_TABLE ); expect(data).toHaveLength(3); expect(data[1]).toHaveProperty('name', 'line0Trendline0_params'); diff --git a/src/specBuilder/trendline/trendlineDataUtils.ts b/src/specBuilder/trendline/trendlineDataUtils.ts index 7b069d5cf..d958b44bf 100644 --- a/src/specBuilder/trendline/trendlineDataUtils.ts +++ b/src/specBuilder/trendline/trendlineDataUtils.ts @@ -10,15 +10,22 @@ * governing permissions and limitations under the License. */ import { + FILTERED_PREVIOUS_TABLE, FILTERED_TABLE, HIGHLIGHTED_ITEM, HIGHLIGHTED_SERIES, MARK_ID, + PREVIOUS_PREFIX, SELECTED_ITEM, SELECTED_SERIES, SERIES_ID, } from '@constants'; -import { getSeriesIdTransform, getTableData } from '@specBuilder/data/dataUtils'; +import { + getIdentifierTransform, + getPreviousTableData, + getSeriesIdTransform, + getTableData, +} from '@specBuilder/data/dataUtils'; import { hasInteractiveChildren } from '@specBuilder/marks/markUtils'; import { getFacetsFromProps } from '@specBuilder/specUtils'; import { produce } from 'immer'; @@ -56,6 +63,82 @@ export const addTrendlineData = (data: Data[], markProps: TrendlineParentProps) const tableData = getTableData(data); tableData.transform = addTableDataTransforms(tableData.transform ?? [], markProps); + + if (markProps.animations) { + const previousTableData = getPreviousTableData(data); + previousTableData.transform = addTableDataTransforms( + previousTableData.transform ?? [], + markProps, + PREVIOUS_PREFIX + ); + } +}; + +/** + * Helper function for getTrendlineData + * @param data + * @param markProps + * @param trendlineProps + * @param facets + */ +const pushRegressionTrendlineData = ( + data: SourceData[], + markProps: TrendlineParentProps, + trendlineProps: TrendlineSpecProps, + facets: string[] +) => { + data.push(...getRegressionTrendlineData(markProps, trendlineProps, facets, trendlineProps.name, FILTERED_TABLE)); + if (markProps.animations) { + data.push( + ...getRegressionTrendlineData( + markProps, + trendlineProps, + facets, + `${PREVIOUS_PREFIX}${trendlineProps.name}`, + FILTERED_PREVIOUS_TABLE + ) + ); + } +}; + +/** + * Helper function for getTrendlineData + * @param data + * @param markProps + * @param trendlineProps + * @param facets + */ +const pushAggregateTrendlineData = ( + data: SourceData[], + markProps: TrendlineParentProps, + trendlineProps: TrendlineSpecProps, + facets: string[] +) => { + data.push(...getAggregateTrendlineData(markProps, trendlineProps, facets)); +}; + +/** + * Helper function for getTrendlineData + * @param data + * @param markProps + * @param trendlineProps + */ +const pushWindowTrendlineData = ( + data: SourceData[], + markProps: TrendlineParentProps, + trendlineProps: TrendlineSpecProps +) => { + data.push(getWindowTrendlineData(markProps, trendlineProps, trendlineProps.name, FILTERED_TABLE)); + if (markProps.animations) { + data.push( + getWindowTrendlineData( + markProps, + trendlineProps, + `${PREVIOUS_PREFIX}${trendlineProps.name}`, + FILTERED_PREVIOUS_TABLE + ) + ); + } }; /** @@ -79,12 +162,13 @@ export const getTrendlineData = (markProps: TrendlineParentProps): SourceData[] const { facets } = getFacetsFromProps({ color, lineType }); if (isRegressionMethod(method)) { - data.push(...getRegressionTrendlineData(markProps, trendlineProps, facets)); + pushRegressionTrendlineData(data, markProps, trendlineProps, facets); } else if (isAggregateMethod(method)) { - data.push(...getAggregateTrendlineData(markProps, trendlineProps, facets)); + pushAggregateTrendlineData(data, markProps, trendlineProps, facets); } else if (isWindowMethod(method)) { - data.push(getWindowTrendlineData(markProps, trendlineProps)); + pushWindowTrendlineData(data, markProps, trendlineProps); } + if (displayOnHover) { data.push(getTrendlineDisplayOnHoverData(name, method)); } @@ -124,6 +208,7 @@ export const getAggregateTrendlineData = ( ...dimensionRangeTransforms, ...getTrendlineStatisticalTransforms(markProps, trendlineProps, true), getSeriesIdTransform(facets), + getIdentifierTransform(), ], }); if (hasInteractiveChildren(trendlineChildren)) { @@ -134,6 +219,7 @@ export const getAggregateTrendlineData = ( transform: [ ...dimensionRangeTransforms, ...getTrendlineStatisticalTransforms(markProps, trendlineProps, false), + getIdentifierTransform(), ], }); } @@ -150,18 +236,13 @@ export const getAggregateTrendlineData = ( export const getRegressionTrendlineData = ( markProps: TrendlineParentProps, trendlineProps: TrendlineSpecProps, - facets: string[] + facets: string[], + trendlineName: string, + source: string ) => { const data: SourceData[] = []; const { dimension, metric } = markProps; - const { - dimensionRange, - method, - name, - orientation, - children: trendlineChildren, - trendlineDimension, - } = trendlineProps; + const { dimensionRange, method, orientation, children: trendlineChildren, trendlineDimension } = trendlineProps; const { trendlineDimension: standardTrendlineDimension } = getTrendlineDimensionMetric( dimension, metric, @@ -171,12 +252,13 @@ export const getRegressionTrendlineData = ( const dimensionRangeTransforms = getTrendlineDimensionRangeTransforms(standardTrendlineDimension, dimensionRange); // high resolution data used for drawing the smooth trendline data.push({ - name: `${name}_highResolutionData`, - source: FILTERED_TABLE, + name: `${trendlineName}_highResolutionData`, + source, transform: [ ...dimensionRangeTransforms, ...getTrendlineStatisticalTransforms(markProps, trendlineProps, true), getSeriesIdTransform(facets), + getIdentifierTransform(), ], }); if (hasInteractiveChildren(trendlineChildren)) { @@ -184,20 +266,21 @@ export const getRegressionTrendlineData = ( // the high resolution data has too much detail and we don't want a tooltip at each high resolution point data.push( { - name: `${name}_params`, - source: FILTERED_TABLE, + name: `${trendlineName}_params`, + source, transform: [ ...dimensionRangeTransforms, ...getTrendlineStatisticalTransforms(markProps, trendlineProps, false), ], }, { - name: `${name}_data`, - source: FILTERED_TABLE, + name: `${trendlineName}_data`, + source, transform: [ ...dimensionRangeTransforms, getTrendlineParamLookupTransform(markProps, trendlineProps), ...getTrendlineParamFormulaTransforms(trendlineDimension, method), + getIdentifierTransform(), ], } ); @@ -205,15 +288,20 @@ export const getRegressionTrendlineData = ( return data; }; -/** +export /** * Gets the data source and transforms for window trendlines (movingAverage-x) * @param markProps * @param trendlineProps * @returns Data */ -const getWindowTrendlineData = (markProps: TrendlineParentProps, trendlineProps: TrendlineSpecProps): SourceData => ({ - name: `${trendlineProps.name}_data`, - source: FILTERED_TABLE, +const getWindowTrendlineData = ( + markProps: TrendlineParentProps, + trendlineProps: TrendlineSpecProps, + name: string, + source: string +): SourceData => ({ + name: `${name}_data`, + source, transform: [ ...getTrendlineStatisticalTransforms(markProps, trendlineProps, false), ...getTrendlineDimensionRangeTransforms(markProps.dimension, trendlineProps.dimensionRange), @@ -272,34 +360,37 @@ export const getTrendlineStatisticalTransforms = ( * @param transforms * @param markProps */ -export const addTableDataTransforms = produce((transforms, markProps) => { - const { dimension, metric } = markProps; +export const addTableDataTransforms = produce( + (transforms, markProps, extentPrefix = '') => { + const { dimension, metric } = markProps; - const trendlines = getTrendlines(markProps); - for (const { isDimensionNormalized, method, name, orientation, trendlineDimension } of trendlines) { - if (isRegressionMethod(method)) { - // time scales need to be normalized for regression trendlines - const { trendlineDimension: standardTrendlinDimension } = getTrendlineDimensionMetric( - dimension, - metric, - orientation, - false - ); + const trendlines = getTrendlines(markProps); + for (const { isDimensionNormalized, method, name, orientation, trendlineDimension } of trendlines) { + if (isRegressionMethod(method)) { + // time scales need to be normalized for regression trendlines + const { trendlineDimension: standardTrendlinDimension } = getTrendlineDimensionMetric( + dimension, + metric, + orientation, + false + ); - if (isDimensionNormalized) { - if ( - !transforms.some( - (transform) => 'as' in transform && transform.as === `${standardTrendlinDimension}Normalized` - ) - ) { - transforms.push(...getNormalizedDimensionTransform(standardTrendlinDimension)); + if (isDimensionNormalized) { + if ( + !transforms.some( + (transform) => + 'as' in transform && transform.as === `${standardTrendlinDimension}Normalized` + ) + ) { + transforms.push(...getNormalizedDimensionTransform(standardTrendlinDimension)); + } } + // add the extent transform + transforms.push(getRegressionExtentTransform(trendlineDimension, `${extentPrefix}${name}`)); } - // add the extent transform - transforms.push(getRegressionExtentTransform(trendlineDimension, name)); } } -}); +); /** * Gets the data source and transforms for displaying the trendline on hover diff --git a/src/specBuilder/trendline/trendlineMarkUtils.ts b/src/specBuilder/trendline/trendlineMarkUtils.ts index 0d899dea0..d1046f377 100644 --- a/src/specBuilder/trendline/trendlineMarkUtils.ts +++ b/src/specBuilder/trendline/trendlineMarkUtils.ts @@ -10,7 +10,7 @@ * governing permissions and limitations under the License. */ import { ChartTooltip } from '@components/ChartTooltip'; -import { TRENDLINE_VALUE } from '@constants'; +import { ANIMATION_FUNCTION, MARK_ID, PREVIOUS_PREFIX, TRENDLINE_VALUE } from '@constants'; import { getLineHoverMarks, getLineOpacity } from '@specBuilder/line/lineMarkUtils'; import { LineMarkProps } from '@specBuilder/line/lineUtils'; import { @@ -250,6 +250,7 @@ export const getTrendlineLineMark = (markProps: TrendlineParentProps, trendlineP }, update: { x: getLineXProductionRule(trendlineDimension, dimensionScaleType, orientation, isDimensionNormalized), + ...(markProps.animations && { y: getLineYAnimationMarks(markProps, trendlineProps) }), opacity: getLineOpacity(getLineMarkProps(markProps, trendlineProps)), }, }, @@ -293,6 +294,58 @@ export const getLineXProductionRule = ( : { scale, field: trendlineDimension }; }; +/** + * Gets the marks needed to show trendline animations + * @param markProps + * @param trendlineProps + * @param easingFunction + * @param scale + * @returns + */ +const getLineYAnimationMarks = ( + markProps: TrendlineParentProps, + trendlineProps: TrendlineSpecProps, + easingFunction: string = ANIMATION_FUNCTION, + scale: string = 'yLinear' +) => { + const { method, name, trendlineDimension } = trendlineProps; + const { data, previousData } = markProps; + + const animationFromZeroMark = { + scale, + signal: `datum.${TRENDLINE_VALUE} * ${easingFunction}`, + }; + + const hasNoPreviousData = !(data && previousData); + if (hasNoPreviousData) { + return animationFromZeroMark; + } + + const hasDifferentDimensions = !( + data !== previousData && + data.length == previousData.length && + data.every((d) => previousData.some((pd) => d[trendlineDimension] === pd[trendlineDimension])) + ); + if (hasDifferentDimensions) { + return animationFromZeroMark; + } + + const tableSuffix = isRegressionMethod(method) ? 'highResolutionData' : 'data'; + const trendlineFullName = `${PREVIOUS_PREFIX}${name}_${tableSuffix}`; + const animationFromPreviousDataMark = { + scale, + signal: ` + (data('${trendlineFullName}') + [indexof( + pluck(data('${trendlineFullName}'), '${MARK_ID}'), + datum.${MARK_ID} + length(data('${trendlineFullName}')) + )].${TRENDLINE_VALUE} * (1 - ${easingFunction}) + ) + (datum.${TRENDLINE_VALUE} * ${easingFunction}) + `, + }; + return animationFromPreviousDataMark; +}; + const getTrendlineHoverMarks = (markProps: TrendlineParentProps, highlightRawPoint: boolean): GroupMark => { const { metric, name } = markProps; const trendlines = getTrendlines(markProps); @@ -322,10 +375,11 @@ const getLineMarkProps = ( { dimensionScaleType, displayOnHover, lineWidth, metric, name, opacity }: TrendlineSpecProps, override?: Partial ): LineMarkProps => { - const { children, color, colorScheme, dimension, interactiveMarkName, lineType } = markProps; + const { children, color, colorScheme, dimension, interactiveMarkName, lineType, animations } = markProps; const popoverMarkName = 'popoverMarkName' in markProps ? markProps.popoverMarkName : undefined; const staticPoint = 'staticPoint' in markProps ? markProps.staticPoint : undefined; return { + animations, children, color, colorScheme, diff --git a/src/specBuilder/trendline/trendlineScaleUtils.ts b/src/specBuilder/trendline/trendlineScaleUtils.ts index cbb843d0e..e5365fcd5 100644 --- a/src/specBuilder/trendline/trendlineScaleUtils.ts +++ b/src/specBuilder/trendline/trendlineScaleUtils.ts @@ -10,9 +10,11 @@ * governing permissions and limitations under the License. */ import { FILTERED_TABLE, LINEAR_PADDING } from '@constants'; +import { hasPopover, hasTooltip } from '@specBuilder/marks/markUtils'; +import { addRscAnimationScales, hasScaleByName } from '@specBuilder/scale/scaleSpecBuilder'; import { Scale } from 'vega'; -import { TrendlineParentProps, hasTrendlineWithNormalizedDimension } from './trendlineUtils'; +import { TrendlineParentProps, getTrendlines, hasTrendlineWithNormalizedDimension } from './trendlineUtils'; /** * Gets all the scales used for trendlines @@ -21,7 +23,6 @@ import { TrendlineParentProps, hasTrendlineWithNormalizedDimension } from './tre */ export const getTrendlineScales = (props: TrendlineParentProps): Scale[] => { const { dimension } = props; - // if there is a trendline that requires a normalized dimension, add the scale if (hasTrendlineWithNormalizedDimension(props)) { return [ @@ -38,3 +39,19 @@ export const getTrendlineScales = (props: TrendlineParentProps): Scale[] => { } return []; }; + +/** + * Adds scales to spec if the scales are not already present and if trend lines have highlighting enabled. + * @param name + * @param scales + * @param props + */ + +export const checkTrendlineAnimationScales = (name: string, scales: Scale[], props: TrendlineParentProps) => { + if ( + !hasScaleByName(scales, 'rscAnimationCurve') && + getTrendlines(props).some((trendline) => hasTooltip(trendline.children) || hasPopover(trendline.children)) + ) { + addRscAnimationScales(scales); + } +}; diff --git a/src/specBuilder/trendline/trendlineSignalUtils.test.ts b/src/specBuilder/trendline/trendlineSignalUtils.test.ts index 845d25299..1892b9046 100644 --- a/src/specBuilder/trendline/trendlineSignalUtils.test.ts +++ b/src/specBuilder/trendline/trendlineSignalUtils.test.ts @@ -13,7 +13,7 @@ import { createElement } from 'react'; import { ChartTooltip } from '@components/ChartTooltip'; import { Trendline } from '@components/Trendline'; -import { HIGHLIGHTED_ITEM, HIGHLIGHTED_SERIES } from '@constants'; +import { HIGHLIGHTED_ITEM, HIGHLIGHTED_SERIES, RSC_ANIMATION } from '@constants'; import { defaultSignals } from '@specBuilder/specTestUtils'; import { Signal } from 'vega'; @@ -28,6 +28,7 @@ describe('getTrendlineSignals()', () => { test('should add voronoi hover signal events if ChartTooltip exists', () => { setTrendlineSignals(signals, { ...defaultLineProps, + animations: false, children: [createElement(Trendline, {}, createElement(ChartTooltip))], }); expect(signals).toHaveLength(4); @@ -37,6 +38,30 @@ describe('getTrendlineSignals()', () => { expect(signals[1].on).toHaveLength(2); }); + test('should add voronoi hover signal events if ChartTooltip exists with Animations', () => { + setTrendlineSignals(signals, { + ...defaultLineProps, + animations: true, + children: [createElement(Trendline, {}, createElement(ChartTooltip))], + }); + expect(signals).toHaveLength(9); + expect(signals[0]).toHaveProperty('name', HIGHLIGHTED_ITEM); + expect(signals[0].on).toHaveLength(2); + expect(signals[1]).toHaveProperty('name', HIGHLIGHTED_SERIES); + expect(signals[1].on).toHaveLength(2); + expect(signals[4]).toHaveProperty('name', RSC_ANIMATION); + expect(signals[4].on).toHaveLength(1); + expect(signals[5]).toHaveProperty('name', 'rscColorAnimationDirection'); + expect(signals[5].on).toHaveLength(4); + expect(signals[6]).toHaveProperty('name', 'rscColorAnimation'); + expect(signals[6].on).toHaveLength(1); + expect(signals[7]).toHaveProperty('name', `${HIGHLIGHTED_ITEM}_prev`); + expect(signals[7].on).toHaveLength(1); + expect(signals[8]).toHaveProperty('name', `${HIGHLIGHTED_SERIES}_prev`); + expect(signals[8].on).toHaveLength(1); + }); + + test('should not modify any signals if there is not a ChartTooltip', () => { setTrendlineSignals(signals, defaultLineProps); expect(signals).toStrictEqual(defaultSignals); diff --git a/src/specBuilder/trendline/trendlineSignalUtils.ts b/src/specBuilder/trendline/trendlineSignalUtils.ts index 7ee8a972d..8736392ab 100644 --- a/src/specBuilder/trendline/trendlineSignalUtils.ts +++ b/src/specBuilder/trendline/trendlineSignalUtils.ts @@ -9,21 +9,29 @@ * OF ANY KIND, either express or implied. See the License for the specific language * governing permissions and limitations under the License. */ -import { hasTooltip } from '@specBuilder/marks/markUtils'; +import { RSC_ANIMATION } from '@constants'; +import { TrendlineSpecProps } from '@rsc'; +import { hasPopover, hasTooltip } from '@specBuilder/marks/markUtils'; import { addHighlightedItemSignalEvents, addHighlightedSeriesSignalEvents, + getRscAnimationSignals, + getRscTrendlineColorAnimationDirection, + hasSignalByName, } from '@specBuilder/signal/signalSpecBuilder'; import { Signal } from 'vega'; import { TrendlineParentProps, getTrendlines } from './trendlineUtils'; export const setTrendlineSignals = (signals: Signal[], markProps: TrendlineParentProps): void => { - const { name: markName } = markProps; + const { name: markName, animations } = markProps; const trendlines = getTrendlines(markProps); - + // if animations are enabled, add necessary opacity animation signals + if (animations) { + addRSCTrendlineAnimationSignals(markName, signals, trendlines); + } if (trendlines.some((trendline) => hasTooltip(trendline.children))) { - addHighlightedItemSignalEvents(signals, `${markName}Trendline_voronoi`, 2); + addHighlightedItemSignalEvents({ signals, markName: `${markName}Trendline_voronoi`, datumOrder: 2 }); addHighlightedSeriesSignalEvents(signals, `${markName}Trendline_voronoi`, 2); } @@ -31,3 +39,20 @@ export const setTrendlineSignals = (signals: Signal[], markProps: TrendlineParen addHighlightedSeriesSignalEvents(signals, `${markName}_voronoi`, 2); } }; +/** + * add signals and signal events to spec if signals are not present already and if trend line has highlighted enabled. + * @param name + * @param signals + * @param trendlines + */ +const addRSCTrendlineAnimationSignals = (name: string, signals: Signal[], trendlines: TrendlineSpecProps[]) => { + if ( + !hasSignalByName(signals, RSC_ANIMATION) && + trendlines.some((trendline) => hasTooltip(trendline.children) || hasPopover(trendline.children)) + ) { + signals.push(...getRscAnimationSignals(`${name}Trendline`, true)); + signals + .find((sig) => sig.name == 'rscColorAnimationDirection') + ?.on?.push(...getRscTrendlineColorAnimationDirection(`${name}Trendline`)); + } +}; diff --git a/src/specBuilder/trendline/trendlineUtils.ts b/src/specBuilder/trendline/trendlineUtils.ts index 62c6eaca7..dceb189d8 100644 --- a/src/specBuilder/trendline/trendlineUtils.ts +++ b/src/specBuilder/trendline/trendlineUtils.ts @@ -10,7 +10,10 @@ * governing permissions and limitations under the License. */ import { Trendline } from '@components/Trendline'; -import { FILTERED_TABLE, MS_PER_DAY, TRENDLINE_VALUE } from '@constants'; +import { + FILTERED_TABLE, + MS_PER_DAY, + TRENDLINE_VALUE } from '@constants'; import { sanitizeTrendlineChildren } from '@utils'; import { AggregateMethod, @@ -25,7 +28,7 @@ import { TrendlineSpecProps, WindowMethod, } from 'types'; -import { SignalRef } from 'vega'; +import { SignalRef } from 'vega'; /** These are all the spec props that currently support trendlines */ export type TrendlineParentProps = LineSpecProps | ScatterSpecProps; @@ -248,4 +251,4 @@ export const getTrendlineScaleType = ( // y axis only support linear... for now... if (trendlineOrientation === 'vertical') return 'linear'; return 'scaleType' in markProps ? markProps.scaleType : markProps.dimensionScaleType; -}; +}; \ No newline at end of file diff --git a/src/stories/Chart.story.tsx b/src/stories/Chart.story.tsx index bf0de83c5..0a2048602 100644 --- a/src/stories/Chart.story.tsx +++ b/src/stories/Chart.story.tsx @@ -26,7 +26,7 @@ export default { }; const ChartLineStory: StoryFn = (args): ReactElement => { - const props = useChartProps(args); + const props = useChartProps({ ...args }); return ( @@ -37,7 +37,7 @@ const ChartLineStory: StoryFn = (args): ReactElement => { }; const ChartTimeStory: StoryFn = (args): ReactElement => { - const props = useChartProps(args); + const props = useChartProps({ ...args }); return ( @@ -54,6 +54,7 @@ Basic.args = { data, renderer: 'svg', height: 300 }; const BackgroundColor = bindWithProps(ChartLineStory); BackgroundColor.args = { + animations: false, backgroundColor: 'gray-50', padding: 32, data, @@ -61,6 +62,7 @@ BackgroundColor.args = { const Config = bindWithProps(ChartBarStory); Config.args = { + animations: false, config: { rect: { strokeWidth: 2, @@ -71,12 +73,14 @@ Config.args = { const Locale = bindWithProps(ChartTimeStory); Locale.args = { + animations: false, locale: 'de-DE', data: workspaceTrendsData, }; const Width = bindWithProps(ChartBarStory); Width.args = { + animations: false, width: '50%', minWidth: 300, maxWidth: 600, diff --git a/src/stories/ChartBarStory.tsx b/src/stories/ChartBarStory.tsx index 38a3890df..599dc9c96 100644 --- a/src/stories/ChartBarStory.tsx +++ b/src/stories/ChartBarStory.tsx @@ -16,7 +16,7 @@ import { Axis, Bar, Chart } from '@rsc'; import { StoryFn } from '@storybook/react'; export const ChartBarStory: StoryFn = (args): ReactElement => { - const props = useChartProps(args); + const props = useChartProps({...args}); return ( diff --git a/src/stories/ChartColors.story.tsx b/src/stories/ChartColors.story.tsx index f23642ab0..a87a4a2ca 100644 --- a/src/stories/ChartColors.story.tsx +++ b/src/stories/ChartColors.story.tsx @@ -22,24 +22,28 @@ export default { const SpectrumColorNames = bindWithProps(ChartBarStory); SpectrumColorNames.args = { + animations: false, colors: ['gray-800', 'gray-700', 'gray-600', 'gray-500'], data, }; const SpectrumDivergentColorScheme = bindWithProps(ChartBarStory); SpectrumDivergentColorScheme.args = { + animations: false, colors: 'divergentOrangeYellowSeafoam5', data, }; const SpectrumSequentialColorScheme = bindWithProps(ChartBarStory); SpectrumSequentialColorScheme.args = { + animations: false, colors: 'sequentialCerulean5', data, }; const CssColors = bindWithProps(ChartBarStory); CssColors.args = { + animations: false, colors: ['purple', 'rgb(38, 142, 108)', '#0d66d0', 'hsl(32deg, 86%, 46%)'], data, }; diff --git a/src/stories/ChartExamples.story.tsx b/src/stories/ChartExamples.story.tsx index 9ff7379cc..5ccf58421 100644 --- a/src/stories/ChartExamples.story.tsx +++ b/src/stories/ChartExamples.story.tsx @@ -508,11 +508,12 @@ UserGrowthBarGrowth.args = { height: 500, minWidth: 600, maxWidth: 1600, - width: 'auto', + width: 'auto' }; const UserGrowthTimeComparisonBarGrowth = bindWithProps(UserGrowthBarTimeComparisonStory); UserGrowthTimeComparisonBarGrowth.args = { + animations: false, data: userGrowthTimeComparisonData, colors: userGrowthColors, height: 500, @@ -525,6 +526,7 @@ UserGrowthTimeComparisonBarGrowth.args = { const UserGrowthAreaGrowth = bindWithProps(UserGrowthAreaStory); UserGrowthAreaGrowth.args = { + animations: false, data: userGrowthData, colors: userGrowthColors, height: 500, @@ -536,6 +538,7 @@ UserGrowthAreaGrowth.args = { const FunnelConversion = bindWithProps(FunnelConversionStory); const funnelColors: Colors[] = categorical16.map((color) => [color, 'gray-300']); FunnelConversion.args = { + animations: false, data: funnelConversionData, colors: funnelColors, height: 500, @@ -547,6 +550,7 @@ FunnelConversion.args = { const FunnelTimeComparison = bindWithProps(FunnelTimeComparisonStory); const funnelTimeComparisonColors: Colors[] = categorical16.map((color) => [color, 'gray-300']); FunnelTimeComparison.args = { + animations: false, data: funnelConversionTimeComparisonData, colors: funnelTimeComparisonColors, height: 500, @@ -558,6 +562,7 @@ FunnelTimeComparison.args = { const ReleaseImpact = bindWithProps(ReleaseImpactStory); ReleaseImpact.args = { + animations: false, data: releaseImpactData, height: 500, minWidth: 840, @@ -565,6 +570,7 @@ ReleaseImpact.args = { const ReleaseImpactBar = bindWithProps(ReleaseImpactBarStory); ReleaseImpactBar.args = { + animations: false, data: releaseImpactBarData, height: 250, minWidth: 840, @@ -572,6 +578,7 @@ ReleaseImpactBar.args = { const TrendsTimeComparisonBar = bindWithProps(TrendsTimeComparisonBarStory); TrendsTimeComparisonBar.args = { + animations: false, data: trendsTimeComparisonData, height: 500, minWidth: 840, @@ -582,6 +589,7 @@ TrendsTimeComparisonBar.args = { const TrendsTimeComparisonStackedBar = bindWithProps(TrendsTimeComparisonStackedBarStory); TrendsTimeComparisonStackedBar.args = { + animations: false, data: trendsTimeComparisonData, height: 500, minWidth: 840, @@ -592,6 +600,7 @@ TrendsTimeComparisonStackedBar.args = { const TrendsTimeComparisonLine = bindWithProps(TrendsTimeComparisonLineStory); TrendsTimeComparisonLine.args = { + animations: false, data: trendsTimeComparisonData, height: 500, minWidth: 840, @@ -602,6 +611,7 @@ TrendsTimeComparisonLine.args = { const StackOverflowTrends = bindWithProps(StackOverflowStory); StackOverflowTrends.args = { + animations: false, data: stackOverflowData, height: 500, minWidth: 840, diff --git a/src/stories/ChartExamples/FeatureMatrix/FeatureMatrix.story.tsx b/src/stories/ChartExamples/FeatureMatrix/FeatureMatrix.story.tsx index 9b6072c47..88059eea6 100644 --- a/src/stories/ChartExamples/FeatureMatrix/FeatureMatrix.story.tsx +++ b/src/stories/ChartExamples/FeatureMatrix/FeatureMatrix.story.tsx @@ -100,6 +100,7 @@ const TimeCompareFeatureMatrixStory: StoryFn = (args): ReactElemen const FeatureMatrix = bindWithProps(FeatureMatrixStory); FeatureMatrix.args = { + animations: false, width: 'auto', maxWidth: 850, height: 500, @@ -108,6 +109,7 @@ FeatureMatrix.args = { const MultipleSegmentFeatureMatrix = bindWithProps(MultipleSegmentFeatureMatrixStory); MultipleSegmentFeatureMatrix.args = { + animations: false, width: 'auto', maxWidth: 850, height: 500, @@ -116,6 +118,7 @@ MultipleSegmentFeatureMatrix.args = { const TimeCompareFeatureMatrix = bindWithProps(TimeCompareFeatureMatrixStory); TimeCompareFeatureMatrix.args = { + animations: false, width: 'auto', maxWidth: 850, height: 500, diff --git a/src/stories/ChartStates.story.tsx b/src/stories/ChartStates.story.tsx index 5bad33412..60884bac6 100644 --- a/src/stories/ChartStates.story.tsx +++ b/src/stories/ChartStates.story.tsx @@ -21,6 +21,7 @@ export default { const EmptyState = bindWithProps(ChartBarStory); EmptyState.args = { + animations: false, data: [], height: 500, emptyStateText: 'No data found', @@ -28,6 +29,7 @@ EmptyState.args = { const LoadingState = bindWithProps(ChartBarStory); LoadingState.args = { + animations: false, data: [], height: 500, loading: true, diff --git a/src/stories/ChartUnsafeVega.story.tsx b/src/stories/ChartUnsafeVega.story.tsx index 703b8d705..0f6ccb726 100644 --- a/src/stories/ChartUnsafeVega.story.tsx +++ b/src/stories/ChartUnsafeVega.story.tsx @@ -33,6 +33,7 @@ const UnsafeVegaSpecStory: StoryFn = (args): ReactElement => { const BasicBar = bindWithProps(UnsafeVegaSpecStory); BasicBar.args = { + animations: false, data: barData, height: 500, maxWidth: 400, @@ -105,6 +106,7 @@ BasicBar.args = { const PackedBubbleChart = bindWithProps(UnsafeVegaSpecStory); PackedBubbleChart.args = { + animations: false, data: packedBubbleData, height: 500, maxWidth: 400, @@ -196,6 +198,7 @@ PackedBubbleChart.args = { const ContourPlot = bindWithProps(UnsafeVegaSpecStory); ContourPlot.args = { + animations: false, data: [ { name: 'source', diff --git a/src/stories/components/Animations/Animations.story.tsx b/src/stories/components/Animations/Animations.story.tsx new file mode 100644 index 000000000..a34ef6649 --- /dev/null +++ b/src/stories/components/Animations/Animations.story.tsx @@ -0,0 +1,428 @@ +import React, { ReactElement, useState } from 'react'; + +import { + MARK_ID, + TRENDLINE_VALUE +} from '@constants'; +import useChartProps from '@hooks/useChartProps'; +import { + Annotation, + Area, + Axis, + Bar, + Chart, + ChartPopover, + ChartProps, + ChartTooltip, + Legend, + Line, + Trendline +} from '@rsc'; +import { areaData, newDataArray1WithStaticPoints, stackedAreaData, workspaceTrendsData } from '@stories/data/data'; +import { StoryFn } from '@storybook/react'; +import { bindWithProps } from '@test-utils'; +import { ChartData, ChartElement, Datum, SpectrumColor } from 'types'; + +import { Button, Content, Text, View } from '@adobe/react-spectrum'; + +import { barData, barSubSeriesData, generateMockDataForTrellis, } from '../Bar/data'; + +export default { + title: 'RSC/Animations', +}; + +interface ToggleableDataProps { + initialData: ChartData[]; + secondaryData: ChartData[]; +} + +interface ChartWithToggleableDataProps extends ToggleableDataProps { + ChartComponent: ChartElement; +} + +const defaultChartProps: ChartProps = { data: [], minWidth: 400, maxWidth: 800, height: 400, animations: true }; + +const ChartWithToggleableData = ({ ChartComponent, initialData, secondaryData }: ChartWithToggleableDataProps) => { + const [dataSource, setDataSource] = useState(true); + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const { data, animations, ...remaingProps } = ChartComponent.props; + + const toggleDataSource = () => { + setDataSource(!dataSource); + }; + + const currentData = dataSource ? initialData : secondaryData; + + return ( +
+ + +
+ ); +}; + +const dialog = (item: Datum) => { + return ( + + + {item[MARK_ID]} + + + ); +} +const manipulateData = (data: number): number => { + const randomFactor = Math.random() * (1.25 - 0.75) + 0.75; + return Number((data * randomFactor).toFixed(1)); +}; + +const AreaStory: StoryFn = (args): ReactElement => { + const chartProps = useChartProps( defaultChartProps ); + return ( + + + {dialog} + {dialog} + +
+ } + {...args} + /> + ); +}; + +const StackedAreaStory: StoryFn = (args): ReactElement => { + const chartProps = useChartProps({ data: [], minWidth: 400, maxWidth: 800, height: 400 }); + return ( + + +
+ } + { ...args} + /> + ); +}; + +const SingleLineStory: StoryFn = (args): ReactElement => { + const chartProps = useChartProps(defaultChartProps); + return ( + + + {dialog} + {dialog} + +
+ } + {...args} + /> + ); +}; +const BarStory: StoryFn = (args): ReactElement => { + const chartProps = useChartProps(defaultChartProps); + return ( + + + + + {dialog} + {dialog} + + + + } + {...args} + /> + ); +}; + +const DodgedBarStory: StoryFn = (args): ReactElement => { + const colors = [ + ['#00a6a0', '#4bcec7'], + ['#575de8', '#8489fd'], + ['#d16100', '#fa8b1a'], + ]; + const chartProps = useChartProps({ ...defaultChartProps, colors }); + return ( + + + + + + {dialog} + {dialog} + + + + } + {...args} + /> + ); +}; + +const TrellisHorizontalBarStory: StoryFn = (args): ReactElement => { + const colors: SpectrumColor[] = [ + 'sequential-magma-200', + 'sequential-magma-400', + 'sequential-magma-600', + 'sequential-magma-800', + 'sequential-magma-1000', + 'sequential-magma-1200', + 'sequential-magma-1400', + ]; + + const chartProps = useChartProps({ ...defaultChartProps, colors }); + + return ( + + + + + {dialog} + {dialog} + + + + } + {...args} + /> + ); +}; + +const TrendlineStory: StoryFn = (args): ReactElement => { + const chartProps = useChartProps({ data: [], minWidth: 400, maxWidth: 800, height: 400 }); + return ( + + + + + + {dialog} + + {(item) => ( + <> +
Trendline value: {item[TRENDLINE_VALUE]}
+
Line value: {item.value}
+ + )} +
+
+
+ + + } + {...args} + /> + ); +}; + +const AreaSwitch = bindWithProps(AreaStory); +AreaSwitch.args = { + initialData: areaData, + secondaryData: areaData.map((data) => { + return { + ...data, + minTemperature: manipulateData(data.minTemperature), + maxTemperature: manipulateData(data.maxTemperature), + }; + }), +}; + +const AreaZero = bindWithProps(AreaStory); +AreaZero.args = { + initialData: areaData, + secondaryData: areaData.concat({ + datetime: 1668509200000, + minTemperature: 5, + maxTemperature: 32, + series: 'Add Fallout', + }), +}; + +const StackedAreaSwitch = bindWithProps(StackedAreaStory); +StackedAreaSwitch.args = { + initialData: stackedAreaData, + secondaryData: stackedAreaData.map((data) => { + return { + ...data, + value: manipulateData(data.value), + }; + }), +}; + +const SingleLineSwitch = bindWithProps(SingleLineStory); +SingleLineSwitch.args = { + initialData: newDataArray1WithStaticPoints, + secondaryData: newDataArray1WithStaticPoints.map((data) => { + return { + ...data, + y: manipulateData(data.y), + }; + }), +}; + +const SingleLineZero = bindWithProps(SingleLineStory); +SingleLineZero.args = { + initialData: newDataArray1WithStaticPoints, + secondaryData: newDataArray1WithStaticPoints.concat({ x: 16, y: 55, point: true }), +}; + +const TrendlineSwitch = bindWithProps(TrendlineStory); +TrendlineSwitch.args = { + initialData: workspaceTrendsData, + secondaryData: workspaceTrendsData.map((data) => { + return { + ...data, + value: manipulateData(data.value), + users: manipulateData(data.users), + }; + }), +}; + +const TrendlineZero = bindWithProps(TrendlineStory); +TrendlineZero.args = { + initialData: workspaceTrendsData, + secondaryData: workspaceTrendsData.concat( + { datetime: 1668410200000, point: 27, value: 648, users: 438, series: 'Add Fallout' }, + { datetime: 1668410200000, point: 27, value: 10932, users: 4913, series: 'Add Freeform table' }, + { datetime: 1668410200000, point: 27, value: 1932, users: 1413, series: 'Add Line viz' }, + { datetime: 1668410200000, point: 27, value: 6932, users: 3493, series: 'Add Bar viz' } + ), +}; + +const BarSwitch = bindWithProps(BarStory); +BarSwitch.args = { + initialData: barData, + secondaryData: barData.map((data) => { + return { + ...data, + downloads: manipulateData(data.downloads), + }; + }), +}; + +const BarZero = bindWithProps(BarStory); +BarZero.args = { + initialData: barData, + secondaryData: barData.concat({ browser: 'Opera', downloads: 10, percentLabel: '.01%' }), +}; + +const DodgedBarSwitch = bindWithProps(DodgedBarStory); +DodgedBarSwitch.args = { + initialData: barSubSeriesData, + secondaryData: barSubSeriesData.map((data) => { + return { + ...data, + value: manipulateData(data.value), + }; + }), +}; + +const DodgedBarZero = bindWithProps(DodgedBarStory); +DodgedBarZero.args = { + initialData: barSubSeriesData, + secondaryData: barSubSeriesData.concat([ + { + browser: 'Opera', + value: 5, + operatingSystem: 'Windows', + version: 'Current', + order: 2, + percentLabel: '71.4%', + }, + { browser: 'Opera', value: 3, operatingSystem: 'Mac', version: 'Current', order: 1, percentLabel: '42.9%' }, + { browser: 'Opera', value: 2, operatingSystem: 'Linux', version: 'Current', order: 0, percentLabel: '28.6%' }, + { + browser: 'Opera', + value: 2, + operatingSystem: 'Windows', + version: 'Previous', + order: 2, + percentLabel: '28.6%', + }, + { browser: 'Opera', value: 4, operatingSystem: 'Mac', version: 'Previous', order: 1, percentLabel: '57.1%' }, + { browser: 'Opera', value: 5, operatingSystem: 'Linux', version: 'Previous', order: 0, percentLabel: '71.4%' }, + ]), +}; + +const trellisData = generateMockDataForTrellis({ + property1: ['All users', 'Roku', 'Chromecast', 'Amazon Fire', 'Apple TV'], + property2: ['A. Sign up', 'B. Watch a video', 'C. Add to MyList'], + property3: ['1-5 times', '6-10 times', '11-15 times', '16-20 times', '21-25 times', '26+ times'], + propertyNames: ['segment', 'event', 'bucket'], + randomizeSteps: false, + orderBy: 'bucket', +}); +const TrellisHorizontalBarSwitch = bindWithProps(TrellisHorizontalBarStory); +TrellisHorizontalBarSwitch.args = { + initialData: trellisData, + secondaryData: trellisData.map((data) => { + return { + ...data, + value: manipulateData(data.value as number), + }; + }), +}; + +const TrellisHorizontalBarZero = bindWithProps(TrellisHorizontalBarStory); +TrellisHorizontalBarZero.args = { + initialData: trellisData, + secondaryData: generateMockDataForTrellis({ + property1: ['All users', 'Roku', 'Chromecast', 'Amazon Fire', 'Apple TV', 'LG Smart TV'], + property2: ['A. Sign up', 'B. Watch a video', 'C. Add to MyList'], + property3: ['1-5 times', '6-10 times', '11-15 times', '16-20 times', '21-25 times', '26+ times'], + propertyNames: ['segment', 'event', 'bucket'], + randomizeSteps: true, + orderBy: 'bucket', + }), +}; + +export { + AreaSwitch, + AreaZero, + StackedAreaSwitch, + SingleLineSwitch, + SingleLineZero, + BarSwitch, + BarZero, + DodgedBarSwitch, + DodgedBarZero, + TrendlineSwitch, + TrendlineZero, + TrellisHorizontalBarSwitch, + TrellisHorizontalBarZero, +}; diff --git a/src/stories/components/Animations/Animations.test.tsx b/src/stories/components/Animations/Animations.test.tsx new file mode 100644 index 000000000..910f4454e --- /dev/null +++ b/src/stories/components/Animations/Animations.test.tsx @@ -0,0 +1,109 @@ +import { findChart, render } from '@test-utils'; + +import { + AreaSwitch, + AreaZero, + BarSwitch, + BarZero, + DodgedBarSwitch, + DodgedBarZero, + SingleLineSwitch, + SingleLineZero, + TrellisHorizontalBarSwitch, + TrellisHorizontalBarZero, + TrendlineSwitch, + TrendlineZero, +} from './Animations.story'; + +describe('Animations', () => { + test('Area Switch renders properly', async () => { + render(); + const chart = await findChart(); + expect(chart).toBeInTheDocument(); + }); + + test('Area Zero renders properly', async () => { + render(); + const chart = await findChart(); + expect(chart).toBeInTheDocument(); + }); + + test('Single Line Switch renders properly', async () => { + render(); + const chart = await findChart(); + expect(chart).toBeInTheDocument(); + }); + + test('Single Line Zero renders properly', async () => { + render(); + const chart = await findChart(); + expect(chart).toBeInTheDocument(); + }); + + test('Trendline Switch renders properly', async () => { + render(); + const chart = await findChart(); + expect(chart).toBeInTheDocument(); + }); + + test('Tendline Zero renders properly', async () => { + render(); + const chart = await findChart(); + expect(chart).toBeInTheDocument(); + }); + + test('Bar Switch renders properly', async () => { + render(); + const chart = await findChart(); + expect(chart).toBeInTheDocument(); + }); + + test('Bar Zero renders properly', async () => { + render(); + const chart = await findChart(); + expect(chart).toBeInTheDocument(); + }); + + test('Dodged Bar Switch renders properly', async () => { + render(); + const chart = await findChart(); + expect(chart).toBeInTheDocument(); + }); + + test('Dodged Bar Zero renders properly', async () => { + render(); + const chart = await findChart(); + expect(chart).toBeInTheDocument(); + }); + + test('Trellis Horizontal Bar Switch renders properly', async () => { + render(); + const chart = await findChart(); + expect(chart).toBeInTheDocument(); + }); + + test('Trellis Horizontal Zero renders properly', async () => { + render(); + const chart = await findChart(); + expect(chart).toBeInTheDocument(); + + // const bars = await findAllMarksByGroupName(chart, 'bar0'); + // expect(bars.length).toEqual(90); + // click on bar and check where it rendered + // await clickNthElement(bars, 45); + // let popoverAnchor = await screen.findByTestId('rsc-popover-anchor'); + // expect(popoverAnchor).toHaveStyle('position: absolute'); + // expect(popoverAnchor).not.toHaveStyle('width: 85.60000000000002px'); + // expect(popoverAnchor).not.toHaveStyle('height: 8.421428571428521px'); + // expect(popoverAnchor).not.toHaveStyle('left: 316.5px'); + // expect(popoverAnchor).not.toHaveStyle('top: 352.30714285714294px'); + // await clickNthElement(bars, 45); + // await new Promise((r) => setTimeout(r, 3000)); + // await clickNthElement(bars, 45); + // const popoverAnchor = await screen.findByTestId('rsc-popover-anchor'); + // expect(popoverAnchor).toHaveStyle('width: 85.60000000000002px'); + // expect(popoverAnchor).toHaveStyle('height: 8.421428571428521px'); + // expect(popoverAnchor).toHaveStyle('left: 316.5px'); + // expect(popoverAnchor).toHaveStyle('top: 352.30714285714294px'); + }); +}); diff --git a/src/stories/components/Animations/AnimationsOpacity.story.tsx b/src/stories/components/Animations/AnimationsOpacity.story.tsx new file mode 100644 index 000000000..afede6adb --- /dev/null +++ b/src/stories/components/Animations/AnimationsOpacity.story.tsx @@ -0,0 +1,199 @@ +import React, { ReactElement, createElement } from 'react'; + +import { COLOR_SCALE, LINE_TYPE_SCALE, OPACITY_SCALE } from '@constants'; +import useChartProps from '@hooks/useChartProps'; +import { + Area, + Axis, + Bar, + Chart, + ChartColors, + ChartPopover, + ChartProps, + ChartTooltip, + Datum, + Legend, + LegendProps, + Line, + Scatter, + ScatterProps, + Title, +} from '@rsc'; +import { barSeriesData } from '@stories/components/Bar/data'; +import { browserData as data } from '@stories/data/data'; +import { characterData } from '@stories/data/marioKartData'; +import { StoryFn } from '@storybook/react'; +import { bindWithProps } from '@test-utils'; + +import { Content, Flex } from '@adobe/react-spectrum'; + +export default { + title: 'RSC/Animations/Opacity', +}; + +const defaultChartProps: ChartProps = { data: [], minWidth: 400, maxWidth: 800, height: 400, animations: true }; +const dialogContent = (datum) => ( + +
Operating system: {datum.series}
+
Browser: {datum.category}
+
Users: {datum.value}
+
+); + +const dialog = (item: Datum) => { + return ( + + +
{(item.character as string[]).join(', ')}
+
+ {marioKeyTitle.speedNormal}: {item.speedNormal} +
+
+ {marioKeyTitle.handlingNormal}: {item.handlingNormal} +
+
+
+ ); +}; + +type MarioDataKey = keyof (typeof characterData)[0]; + +const marioKeyTitle: Record, string> = { + weightClass: 'Weight class', + speedNormal: 'Speed (normal)', + speedAntigravity: 'Speed (antigravity)', + speedWater: 'Speed (water)', + speedAir: 'Speed (air)', + acceleration: 'Acceleration', + weight: 'Weight', + handlingNormal: 'Handling (normal)', + handlingAntigravity: 'Handling (antigravity)', + handlingWater: 'Handling (water)', + handlingAir: 'Handling (air)', + grip: 'Grip', + miniTurbo: 'Mini-turbo', +}; + +const areaStoryData = [ + { browser: 'Chrome', value: 5, operatingSystem: 'Windows', order: 2 }, + { browser: 'Chrome', value: 3, operatingSystem: 'Mac', order: 1 }, + { browser: 'Chrome', value: 2, operatingSystem: 'Other', order: 0 }, + { browser: 'Firefox', value: 3, operatingSystem: 'Windows', order: 2 }, + { browser: 'Firefox', value: 3, operatingSystem: 'Mac', order: 1 }, + { browser: 'Firefox', value: 1, operatingSystem: 'Other', order: 0 }, + { browser: 'Safari', value: 3, operatingSystem: 'Windows', order: 2 }, + { browser: 'Safari', value: 0, operatingSystem: 'Mac', order: 1 }, + { browser: 'Safari', value: 1, operatingSystem: 'Other', order: 0 }, +]; + +const getLegendProps = (args: ScatterProps): LegendProps => { + const facets = [COLOR_SCALE, LINE_TYPE_SCALE, OPACITY_SCALE, 'size']; + const legendKey = args[facets.find((key) => args[key] !== undefined) ?? COLOR_SCALE]; + const legendProps: LegendProps = { + position: 'right', + title: marioKeyTitle[legendKey], + }; + if (typeof args.opacity === 'object') { + legendProps.opacity = args.opacity; + } + return legendProps; +}; + +const AreaPopoverStory: StoryFn = (args): ReactElement => { + const chartProps = useChartProps({ data: areaStoryData, minWidth: 400, maxWidth: 800, height: 400, animations: args.animations }); + return ( + + + + + {dialogContent} + {dialogContent} + + + + ); +}; + +const DodgedBarPopoverStory: StoryFn = (args): ReactElement => { + const chartProps = useChartProps({ data: barSeriesData, width: 800, height: 600, animations: args.animations }); + return ( + + + + + {dialogContent} + {dialogContent} + + + + ); +}; + +const LineStory: StoryFn = (args): ReactElement => { + const chartProps = useChartProps({ ...defaultChartProps, data, animations: args.animations }); + return ( + + + + + {dialogContent} + + + + + ); +}; + +const ScatterStory: StoryFn = (args): ReactElement => { + const colors: ChartColors = args.colorScaleType === 'linear' ? 'sequentialViridis5' : 'categorical16'; + const chartProps = useChartProps({ + data: characterData, + height: 500, + width: 500, + lineWidths: [1, 2, 3], + colors, + animations: args.animations, + }); + const legendProps = getLegendProps(args); + + return ( + + + + + + + </Chart> + ); +}; + +const AreaPopover = bindWithProps(AreaPopoverStory); +AreaPopover.args = { + dimension: 'browser', + color: 'operatingSystem', + scaleType: 'point', + animations: true +}; + +const BarPopover = bindWithProps(DodgedBarPopoverStory); +BarPopover.args = { + type: 'dodged', + dimension: 'browser', + order: 'order', + color: 'operatingSystem', + animations: true +}; + +const LineChart = bindWithProps(LineStory); +LineChart.args = { children: dialogContent, animations: true }; + +const ScatterPopover = bindWithProps(ScatterStory); +ScatterPopover.args = { + color: 'weightClass', + dimension: 'speedNormal', + metric: 'handlingNormal', + children: [createElement(ChartTooltip, {}, dialog), createElement(ChartPopover, { width: 200, animations:true }, dialog)], + animations: true +}; + +export { AreaPopover, BarPopover, LineChart, ScatterPopover }; diff --git a/src/stories/components/Animations/AnimationsOpacity.test.tsx b/src/stories/components/Animations/AnimationsOpacity.test.tsx new file mode 100644 index 000000000..f0002d520 --- /dev/null +++ b/src/stories/components/Animations/AnimationsOpacity.test.tsx @@ -0,0 +1,34 @@ +import { findChart, render } from '@test-utils'; +import { + AreaPopover, + BarPopover, + LineChart, + ScatterPopover +} from '@stories/components/Animations/AnimationsOpacity.story'; + +describe('Opacity Animations', () => { + test('Area Popover renders properly', async () => { + render(<AreaPopover {...AreaPopover.args} />); + const chart = await findChart(); + expect(chart).toBeInTheDocument(); + }); + + test('Bar Chart renders properly', async () => { + render(<BarPopover {...BarPopover.args}/>); + const chart = await findChart(); + expect(chart).toBeInTheDocument(); + }) + + test('Line Chart renders properly', async () => { + render(<LineChart {...LineChart.args}/>); + const chart = await findChart(); + expect(chart).toBeInTheDocument(); + }); + + test('Scatter renders properly', async () => { + render(<ScatterPopover {...ScatterPopover.args} />); + const chart = await findChart(); + expect(chart).toBeInTheDocument(); + }); + +}); \ No newline at end of file diff --git a/src/stories/components/Annotation/Annotation.story.tsx b/src/stories/components/Annotation/Annotation.story.tsx index b3197dbca..5c5977862 100644 --- a/src/stories/components/Annotation/Annotation.story.tsx +++ b/src/stories/components/Annotation/Annotation.story.tsx @@ -44,7 +44,7 @@ const data = [ const barArgs: BarProps = { dimension: 'browser', order: 'order', color: 'operatingSystem' }; const BarAnnotationStory: StoryFn<typeof Annotation> = (args): ReactElement => { - const chartProps = useChartProps({ data: data, width: 600, height: 600 }); + const chartProps = useChartProps({ animations: false, data: data, width: 600, height: 600 }); return ( <Chart {...chartProps}> <Bar {...barArgs}> diff --git a/src/stories/components/Area/Area.story.tsx b/src/stories/components/Area/Area.story.tsx index 4fb162823..c9e0163e3 100644 --- a/src/stories/components/Area/Area.story.tsx +++ b/src/stories/components/Area/Area.story.tsx @@ -15,6 +15,7 @@ import useChartProps from '@hooks/useChartProps'; import { Area, Axis, Chart, ChartProps } from '@rsc'; import { StoryFn } from '@storybook/react'; import { bindWithProps } from 'test-utils/bindWithProps'; +import { areaData } from '@stories/data/data'; export default { title: 'RSC/Area', @@ -28,17 +29,7 @@ export default { }, }; -const data = [ - { datetime: 1667890800000, maxTemperature: 73, minTemperature: 47, series: 'Add Fallout' }, - { datetime: 1667977200000, maxTemperature: 70, minTemperature: 48, series: 'Add Fallout' }, - { datetime: 1668063600000, maxTemperature: 73, minTemperature: 48, series: 'Add Fallout' }, - { datetime: 1668150000000, maxTemperature: 56, minTemperature: 31, series: 'Add Fallout' }, - { datetime: 1668236400000, maxTemperature: 41, minTemperature: 18, series: 'Add Fallout' }, - { datetime: 1668322800000, maxTemperature: 60, minTemperature: 45, series: 'Add Fallout' }, - { datetime: 1668409200000, maxTemperature: 64, minTemperature: 43, series: 'Add Fallout' }, -]; - -const defaultChartProps: ChartProps = { data, minWidth: 400, maxWidth: 800, height: 400 }; +const defaultChartProps: ChartProps = { data: areaData, minWidth: 400, maxWidth: 800, height: 400}; const BasicStory: StoryFn<typeof Area> = (args): ReactElement => { const chartProps = useChartProps({ ...defaultChartProps }); diff --git a/src/stories/components/Area/StackedArea.story.tsx b/src/stories/components/Area/StackedArea.story.tsx index 11f2a34d4..9cd391266 100644 --- a/src/stories/components/Area/StackedArea.story.tsx +++ b/src/stories/components/Area/StackedArea.story.tsx @@ -15,6 +15,7 @@ import useChartProps from '@hooks/useChartProps'; import { Area, Axis, Chart, ChartPopover, ChartProps, ChartTooltip, Legend } from '@rsc'; import { StoryFn } from '@storybook/react'; import { bindWithProps } from 'test-utils/bindWithProps'; +import { stackedAreaData } from '@stories/data/data'; export default { title: 'RSC/Area/StackedArea', @@ -28,18 +29,7 @@ export default { }, }; -const data = [ - { browser: 'Chrome', value: 5, operatingSystem: 'Windows', order: 2 }, - { browser: 'Chrome', value: 3, operatingSystem: 'Mac', order: 1 }, - { browser: 'Chrome', value: 2, operatingSystem: 'Other', order: 0 }, - { browser: 'Firefox', value: 3, operatingSystem: 'Windows', order: 2 }, - { browser: 'Firefox', value: 3, operatingSystem: 'Mac', order: 1 }, - { browser: 'Firefox', value: 1, operatingSystem: 'Other', order: 0 }, - { browser: 'Safari', value: 3, operatingSystem: 'Windows', order: 2 }, - { browser: 'Safari', value: 0, operatingSystem: 'Mac', order: 1 }, - { browser: 'Safari', value: 1, operatingSystem: 'Other', order: 0 }, -]; -const defaultChartProps: ChartProps = { data, minWidth: 400, maxWidth: 800, height: 400 }; +const defaultChartProps: ChartProps = { data: stackedAreaData, minWidth: 400, maxWidth: 800, height: 400, animations: false }; const AreaStory: StoryFn<typeof Area> = (args): ReactElement => { const chartProps = useChartProps(defaultChartProps); diff --git a/src/stories/components/Axis/Axis.story.tsx b/src/stories/components/Axis/Axis.story.tsx index 70f9e6b34..085ed0983 100644 --- a/src/stories/components/Axis/Axis.story.tsx +++ b/src/stories/components/Axis/Axis.story.tsx @@ -46,7 +46,7 @@ const data = [ ]; const AxisStory: StoryFn<typeof Axis> = (args): ReactElement => { - const chartProps = useChartProps({ data, width: 600 }); + const chartProps = useChartProps({ animations: false, data, width: 600 }); return ( <Chart {...chartProps}> <Axis {...args} /> @@ -55,7 +55,7 @@ const AxisStory: StoryFn<typeof Axis> = (args): ReactElement => { }; const TimeAxisStory: StoryFn<typeof Axis> = (args): ReactElement => { - const chartProps = useChartProps({ data: timeData[args.granularity ?? DEFAULT_GRANULARITY], width: 'auto' }); + const chartProps = useChartProps({animations: false, data: timeData[args.granularity ?? DEFAULT_GRANULARITY], width: 'auto' }); return ( <Chart {...chartProps}> <Axis {...args} /> @@ -65,7 +65,7 @@ const TimeAxisStory: StoryFn<typeof Axis> = (args): ReactElement => { }; const SubLabelStory: StoryFn<typeof Axis> = (args): ReactElement => { - const chartProps = useChartProps({ data: barData, width: 600 }); + const chartProps = useChartProps({ animations: false, data: barData, width: 600 }); return ( <Chart {...chartProps}> <Axis {...args} /> @@ -75,7 +75,7 @@ const SubLabelStory: StoryFn<typeof Axis> = (args): ReactElement => { }; const TruncatedLabelStory: StoryFn<typeof Axis> = (args): ReactElement => { - const chartProps = useChartProps({ data: barDataLongLabels, width: 450 }); + const chartProps = useChartProps({ animations: false, data: barDataLongLabels, width: 450 }); return ( <Chart {...chartProps}> <Axis {...args} /> @@ -85,7 +85,7 @@ const TruncatedLabelStory: StoryFn<typeof Axis> = (args): ReactElement => { }; const LinearAxisStory: StoryFn<typeof Axis> = (args): ReactElement => { - const chartProps = useChartProps({ data: workspaceTrendsData, width: 600 }); + const chartProps = useChartProps({ animations: false, data: workspaceTrendsData, width: 600 }); return ( <Chart {...chartProps}> <Axis position="left" grid title="Users" /> @@ -96,7 +96,7 @@ const LinearAxisStory: StoryFn<typeof Axis> = (args): ReactElement => { }; const DurationStory: StoryFn<typeof Axis> = (args): ReactElement => { - const chartProps = useChartProps({ data: workspaceTrendsData, width: 600 }); + const chartProps = useChartProps({ animations: false, data: workspaceTrendsData, width: 600 }); return ( <Chart {...chartProps}> <Axis {...args} /> @@ -107,7 +107,7 @@ const DurationStory: StoryFn<typeof Axis> = (args): ReactElement => { }; const NonLinearAxisStory: StoryFn<typeof Axis> = (args): ReactElement => { - const chartProps = useChartProps({ data: workspaceTrendsData, width: 600 }); + const chartProps = useChartProps({ animations: false, data: workspaceTrendsData, width: 600 }); return ( <Chart {...chartProps}> <Axis position="bottom" ticks baseline labelFormat="time" /> @@ -118,7 +118,7 @@ const NonLinearAxisStory: StoryFn<typeof Axis> = (args): ReactElement => { }; const SparkLineStory: StoryFn<typeof Axis> = (args): ReactElement => { - const chartProps = useChartProps({ data: stockPriceData, width: 200, height: 100 }); + const chartProps = useChartProps({ animations: false, data: stockPriceData, width: 200, height: 100 }); return ( <Chart {...chartProps}> <Axis {...args} /> diff --git a/src/stories/components/Axis/AxisLabels.story.tsx b/src/stories/components/Axis/AxisLabels.story.tsx index 9643a9f25..c41063fc8 100644 --- a/src/stories/components/Axis/AxisLabels.story.tsx +++ b/src/stories/components/Axis/AxisLabels.story.tsx @@ -29,7 +29,7 @@ const data = [ ]; const AxisLabelStory: StoryFn<typeof Axis> = (args): ReactElement => { - const chartProps = useChartProps({ data, width: 600 }); + const chartProps = useChartProps({ animations: false, data, width: 600 }); return ( <Chart {...chartProps}> <Axis {...args}></Axis> diff --git a/src/stories/components/Axis/AxisReferenceLine.story.tsx b/src/stories/components/Axis/AxisReferenceLine.story.tsx index ca3511746..d063475cc 100644 --- a/src/stories/components/Axis/AxisReferenceLine.story.tsx +++ b/src/stories/components/Axis/AxisReferenceLine.story.tsx @@ -28,7 +28,7 @@ const data = [ ]; const ReferenceLineStory: StoryFn<typeof ReferenceLine> = (args): ReactElement => { - const chartProps = useChartProps({ data, width: 600 }); + const chartProps = useChartProps({ animations: false, data, width: 600 }); return ( <Chart {...chartProps}> <Axis position="bottom" baseline ticks> diff --git a/src/stories/components/AxisAnnotation/AxisAnnotation.story.tsx b/src/stories/components/AxisAnnotation/AxisAnnotation.story.tsx index 9b9b902a9..5099295c2 100644 --- a/src/stories/components/AxisAnnotation/AxisAnnotation.story.tsx +++ b/src/stories/components/AxisAnnotation/AxisAnnotation.story.tsx @@ -56,7 +56,7 @@ const popoverContent = (datum) => ( </Content> ); -const defaultChartLineProps: ChartProps = { data: annotationAxisData, minWidth: 400, maxWidth: 800, height: 400 }; +const defaultChartLineProps: ChartProps = { animations: false, data: annotationAxisData, minWidth: 400, maxWidth: 800, height: 400 }; const BasicAxisAnnotationStory: StoryFn<typeof AxisAnnotation> = (args): ReactElement => { const chartProps = useChartProps(defaultChartLineProps); diff --git a/src/stories/components/Bar/Bar.story.tsx b/src/stories/components/Bar/Bar.story.tsx index c58b088a6..171d12f22 100644 --- a/src/stories/components/Bar/Bar.story.tsx +++ b/src/stories/components/Bar/Bar.story.tsx @@ -25,7 +25,7 @@ export default { }; const BarStory: StoryFn<typeof Bar> = (args): ReactElement => { - const chartProps = useChartProps({ data: barData, width: 600, height: 600 }); + const chartProps = useChartProps({ animations: false, data: barData, width: 600, height: 600 }); return ( <Chart {...chartProps}> <Axis position={args.orientation === 'horizontal' ? 'left' : 'bottom'} baseline title="Browser" /> diff --git a/src/stories/components/Bar/DodgedBar.story.tsx b/src/stories/components/Bar/DodgedBar.story.tsx index 07633976f..cd97f99e0 100644 --- a/src/stories/components/Bar/DodgedBar.story.tsx +++ b/src/stories/components/Bar/DodgedBar.story.tsx @@ -36,7 +36,7 @@ const DodgedBarStory: StoryFn<typeof Bar> = (args): ReactElement => { ] : categorical6; const data = Array.isArray(color) ? barSubSeriesData : barSeriesData; - const chartProps = useChartProps({ data, width: 800, height: 600, colors }); + const chartProps = useChartProps({ animations: false, data, width: 800, height: 600, colors }); return ( <Chart {...chartProps}> <Axis position={args.orientation === 'horizontal' ? 'left' : 'bottom'} baseline title="Browser" /> @@ -56,7 +56,7 @@ const dialogContent = (datum) => ( ); const DodgedBarPopoverStory: StoryFn<typeof Bar> = (args): ReactElement => { - const chartProps = useChartProps({ data: barSeriesData, width: 800, height: 600 }); + const chartProps = useChartProps({ animations: false, data: barSeriesData, width: 800, height: 600 }); return ( <Chart {...chartProps}> <Axis position={args.orientation === 'horizontal' ? 'left' : 'bottom'} baseline title="Browser" /> @@ -71,7 +71,7 @@ const DodgedBarPopoverStory: StoryFn<typeof Bar> = (args): ReactElement => { }; const DodgedBarLineTypeStory: StoryFn<typeof Bar> = (args): ReactElement => { - const chartProps = useChartProps({ data: barSeriesData, width: 800, height: 600 }); + const chartProps = useChartProps({ animations: false, data: barSeriesData, width: 800, height: 600 }); return ( <Chart {...chartProps}> <Axis position={args.orientation === 'horizontal' ? 'left' : 'bottom'} baseline title="Browser" /> diff --git a/src/stories/components/Bar/ReferenceLineBar.story.tsx b/src/stories/components/Bar/ReferenceLineBar.story.tsx index 9b80965ba..19c22b642 100644 --- a/src/stories/components/Bar/ReferenceLineBar.story.tsx +++ b/src/stories/components/Bar/ReferenceLineBar.story.tsx @@ -32,7 +32,7 @@ const data = [ ]; const ReferenceLineStory: StoryFn<typeof ReferenceLine> = (args): ReactElement => { - const chartProps = useChartProps({ data, width: 600 }); + const chartProps = useChartProps({ animations: false, data, width: 600 }); return ( <Chart {...chartProps}> <Axis position="bottom" baseline ticks> @@ -44,7 +44,7 @@ const ReferenceLineStory: StoryFn<typeof ReferenceLine> = (args): ReactElement = }; const ReferenceLineHorizontalStory: StoryFn<typeof ReferenceLine> = (args): ReactElement => { - const chartProps = useChartProps({ data, width: 600 }); + const chartProps = useChartProps({ animations: false, data, width: 600 }); return ( <Chart {...chartProps}> <Axis position="left" baseline ticks> diff --git a/src/stories/components/Bar/StackedBar.story.tsx b/src/stories/components/Bar/StackedBar.story.tsx index fca160c45..6c1a84ce8 100644 --- a/src/stories/components/Bar/StackedBar.story.tsx +++ b/src/stories/components/Bar/StackedBar.story.tsx @@ -33,7 +33,7 @@ const colors: SpectrumColor[] = [ ]; const BarStory: StoryFn<typeof Bar> = (args): ReactElement => { - const chartProps = useChartProps({ data: barSeriesData, colors, width: 800, height: 600 }); + const chartProps = useChartProps({ animations: false, data: barSeriesData, colors, width: 800, height: 600 }); return ( <Chart {...chartProps}> <Axis position={args.orientation === 'horizontal' ? 'left' : 'bottom'} baseline title="Browser" /> @@ -45,7 +45,7 @@ const BarStory: StoryFn<typeof Bar> = (args): ReactElement => { }; const NegativeBarStory: StoryFn<typeof Bar> = (args): ReactElement => { - const chartProps = useChartProps({ data: negativeBarSeriesData, width: 800, height: 600 }); + const chartProps = useChartProps({ animations: false, data: negativeBarSeriesData, width: 800, height: 600 }); return ( <Chart {...chartProps}> <Axis position={args.orientation === 'horizontal' ? 'left' : 'bottom'} baseline title="Browser" /> diff --git a/src/stories/components/Bar/TrellisBar.story.tsx b/src/stories/components/Bar/TrellisBar.story.tsx index bca57877a..4e62c1952 100644 --- a/src/stories/components/Bar/TrellisBar.story.tsx +++ b/src/stories/components/Bar/TrellisBar.story.tsx @@ -37,7 +37,7 @@ const colors: SpectrumColor[] = [ 'sequential-magma-1400', ]; -const BarStory: StoryFn<typeof Bar> = (args: BarProps): ReactElement => { +const BarStory: StoryFn<typeof Bar> = (args: Partial<BarProps>): ReactElement => { const chartProps = useChartProps({ data: generateMockDataForTrellis({ property1: ['All users', 'Roku', 'Chromecast', 'Amazon Fire', 'Apple TV'], @@ -75,8 +75,9 @@ const BarStory: StoryFn<typeof Bar> = (args: BarProps): ReactElement => { ); }; -const Dodged = bindWithProps<BarProps>(BarStory); +const Dodged = bindWithProps<Partial<BarProps>>(BarStory); Dodged.args = { + animations: false, type: 'dodged', dimension: 'segment', order: 'order', @@ -89,6 +90,7 @@ Dodged.args = { const HorizontalBarHorizontalTrellis = bindWithProps<BarProps>(BarStory); HorizontalBarHorizontalTrellis.storyName = 'Horizontal Bar, Horizontal Trellis'; HorizontalBarHorizontalTrellis.args = { + animations: false, type: 'stacked', trellis: 'event', dimension: 'segment', @@ -102,6 +104,7 @@ const HorizontalBarVerticalTrellis = bindWithProps<BarProps>(BarStory); HorizontalBarVerticalTrellis.storyName = 'Horizontal Bar, Vertical Trellis'; HorizontalBarVerticalTrellis.args = { ...HorizontalBarHorizontalTrellis.args, + animations: false, trellisOrientation: 'vertical', }; @@ -109,6 +112,7 @@ const VerticalBarHorizontalTrellis = bindWithProps<BarProps>(BarStory); VerticalBarHorizontalTrellis.storyName = 'Vertical Bar, Horizontal Trellis'; VerticalBarHorizontalTrellis.args = { ...HorizontalBarHorizontalTrellis.args, + animations: false, orientation: 'vertical', trellisOrientation: 'horizontal', }; @@ -117,6 +121,7 @@ const VerticalBarVerticalTrellis = bindWithProps<BarProps>(BarStory); VerticalBarVerticalTrellis.storyName = 'Vertical Bar, Vertical Trellis'; VerticalBarVerticalTrellis.args = { ...HorizontalBarVerticalTrellis.args, + animations: false, orientation: 'vertical', trellisOrientation: 'vertical', }; @@ -124,6 +129,7 @@ VerticalBarVerticalTrellis.args = { const WithCustomTrellisPadding = bindWithProps<BarProps>(BarStory); WithCustomTrellisPadding.args = { ...HorizontalBarVerticalTrellis.args, + animations: false, orientation: 'vertical', trellisPadding: 0.33, }; diff --git a/src/stories/components/Bar/TrellisBar.test.tsx b/src/stories/components/Bar/TrellisBar.test.tsx index 5a8f84415..c25b3036a 100644 --- a/src/stories/components/Bar/TrellisBar.test.tsx +++ b/src/stories/components/Bar/TrellisBar.test.tsx @@ -33,7 +33,7 @@ import { describe('TrellisBar', () => { test('Dodged renders properly', async () => { - render(<Dodged {...Dodged.args} />); + render(<Dodged {...Dodged.args } animations={false} />); const chart = await findChart(); expect(chart).toBeInTheDocument(); diff --git a/src/stories/components/ChartPopover/ChartPopover.story.tsx b/src/stories/components/ChartPopover/ChartPopover.story.tsx index 072d2f030..80cdd0efc 100644 --- a/src/stories/components/ChartPopover/ChartPopover.story.tsx +++ b/src/stories/components/ChartPopover/ChartPopover.story.tsx @@ -40,10 +40,10 @@ const dialogContent = (datum) => ( </Content> ); -const defaultChartProps: ChartProps = { data, renderer: 'svg', width: 600 }; +const defaultChartProps: ChartProps = {animations: false, data, renderer: 'svg', width: 600 }; const ChartPopoverCanvasStory: StoryFn<typeof ChartPopover> = (args): ReactElement => { - const chartProps = useChartProps({ data, renderer: 'canvas', width: 600 }); + const chartProps = useChartProps({ animations: false, data, renderer: 'canvas', width: 600 }); return ( <Chart {...chartProps}> <Bar color="series"> diff --git a/src/stories/components/ChartTooltip/ChartTooltip.story.tsx b/src/stories/components/ChartTooltip/ChartTooltip.story.tsx index 77d8cd792..f5dce037b 100644 --- a/src/stories/components/ChartTooltip/ChartTooltip.story.tsx +++ b/src/stories/components/ChartTooltip/ChartTooltip.story.tsx @@ -33,7 +33,7 @@ export default { }; const StackedBarTooltipStory: StoryFn<typeof ChartTooltip> = (args): ReactElement => { - const chartProps = useChartProps({ data, width: 600 }); + const chartProps = useChartProps({ animations: false, data, width: 600 }); return ( <Chart {...chartProps}> <Bar color="series"> @@ -43,7 +43,7 @@ const StackedBarTooltipStory: StoryFn<typeof ChartTooltip> = (args): ReactElemen ); }; const DodgedBarTooltipStory: StoryFn<typeof ChartTooltip> = (args): ReactElement => { - const chartProps = useChartProps({ data, width: 600 }); + const chartProps = useChartProps({ animations: false, data, width: 600 }); return ( <Chart {...chartProps}> <Bar type="dodged" color="series"> @@ -71,9 +71,9 @@ const lineData = [ ]; const LineTooltipStory: StoryFn<typeof ChartTooltip> = (args): ReactElement => { - const chartProps = useChartProps({ data: lineData, width: 600 }); + const chartProps = useChartProps({ animations: false, data: lineData, width: 600 }); return ( - <Chart {...chartProps}> + <Chart {...chartProps} animations={false}> <Line color="series"> <ChartTooltip {...args} /> </Line> @@ -91,7 +91,7 @@ interface LineData extends Datum { } const AreaTooltipStory: StoryFn<typeof ChartTooltip> = (args): ReactElement => { - const chartProps = useChartProps({ data: lineData, width: 600 }); + const chartProps = useChartProps({ animations: false, data: lineData, width: 600 }); return ( <Chart {...chartProps}> <Area> diff --git a/src/stories/components/Donut/Donut.story.tsx b/src/stories/components/Donut/Donut.story.tsx index fb3e0b7a7..cb26b45e3 100644 --- a/src/stories/components/Donut/Donut.story.tsx +++ b/src/stories/components/Donut/Donut.story.tsx @@ -27,7 +27,7 @@ export default { }; const DonutStory: StoryFn<typeof Donut> = (args): ReactElement => { - const chartProps = useChartProps({ data: basicDonutData, width: 350, height: 350 }); + const chartProps = useChartProps({ animations: false, data: basicDonutData, width: 350, height: 350 }); return ( <Chart {...chartProps}> <Donut {...args} /> diff --git a/src/stories/components/Legend/Legend.story.tsx b/src/stories/components/Legend/Legend.story.tsx index ee179147e..0741a8e56 100644 --- a/src/stories/components/Legend/Legend.story.tsx +++ b/src/stories/components/Legend/Legend.story.tsx @@ -65,6 +65,7 @@ Title.args = { title: 'Operating system', ...defaultProps }; const Supreme = LegendBarStory.bind({}); Supreme.args = { + animations: false, descriptions, highlight: true, legendLabels, diff --git a/src/stories/components/Legend/LegendHideShow.story.tsx b/src/stories/components/Legend/LegendHideShow.story.tsx index 51f6a999a..3d234f421 100644 --- a/src/stories/components/Legend/LegendHideShow.story.tsx +++ b/src/stories/components/Legend/LegendHideShow.story.tsx @@ -19,7 +19,12 @@ export default { }; const DefaultHiddenSeries = LegendBarStory.bind({}); -DefaultHiddenSeries.args = { defaultHiddenSeries: ['Other'], isToggleable: true, highlight: true, ...defaultProps }; +DefaultHiddenSeries.args = { + defaultHiddenSeries: ['Other'], + isToggleable: true, + highlight: true, + ...defaultProps, +}; DefaultHiddenSeries.storyName = 'Default Hidden Series (uncontrolled)'; const HiddenSeries = LegendBarHiddenSeriesStory.bind({}); diff --git a/src/stories/components/Legend/LegendStoryUtils.tsx b/src/stories/components/Legend/LegendStoryUtils.tsx index 796bf9360..f298943ab 100644 --- a/src/stories/components/Legend/LegendStoryUtils.tsx +++ b/src/stories/components/Legend/LegendStoryUtils.tsx @@ -17,7 +17,7 @@ import { browserData as data } from '@stories/data/data'; import { StoryFn } from '@storybook/react'; export const LegendBarStory: StoryFn<typeof Legend> = (args): ReactElement => { - const chartProps = useChartProps({ data, width: 700 }); + const chartProps = useChartProps({ animations: false, data, width: 700 }); return ( <Chart {...chartProps}> <Bar color="series" /> @@ -29,7 +29,7 @@ export const LegendBarStory: StoryFn<typeof Legend> = (args): ReactElement => { }; export const LegendBarHighlightedSeriesStory: StoryFn<typeof Legend> = (args): ReactElement => { - const chartProps = useChartProps({ data, width: 700, highlightedSeries: 'Mac' }); + const chartProps = useChartProps({ animations: false, data, width: 700, highlightedSeries: 'Mac' }); return ( <Chart {...chartProps}> <Bar color="series" /> @@ -41,7 +41,7 @@ export const LegendBarHighlightedSeriesStory: StoryFn<typeof Legend> = (args): R }; export const LegendBarHiddenSeriesStory: StoryFn<typeof Legend> = (args): ReactElement => { - const chartProps = useChartProps({ data, width: 700, hiddenSeries: ['Mac'] }); + const chartProps = useChartProps({ animations: false, data, width: 700, hiddenSeries: ['Mac'] }); return ( <Chart {...chartProps}> <Bar color="series" /> @@ -53,7 +53,7 @@ export const LegendBarHiddenSeriesStory: StoryFn<typeof Legend> = (args): ReactE }; export const LegendDisconnectedStory: StoryFn<typeof Legend> = (args): ReactElement => { - const chartProps = useChartProps({ data, width: 700, height: 50 }); + const chartProps = useChartProps({ animations: false, data, width: 700, height: 50 }); return ( <Chart {...chartProps}> <Legend {...args} /> @@ -62,5 +62,6 @@ export const LegendDisconnectedStory: StoryFn<typeof Legend> = (args): ReactElem }; export const defaultProps: LegendProps = { + animations: false, onClick: undefined, }; diff --git a/src/stories/components/Legend/LegendSymbol.story.tsx b/src/stories/components/Legend/LegendSymbol.story.tsx index 837e7f861..5fc3a7f39 100644 --- a/src/stories/components/Legend/LegendSymbol.story.tsx +++ b/src/stories/components/Legend/LegendSymbol.story.tsx @@ -25,7 +25,7 @@ export default { }; const LegendBarStory: StoryFn<typeof Legend> = (args): ReactElement => { - const chartProps = useChartProps({ data, width: 700, symbolShapes: ['square', 'triangle', ROUNDED_SQUARE_PATH] }); + const chartProps = useChartProps({ animations: false, data, width: 700, symbolShapes: ['square', 'triangle', ROUNDED_SQUARE_PATH] }); return ( <Chart {...chartProps}> <Bar color="series" /> @@ -34,7 +34,7 @@ const LegendBarStory: StoryFn<typeof Legend> = (args): ReactElement => { ); }; const LegendLineStory: StoryFn<typeof Legend> = (args): ReactElement => { - const chartProps = useChartProps({ data, width: 700 }); + const chartProps = useChartProps({ animations: false, data, width: 700 }); return ( <Chart {...chartProps}> <Line dimension="category" lineType="series" color="series" scaleType="point" /> diff --git a/src/stories/components/Line/Line.story.tsx b/src/stories/components/Line/Line.story.tsx index 5086a72ef..f6cf834b1 100644 --- a/src/stories/components/Line/Line.story.tsx +++ b/src/stories/components/Line/Line.story.tsx @@ -14,7 +14,7 @@ import React, { ReactElement, createElement } from 'react'; import { ReferenceLine } from '@components/ReferenceLine'; import useChartProps from '@hooks/useChartProps'; import { Axis, Bar, Chart, ChartPopover, ChartTooltip, Legend, Line } from '@rsc'; -import { workspaceTrendsData, workspaceTrendsDataWithVisiblePoints } from '@stories/data/data'; +import { newDataArray1, workspaceTrendsData, workspaceTrendsDataWithVisiblePoints } from '@stories/data/data'; import { formatTimestamp } from '@stories/storyUtils'; import { StoryFn } from '@storybook/react'; import { bindWithProps } from '@test-utils'; @@ -56,7 +56,28 @@ const historicalCompareData = [ { datetime: 1668409200000, users: 4913, series: 'Add Freeform table', period: 'Current' }, ]; -const defaultChartProps: ChartProps = { data: workspaceTrendsData, minWidth: 400, maxWidth: 800, height: 400 }; +const defaultChartProps: ChartProps = { + data: workspaceTrendsData, + minWidth: 400, + maxWidth: 800, + height: 400, +}; + +const singleLineChartProps: ChartProps = { + data: newDataArray1, + minWidth: 400, + maxWidth: 800, + height: 40, +}; + +const SingleLineStory: StoryFn<typeof Line> = (args): ReactElement => { + const chartProps = useChartProps(singleLineChartProps); + return ( + <Chart {...chartProps}> + <Line {...args} /> + </Chart> + ); +}; const BasicLineStory: StoryFn<typeof Line> = (args): ReactElement => { const chartProps = useChartProps(defaultChartProps); @@ -126,7 +147,7 @@ const HistoricalCompareStory: StoryFn<typeof Line> = (args): ReactElement => { const LineWithVisiblePointsStory: StoryFn<typeof Line> = (args): ReactElement => { const chartProps = useChartProps({ ...defaultChartProps, data: workspaceTrendsDataWithVisiblePoints }); return ( - <Chart {...chartProps}> + <Chart {...chartProps} animations={false}> <Axis position="left" grid title="Users" /> <Axis position="bottom" labelFormat="time" baseline ticks /> <Line {...args} /> @@ -232,6 +253,13 @@ WithStaticPointsAndDialogs.args = { children: [createElement(ChartTooltip, {}, dialogCallback), createElement(ChartPopover, {}, dialogCallback)], }; +const SingleLine = bindWithProps(SingleLineStory); +SingleLine.args = { + dimension: 'x', + metric: 'y', + scaleType: 'linear', +}; + export { Basic, LineWithAxisAndLegend, @@ -243,4 +271,5 @@ export { Tooltip, WithStaticPoints, WithStaticPointsAndDialogs, + SingleLine, }; diff --git a/src/stories/components/Line/Line.test.tsx b/src/stories/components/Line/Line.test.tsx index c08237505..bc73c489a 100644 --- a/src/stories/components/Line/Line.test.tsx +++ b/src/stories/components/Line/Line.test.tsx @@ -13,7 +13,7 @@ import React from 'react'; import { HIGHLIGHT_CONTRAST_RATIO } from '@constants'; import '@matchMediaMock'; -import { Line } from '@rsc'; +import { Chart, Line } from '@rsc'; import { allElementsHaveAttributeValue, clickNthElement, @@ -39,8 +39,9 @@ import { Tooltip, TrendScale, WithStaticPoints, - WithStaticPointsAndDialogs, + WithStaticPointsAndDialogs } from './Line.story'; +import { newDataArray1, newDataArray2 } from '@stories/data/data'; describe('Line', () => { // Line is not a real React component. This is test just provides test coverage for sonarqube @@ -186,6 +187,7 @@ describe('Line', () => { const lines = await findAllMarksByGroupName(chart, 'line0'); expect(lines).toHaveLength(4); + expect(lines[0]).toHaveAttribute('opacity', '1'); expect(lines[1]).toHaveAttribute('opacity', '1'); @@ -365,4 +367,20 @@ describe('Line', () => { expect(point.getAttribute('stroke-width')).toEqual('2'); }); }); + + describe('Line animation tests', () => { + test('Line animates between congruent datasets', async () => { + const chartProps = { + data: newDataArray1 + } + render( + <Chart {...chartProps}> + <Line dimension="x" metric="y"/> + </Chart> + ); + setTimeout(() => { + chartProps.data = newDataArray2; + }, 3000); + }); + }) }); diff --git a/src/stories/components/MetricRange/MetricRange.story.tsx b/src/stories/components/MetricRange/MetricRange.story.tsx index 1da1982d5..100d84c2f 100644 --- a/src/stories/components/MetricRange/MetricRange.story.tsx +++ b/src/stories/components/MetricRange/MetricRange.story.tsx @@ -25,6 +25,7 @@ export default { }; const defaultChartProps: ChartProps = { + animations: false, data: workspaceTrendsDataWithAnomalies, minWidth: 400, maxWidth: 800, diff --git a/src/stories/components/Scatter/Scatter.story.tsx b/src/stories/components/Scatter/Scatter.story.tsx index 2487f485b..8912c03fa 100644 --- a/src/stories/components/Scatter/Scatter.story.tsx +++ b/src/stories/components/Scatter/Scatter.story.tsx @@ -89,7 +89,12 @@ const marioKeyTitle: Record<Exclude<MarioDataKey, 'character'>, string> = { miniTurbo: 'Mini-turbo', }; -const defaultChartProps: ChartProps = { data: characterData, height: 500, width: 500, lineWidths: [1, 2, 3] }; +const defaultChartProps: ChartProps = { + data: characterData, + height: 500, + width: 500, + lineWidths: [1, 2, 3], +}; const getLegendProps = (args: ScatterProps): LegendProps => { const facets = [COLOR_SCALE, LINE_TYPE_SCALE, OPACITY_SCALE, 'size']; diff --git a/src/stories/components/Scatter/Scatter.test.tsx b/src/stories/components/Scatter/Scatter.test.tsx index e7caa5825..19fb1b419 100644 --- a/src/stories/components/Scatter/Scatter.test.tsx +++ b/src/stories/components/Scatter/Scatter.test.tsx @@ -126,7 +126,7 @@ describe('Scatter', () => { }); test('should highlight hovered points', async () => { - render(<Popover {...Popover.args} />); + render(<Popover {...Popover.args} animations={false}/>); const chart = await findChart(); expect(chart).toBeInTheDocument(); @@ -187,7 +187,7 @@ describe('Scatter', () => { expect(within(tooltip).getByText('Metal Mario, Gold Mario, Pink Gold Peach')).toBeInTheDocument(); }); test('should highlight hovered points', async () => { - render(<Tooltip {...Tooltip.args} />); + render(<Tooltip { ...Tooltip.args } animations={false} />); const chart = await findChart(); expect(chart).toBeInTheDocument(); diff --git a/src/stories/components/ScatterPath/ScatterPath.story.tsx b/src/stories/components/ScatterPath/ScatterPath.story.tsx index 20c7e265d..1cfd153ae 100644 --- a/src/stories/components/ScatterPath/ScatterPath.story.tsx +++ b/src/stories/components/ScatterPath/ScatterPath.story.tsx @@ -23,7 +23,7 @@ export default { }; const ScatterPathStory: StoryFn<typeof ScatterPath> = (args): ReactElement => { - const chartProps = useChartProps({ data: characterData, height: 500, width: 500 }); + const chartProps = useChartProps({ animations: false, data: characterData, height: 500, width: 500 }); return ( <Chart {...chartProps}> diff --git a/src/stories/components/Title/Title.story.tsx b/src/stories/components/Title/Title.story.tsx index f2244d09b..06ba19592 100644 --- a/src/stories/components/Title/Title.story.tsx +++ b/src/stories/components/Title/Title.story.tsx @@ -22,7 +22,7 @@ export default { component: Title, }; -const defaultChartProps: ChartProps = { data, minWidth: 400, maxWidth: 800, height: 400 }; +const defaultChartProps: ChartProps = { animations: false, data, minWidth: 400, maxWidth: 800, height: 400 }; const TitleBarStory: StoryFn<typeof Title> = (args): ReactElement => { const chartProps = useChartProps(defaultChartProps); diff --git a/src/stories/components/Trendline/Trendline.story.tsx b/src/stories/components/Trendline/Trendline.story.tsx index 3659e4f5b..4f425147f 100644 --- a/src/stories/components/Trendline/Trendline.story.tsx +++ b/src/stories/components/Trendline/Trendline.story.tsx @@ -58,7 +58,7 @@ export default { }, }; -const defaultChartProps: ChartProps = { data: workspaceTrendsData, minWidth: 400, maxWidth: 800, height: 400 }; +const defaultChartProps: ChartProps = { animations: false, data: workspaceTrendsData, minWidth: 400, maxWidth: 800, height: 400 }; const TrendlineStory: StoryFn<typeof Trendline> = (args): ReactElement => { const chartProps = useChartProps(defaultChartProps); @@ -136,7 +136,7 @@ const TrendlineWithDialogsOnParentStory: StoryFn<typeof Trendline> = (args): Rea }; const ScatterStory: StoryFn<typeof Trendline> = (args): ReactElement => { - const chartProps = useChartProps({ data: characterData, height: 500, width: 500, lineWidths: [1, 2, 3] }); + const chartProps = useChartProps({ animations: false, data: characterData, height: 500, width: 500, lineWidths: [1, 2, 3] }); return ( <Chart {...chartProps}> diff --git a/src/stories/components/TrendlineAnnotation/TrendlineAnnotation.story.tsx b/src/stories/components/TrendlineAnnotation/TrendlineAnnotation.story.tsx index 271c24bbe..d740bb521 100644 --- a/src/stories/components/TrendlineAnnotation/TrendlineAnnotation.story.tsx +++ b/src/stories/components/TrendlineAnnotation/TrendlineAnnotation.story.tsx @@ -29,7 +29,7 @@ const trendlineProps: TrendlineProps = { }; const TrendlineAnnotationStory: StoryFn<typeof TrendlineAnnotation> = (args): ReactElement => { - const chartProps = useChartProps({ data: characterData, height: 500, width: 500, lineWidths: [1, 2, 3] }); + const chartProps = useChartProps({ animations: false, data: characterData, height: 500, width: 500, lineWidths: [1, 2, 3] }); return ( <Chart {...chartProps}> diff --git a/src/stories/data/data.ts b/src/stories/data/data.ts index acff56a70..a374698e1 100644 --- a/src/stories/data/data.ts +++ b/src/stories/data/data.ts @@ -1035,3 +1035,84 @@ export const stockPriceData = [ { date: 'June 28, 2023', timestamp: 1687910400000, price: 482.429993, stock: 'ADBE' }, { date: 'June 29, 2023', timestamp: 1687996800000, price: 483.769989, stock: 'ADBE' }, ]; + +export const newDataArray1 = [ + { x: 0, y: 92 }, + { x: 1, y: 56 }, + { x: 2, y: 84 }, + { x: 3, y: 37 }, + { x: 4, y: 49 }, + { x: 5, y: 67 }, + { x: 6, y: 95 }, + { x: 7, y: 77 }, + { x: 8, y: 92 }, + { x: 9, y: 56 }, + { x: 10, y: 84 }, + { x: 11, y: 37 }, + { x: 12, y: 49 }, + { x: 13, y: 67 }, + { x: 14, y: 95 }, + { x: 15, y: 77 }, +]; + +export const newDataArray2 = [ + { x: 0, y: 50 }, + { x: 1, y: 10 }, + { x: 2, y: 24 }, + { x: 3, y: 99 }, + { x: 4, y: 8 }, + { x: 5, y: 12 }, + { x: 6, y: 17 }, + { x: 7, y: 76 }, + { x: 8, y: 100 }, + { x: 9, y: 65 }, + { x: 10, y: 48 }, + { x: 11, y: 73 }, + { x: 12, y: 94 }, + { x: 13, y: 49 }, + { x: 14, y: 59 }, + { x: 15, y: 70 }, +]; + +export const newDataArray1WithStaticPoints = [ + { x: 0, y: 92, point: true }, + { x: 1, y: 56, point: true }, + { x: 2, y: 84, point: true }, + { x: 3, y: 37, point: true }, + { x: 4, y: 49, point: true }, + { x: 5, y: 67, point: true }, + { x: 6, y: 95, point: true }, + { x: 7, y: 77, point: true }, + { x: 8, y: 92, point: true }, + { x: 9, y: 56, point: true }, + { x: 10, y: 84, point: true }, + { x: 11, y: 37, point: true }, + { x: 12, y: 49, point: true }, + { x: 13, y: 67, point: true }, + { x: 14, y: 95, point: true }, + { x: 15, y: 77, point: true}, +] + +export const areaData = [ + { datetime: 1667890800000, maxTemperature: 73, minTemperature: 47, series: 'Add Fallout' }, + { datetime: 1667977200000, maxTemperature: 70, minTemperature: 48, series: 'Add Fallout' }, + { datetime: 1668063600000, maxTemperature: 73, minTemperature: 48, series: 'Add Fallout' }, + { datetime: 1668150000000, maxTemperature: 56, minTemperature: 31, series: 'Add Fallout' }, + { datetime: 1668236400000, maxTemperature: 41, minTemperature: 18, series: 'Add Fallout' }, + { datetime: 1668322800000, maxTemperature: 60, minTemperature: 45, series: 'Add Fallout' }, + { datetime: 1668409200000, maxTemperature: 64, minTemperature: 43, series: 'Add Fallout' }, +]; + +export const stackedAreaData = [ + { browser: 'Chrome', value: 5, operatingSystem: 'Windows', order: 2 }, + { browser: 'Chrome', value: 3, operatingSystem: 'Mac', order: 1 }, + { browser: 'Chrome', value: 2, operatingSystem: 'Other', order: 0 }, + { browser: 'Firefox', value: 3, operatingSystem: 'Windows', order: 2 }, + { browser: 'Firefox', value: 3, operatingSystem: 'Mac', order: 1 }, + { browser: 'Firefox', value: 1, operatingSystem: 'Other', order: 0 }, + { browser: 'Safari', value: 3, operatingSystem: 'Windows', order: 2 }, + { browser: 'Safari', value: 0, operatingSystem: 'Mac', order: 1 }, + { browser: 'Safari', value: 1, operatingSystem: 'Other', order: 0 }, +]; + + diff --git a/src/test-utils/spectrumRender.tsx b/src/test-utils/spectrumRender.tsx index 34bb9c750..b6df45c51 100644 --- a/src/test-utils/spectrumRender.tsx +++ b/src/test-utils/spectrumRender.tsx @@ -9,7 +9,7 @@ * OF ANY KIND, either express or implied. See the License for the specific language * governing permissions and limitations under the License. */ -import React, { ReactElement } from 'react'; +import { ReactElement } from 'react'; import { DEFAULT_COLOR_SCHEME } from '@constants'; import { render } from '@testing-library/react'; diff --git a/src/test-utils/utils.ts b/src/test-utils/utils.ts index eba775fda..f87dfc075 100644 --- a/src/test-utils/utils.ts +++ b/src/test-utils/utils.ts @@ -30,3 +30,9 @@ export const clickNthElement = async (elements: HTMLElement[], index: number) => export const allElementsHaveAttributeValue = (elements: HTMLElement[], attribute: string, value: number | string) => elements.every((element) => element.getAttribute(attribute) === value.toString()); + +export const manipulateData = (data: number): number => { + const randomFactor = Math.random() * (1.15 - 0.85) + 0.85; + const result = data * randomFactor; + return Math.round(result); +}; diff --git a/src/types/Chart.ts b/src/types/Chart.ts index a37c500e2..b6c4a9c04 100644 --- a/src/types/Chart.ts +++ b/src/types/Chart.ts @@ -78,9 +78,12 @@ export interface SpecProps { } export interface SanitizedSpecProps extends SpecProps { + animateFromZero?: boolean; + animations: boolean; + data?: ChartData[]; /** Children with all non-RSC components removed */ children: ChartChildElement[]; - data?: ChartData[]; + previousData?: ChartData[]; } export type Orientation = 'vertical' | 'horizontal'; @@ -92,6 +95,7 @@ export type ChartSymbolShape = 'rounded-square' | SymbolShape; export type NumberFormat = 'currency' | 'shortCurrency' | 'shortNumber' | 'standardNumber'; export interface SharedChartProps extends SpecProps { + animations?: boolean; /** Vega config that can be used to tweak the style of the chart. @see https://vega.github.io/vega/docs/config/ */ config?: Config; /** Chart data array. */ @@ -105,6 +109,7 @@ export interface SharedChartProps extends SpecProps { /** Chart padding */ padding?: Padding; /** Method to use for rendering the chart. 'canvas' is ideal for large data sets. */ + previousData?: ChartData[]; renderer?: 'svg' | 'canvas'; } @@ -270,6 +275,7 @@ export type SubLabel = { }; export interface MarkProps extends BaseProps { + animations?: boolean; children?: Children<MarkChildElement>; /** Key in the data that is used as the color facet */ color?: string; @@ -319,6 +325,7 @@ export interface AnnotationStyleProps extends MarkProps { } export interface BarProps extends Omit<MarkProps, 'color'> { + animations?: boolean; /** Bar color or key in the data that is used as the color facet */ color?: ColorFacet | DualFacet; /** Data field used for the bar categories (x-axis for a vertical bar) */ @@ -369,6 +376,7 @@ export interface LineProps extends Omit<MarkProps, 'color'> { } export interface ScatterProps extends Omit<MarkProps, 'color'> { + animations?: boolean; /** * point fill and stroke color * uses a key in the data that will map to the color scale or a static color value @@ -489,6 +497,7 @@ export type LegendDescription = { seriesName: string; description: string; title export type LegendLabel = { seriesName: string | number; label: string; maxLength?: number }; export interface LegendProps extends BaseProps { + animations?: boolean; /** color or key in the data that is used as the color facet for the symbols */ color?: ColorFacet; /** series that should be hidden by default (uncontrolled) */ @@ -533,6 +542,7 @@ export interface ChartTooltipProps { children?: TooltipHandler; } export interface ChartPopoverProps { + animations?: boolean, children?: PopoverHandler; width?: number; } diff --git a/src/types/specBuilderTypes.ts b/src/types/specBuilderTypes.ts index 721b196c2..2510c2a21 100644 --- a/src/types/specBuilderTypes.ts +++ b/src/types/specBuilderTypes.ts @@ -17,6 +17,7 @@ import { AxisAnnotationProps, AxisProps, BarProps, + ChartData, ColorFacet, ColorScheme, DonutProps, @@ -41,7 +42,11 @@ type AreaPropsWithDefaults = 'name' | 'dimension' | 'metric' | 'color' | 'scaleT export interface AreaSpecProps extends PartiallyRequired<AreaProps & { colorScheme: ColorScheme; index: number }, AreaPropsWithDefaults> { + animateFromZero?: boolean; + animations?: boolean; children: MarkChildElement[]; + data?: ChartData[]; + previousData?: ChartData[]; } type AxisPropsWithDefaults = @@ -99,7 +104,17 @@ type BarPropsWithDefaults = | 'type'; export interface BarSpecProps - extends PartiallyRequired<BarProps & { colorScheme: ColorScheme; index: number }, BarPropsWithDefaults> { + extends PartiallyRequired< + BarProps & { + colorScheme: ColorScheme; + index: number; + data?: ChartData[]; + previousData?: ChartData[]; + animations?: boolean; + animateFromZero?: boolean; + }, + BarPropsWithDefaults + > { children: MarkChildElement[]; } @@ -121,7 +136,13 @@ type LegendPropsWithDefaults = 'hiddenEntries' | 'highlight' | 'isToggleable' | export interface LegendSpecProps extends PartiallyRequired< - LegendProps & { colorScheme: ColorScheme; index: number; hiddenSeries: string[]; highlightedSeries?: string }, + LegendProps & { + colorScheme: ColorScheme; + index: number; + hiddenSeries: string[]; + highlightedSeries?: string; + animations?: boolean; + }, LegendPropsWithDefaults > { color?: FacetRef<string>; @@ -133,12 +154,16 @@ export interface LegendSpecProps type LinePropsWithDefaults = 'name' | 'dimension' | 'metric' | 'color' | 'scaleType' | 'lineType' | 'opacity'; export interface LineSpecProps extends PartiallyRequired<LineProps, LinePropsWithDefaults> { + animateFromZero?: boolean; + animations?: boolean; children: MarkChildElement[]; colorScheme: ColorScheme; + data?: ChartData[]; index: number; interactiveMarkName: string | undefined; lineWidth?: FacetRef<LineWidth>; popoverMarkName: string | undefined; + previousData?: ChartData[]; } type ScatterPropsWithDefaults = @@ -154,8 +179,11 @@ type ScatterPropsWithDefaults = | 'size'; export interface ScatterSpecProps extends PartiallyRequired<ScatterProps, ScatterPropsWithDefaults> { + animations?: boolean; children: MarkChildElement[]; colorScheme: ColorScheme; + data?: ChartData[]; // currently unused, but in place for future Scatter animations work + previousData?: ChartData[]; // currently unused, but in place for future Scatter animations work index: number; interactiveMarkName: string | undefined; } diff --git a/yarn.lock b/yarn.lock index 411ecfe15..7011c1893 100644 --- a/yarn.lock +++ b/yarn.lock @@ -12,6 +12,11 @@ resolved "https://registry.yarnpkg.com/@adobe/css-tools/-/css-tools-4.3.2.tgz#a6abc715fb6884851fca9dad37fc34739a04fd11" integrity sha512-DA5a1C0gD/pLOvhv33YMrbf2FK3oUzwNl9oOJqE4XVjuEtt6XIakRcsd7eLiOSPkp1kTRQGICTA8cKra/vFbjw== +"@adobe/css-tools@^4.3.2": + version "4.3.3" + resolved "https://registry.yarnpkg.com/@adobe/css-tools/-/css-tools-4.3.3.tgz#90749bde8b89cd41764224f5aac29cd4138f75ff" + integrity sha512-rE0Pygv0sEZ4vBWHlAgJLGDU7Pm8xoO6p3wsEceb7GYAjScrOHpEo8KK/eVkAcnSM+slAEtXjA2JpdjLp4fJQQ== + "@adobe/react-spectrum-ui@1.2.0": version "1.2.0" resolved "https://registry.yarnpkg.com/@adobe/react-spectrum-ui/-/react-spectrum-ui-1.2.0.tgz#b1c72d5db6ac84c99fd45ddb3bd8e00bfff9f150" @@ -124,6 +129,14 @@ "@babel/highlight" "^7.23.4" chalk "^2.4.2" +"@babel/code-frame@^7.24.1", "@babel/code-frame@^7.24.2": + version "7.24.2" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.24.2.tgz#718b4b19841809a58b29b68cde80bc5e1aa6d9ae" + integrity sha512-y5+tLQyV8pg3fsiln67BVLD1P13Eg4lh5RW9mF0zUuvLrv9uIQ4MCL+CRT+FTsBlBjcIan6PGsLcBN0m3ClUyQ== + dependencies: + "@babel/highlight" "^7.24.2" + picocolors "^1.0.0" + "@babel/compat-data@^7.22.6", "@babel/compat-data@^7.23.3", "@babel/compat-data@^7.23.5": version "7.23.5" resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.23.5.tgz#ffb878728bb6bdcb6f4510aa51b1be9afb8cfd98" @@ -150,6 +163,27 @@ json5 "^2.2.3" semver "^6.3.1" +"@babel/core@^7.23.7", "@babel/core@^7.23.9": + version "7.24.3" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.24.3.tgz#568864247ea10fbd4eff04dda1e05f9e2ea985c3" + integrity sha512-5FcvN1JHw2sHJChotgx8Ek0lyuh4kCKelgMTTqhYJJtloNvUfpAFMeNQUtdlIaktwrSV9LtCdqwk48wL2wBacQ== + dependencies: + "@ampproject/remapping" "^2.2.0" + "@babel/code-frame" "^7.24.2" + "@babel/generator" "^7.24.1" + "@babel/helper-compilation-targets" "^7.23.6" + "@babel/helper-module-transforms" "^7.23.3" + "@babel/helpers" "^7.24.1" + "@babel/parser" "^7.24.1" + "@babel/template" "^7.24.0" + "@babel/traverse" "^7.24.1" + "@babel/types" "^7.24.0" + convert-source-map "^2.0.0" + debug "^4.1.0" + gensync "^1.0.0-beta.2" + json5 "^2.2.3" + semver "^6.3.1" + "@babel/eslint-parser@^7.19.1": version "7.23.10" resolved "https://registry.yarnpkg.com/@babel/eslint-parser/-/eslint-parser-7.23.10.tgz#2d4164842d6db798873b40e0c4238827084667a2" @@ -178,6 +212,16 @@ "@jridgewell/trace-mapping" "^0.3.17" jsesc "^2.5.1" +"@babel/generator@^7.24.1": + version "7.24.1" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.24.1.tgz#e67e06f68568a4ebf194d1c6014235344f0476d0" + integrity sha512-DfCRfZsBcrPEHUfuBMgbJ1Ut01Y/itOs+hY2nFLgqsqXd52/iSiVq5TITtUasIUgm+IIKdY2/1I7auiQOEeC9A== + dependencies: + "@babel/types" "^7.24.0" + "@jridgewell/gen-mapping" "^0.3.5" + "@jridgewell/trace-mapping" "^0.3.25" + jsesc "^2.5.1" + "@babel/helper-annotate-as-pure@^7.22.5": version "7.22.5" resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.22.5.tgz#e7f06737b197d580a01edf75d97e2c8be99d3882" @@ -367,6 +411,15 @@ "@babel/traverse" "^7.23.9" "@babel/types" "^7.23.9" +"@babel/helpers@^7.24.1": + version "7.24.1" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.24.1.tgz#183e44714b9eba36c3038e442516587b1e0a1a94" + integrity sha512-BpU09QqEe6ZCHuIHFphEFgvNSrubve1FtyMton26ekZ85gRGi6LrTF7zArARp2YvyFxloeiRmtSCq5sjh1WqIg== + dependencies: + "@babel/template" "^7.24.0" + "@babel/traverse" "^7.24.1" + "@babel/types" "^7.24.0" + "@babel/highlight@^7.23.4": version "7.23.4" resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.23.4.tgz#edaadf4d8232e1a961432db785091207ead0621b" @@ -376,11 +429,26 @@ chalk "^2.4.2" js-tokens "^4.0.0" +"@babel/highlight@^7.24.2": + version "7.24.2" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.24.2.tgz#3f539503efc83d3c59080a10e6634306e0370d26" + integrity sha512-Yac1ao4flkTxTteCDZLEvdxg2fZfz1v8M4QpaGypq/WPDqg3ijHYbDfs+LG5hvzSoqaSZ9/Z9lKSP3CjZjv+pA== + dependencies: + "@babel/helper-validator-identifier" "^7.22.20" + chalk "^2.4.2" + js-tokens "^4.0.0" + picocolors "^1.0.0" + "@babel/parser@^7.1.0", "@babel/parser@^7.14.7", "@babel/parser@^7.20.5", "@babel/parser@^7.20.7", "@babel/parser@^7.23.0", "@babel/parser@^7.23.9": version "7.23.9" resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.23.9.tgz#7b903b6149b0f8fa7ad564af646c4c38a77fc44b" integrity sha512-9tcKgqKbs3xGJ+NtKF2ndOBBLVwPjl1SHxPQkd36r3Dlirw3xWUeGaTbqr7uGZcTaxkVNwc+03SVP7aCdWrTlA== +"@babel/parser@^7.24.0", "@babel/parser@^7.24.1": + version "7.24.1" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.24.1.tgz#1e416d3627393fab1cb5b0f2f1796a100ae9133a" + integrity sha512-Zo9c7N3xdOIQrNip7Lc9wvRPzlRtovHVE4lkz8WEDr7uYh/GMQhSiIgFxGIArRHYdJE5kxtZjAf8rT0xhdLCzg== + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@^7.23.3": version "7.23.3" resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.23.3.tgz#5cd1c87ba9380d0afb78469292c954fee5d2411a" @@ -1121,7 +1189,7 @@ "@babel/types" "^7.4.4" esutils "^2.0.2" -"@babel/preset-react@^7.18.6", "@babel/preset-react@^7.22.15": +"@babel/preset-react@^7.18.6": version "7.23.3" resolved "https://registry.yarnpkg.com/@babel/preset-react/-/preset-react-7.23.3.tgz#f73ca07e7590f977db07eb54dbe46538cc015709" integrity sha512-tbkHOS9axH6Ysf2OUEqoSZ6T3Fa2SrNH6WTWSPBboxKzdxNc9qOICeLXkNG0ZEwbQ1HY8liwOce4aN/Ceyuq6w== @@ -1176,6 +1244,15 @@ "@babel/parser" "^7.23.9" "@babel/types" "^7.23.9" +"@babel/template@^7.24.0": + version "7.24.0" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.24.0.tgz#c6a524aa93a4a05d66aaf31654258fae69d87d50" + integrity sha512-Bkf2q8lMB0AFpX0NFEqSbx1OkTHf0f+0j82mkw+ZpzBnkk7e9Ql0891vlfgi+kHwOk8tQjiQHpqh4LaSa0fKEA== + dependencies: + "@babel/code-frame" "^7.23.5" + "@babel/parser" "^7.24.0" + "@babel/types" "^7.24.0" + "@babel/traverse@7.23.2": version "7.23.2" resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.23.2.tgz#329c7a06735e144a506bdb2cad0268b7f46f4ad8" @@ -1208,6 +1285,22 @@ debug "^4.3.1" globals "^11.1.0" +"@babel/traverse@^7.24.1": + version "7.24.1" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.24.1.tgz#d65c36ac9dd17282175d1e4a3c49d5b7988f530c" + integrity sha512-xuU6o9m68KeqZbQuDt2TcKSxUw/mrsvavlEqQ1leZ/B+C9tk6E4sRWy97WaXgvq5E+nU3cXMxv3WKOCanVMCmQ== + dependencies: + "@babel/code-frame" "^7.24.1" + "@babel/generator" "^7.24.1" + "@babel/helper-environment-visitor" "^7.22.20" + "@babel/helper-function-name" "^7.23.0" + "@babel/helper-hoist-variables" "^7.22.5" + "@babel/helper-split-export-declaration" "^7.22.6" + "@babel/parser" "^7.24.1" + "@babel/types" "^7.24.0" + debug "^4.3.1" + globals "^11.1.0" + "@babel/types@7.17.0": version "7.17.0" resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.17.0.tgz#a826e368bccb6b3d84acd76acad5c0d87342390b" @@ -1225,6 +1318,15 @@ "@babel/helper-validator-identifier" "^7.22.20" to-fast-properties "^2.0.0" +"@babel/types@^7.24.0": + version "7.24.0" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.24.0.tgz#3b951f435a92e7333eba05b7566fd297960ea1bf" + integrity sha512-+j7a5c253RfKh8iABBhywc8NSfP5LURe7Uh4qpsh6jc+aLJguvmIUBdjSdEMQv2bENrCR5MfRdjGo7vzS/ob7w== + dependencies: + "@babel/helper-string-parser" "^7.23.4" + "@babel/helper-validator-identifier" "^7.22.20" + to-fast-properties "^2.0.0" + "@base2/pretty-print-object@1.0.1": version "1.0.1" resolved "https://registry.yarnpkg.com/@base2/pretty-print-object/-/pretty-print-object-1.0.1.tgz#371ba8be66d556812dc7fb169ebc3c08378f69d4" @@ -1245,7 +1347,7 @@ resolved "https://registry.yarnpkg.com/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz#1d572bfbbe14b7704e0ba0f39b74815b84870d70" integrity sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw== -"@emotion/use-insertion-effect-with-fallbacks@^1.0.0": +"@emotion/use-insertion-effect-with-fallbacks@^1.0.1": version "1.0.1" resolved "https://registry.yarnpkg.com/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.0.1.tgz#08de79f54eb3406f9daaf77c76e35313da963963" integrity sha512-jT/qyKZ9rzLErtrjGgdkMBn2OP8wl0G3sQlBb3YPryvKHsjvINUhVaPFfP+fpBcOkmrVOVEEHQFJ7nbj2TH2gw== @@ -1259,115 +1361,120 @@ esquery "^1.5.0" jsdoc-type-pratt-parser "~4.0.0" -"@esbuild/android-arm64@0.18.20": - version "0.18.20" - resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.18.20.tgz#984b4f9c8d0377443cc2dfcef266d02244593622" - integrity sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ== - -"@esbuild/android-arm@0.18.20": - version "0.18.20" - resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.18.20.tgz#fedb265bc3a589c84cc11f810804f234947c3682" - integrity sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw== - -"@esbuild/android-x64@0.18.20": - version "0.18.20" - resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.18.20.tgz#35cf419c4cfc8babe8893d296cd990e9e9f756f2" - integrity sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg== - -"@esbuild/darwin-arm64@0.18.20": - version "0.18.20" - resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.18.20.tgz#08172cbeccf95fbc383399a7f39cfbddaeb0d7c1" - integrity sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA== - -"@esbuild/darwin-x64@0.18.20": - version "0.18.20" - resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.18.20.tgz#d70d5790d8bf475556b67d0f8b7c5bdff053d85d" - integrity sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ== - -"@esbuild/freebsd-arm64@0.18.20": - version "0.18.20" - resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.18.20.tgz#98755cd12707f93f210e2494d6a4b51b96977f54" - integrity sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw== - -"@esbuild/freebsd-x64@0.18.20": - version "0.18.20" - resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.18.20.tgz#c1eb2bff03915f87c29cece4c1a7fa1f423b066e" - integrity sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ== - -"@esbuild/linux-arm64@0.18.20": - version "0.18.20" - resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.18.20.tgz#bad4238bd8f4fc25b5a021280c770ab5fc3a02a0" - integrity sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA== - -"@esbuild/linux-arm@0.18.20": - version "0.18.20" - resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.18.20.tgz#3e617c61f33508a27150ee417543c8ab5acc73b0" - integrity sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg== - -"@esbuild/linux-ia32@0.18.20": - version "0.18.20" - resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.18.20.tgz#699391cccba9aee6019b7f9892eb99219f1570a7" - integrity sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA== - -"@esbuild/linux-loong64@0.18.20": - version "0.18.20" - resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.18.20.tgz#e6fccb7aac178dd2ffb9860465ac89d7f23b977d" - integrity sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg== - -"@esbuild/linux-mips64el@0.18.20": - version "0.18.20" - resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.18.20.tgz#eeff3a937de9c2310de30622a957ad1bd9183231" - integrity sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ== - -"@esbuild/linux-ppc64@0.18.20": - version "0.18.20" - resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.18.20.tgz#2f7156bde20b01527993e6881435ad79ba9599fb" - integrity sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA== - -"@esbuild/linux-riscv64@0.18.20": - version "0.18.20" - resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.18.20.tgz#6628389f210123d8b4743045af8caa7d4ddfc7a6" - integrity sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A== - -"@esbuild/linux-s390x@0.18.20": - version "0.18.20" - resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.18.20.tgz#255e81fb289b101026131858ab99fba63dcf0071" - integrity sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ== - -"@esbuild/linux-x64@0.18.20": - version "0.18.20" - resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.18.20.tgz#c7690b3417af318a9b6f96df3031a8865176d338" - integrity sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w== - -"@esbuild/netbsd-x64@0.18.20": - version "0.18.20" - resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.18.20.tgz#30e8cd8a3dded63975e2df2438ca109601ebe0d1" - integrity sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A== - -"@esbuild/openbsd-x64@0.18.20": - version "0.18.20" - resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.18.20.tgz#7812af31b205055874c8082ea9cf9ab0da6217ae" - integrity sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg== - -"@esbuild/sunos-x64@0.18.20": - version "0.18.20" - resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.18.20.tgz#d5c275c3b4e73c9b0ecd38d1ca62c020f887ab9d" - integrity sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ== - -"@esbuild/win32-arm64@0.18.20": - version "0.18.20" - resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.18.20.tgz#73bc7f5a9f8a77805f357fab97f290d0e4820ac9" - integrity sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg== - -"@esbuild/win32-ia32@0.18.20": - version "0.18.20" - resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.18.20.tgz#ec93cbf0ef1085cc12e71e0d661d20569ff42102" - integrity sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g== - -"@esbuild/win32-x64@0.18.20": - version "0.18.20" - resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.18.20.tgz#786c5f41f043b07afb1af37683d7c33668858f6d" - integrity sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ== +"@esbuild/aix-ppc64@0.20.2": + version "0.20.2" + resolved "https://registry.yarnpkg.com/@esbuild/aix-ppc64/-/aix-ppc64-0.20.2.tgz#a70f4ac11c6a1dfc18b8bbb13284155d933b9537" + integrity sha512-D+EBOJHXdNZcLJRBkhENNG8Wji2kgc9AZ9KiPr1JuZjsNtyHzrsfLRrY0tk2H2aoFu6RANO1y1iPPUCDYWkb5g== + +"@esbuild/android-arm64@0.20.2": + version "0.20.2" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.20.2.tgz#db1c9202a5bc92ea04c7b6840f1bbe09ebf9e6b9" + integrity sha512-mRzjLacRtl/tWU0SvD8lUEwb61yP9cqQo6noDZP/O8VkwafSYwZ4yWy24kan8jE/IMERpYncRt2dw438LP3Xmg== + +"@esbuild/android-arm@0.20.2": + version "0.20.2" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.20.2.tgz#3b488c49aee9d491c2c8f98a909b785870d6e995" + integrity sha512-t98Ra6pw2VaDhqNWO2Oph2LXbz/EJcnLmKLGBJwEwXX/JAN83Fym1rU8l0JUWK6HkIbWONCSSatf4sf2NBRx/w== + +"@esbuild/android-x64@0.20.2": + version "0.20.2" + resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.20.2.tgz#3b1628029e5576249d2b2d766696e50768449f98" + integrity sha512-btzExgV+/lMGDDa194CcUQm53ncxzeBrWJcncOBxuC6ndBkKxnHdFJn86mCIgTELsooUmwUm9FkhSp5HYu00Rg== + +"@esbuild/darwin-arm64@0.20.2": + version "0.20.2" + resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.20.2.tgz#6e8517a045ddd86ae30c6608c8475ebc0c4000bb" + integrity sha512-4J6IRT+10J3aJH3l1yzEg9y3wkTDgDk7TSDFX+wKFiWjqWp/iCfLIYzGyasx9l0SAFPT1HwSCR+0w/h1ES/MjA== + +"@esbuild/darwin-x64@0.20.2": + version "0.20.2" + resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.20.2.tgz#90ed098e1f9dd8a9381695b207e1cff45540a0d0" + integrity sha512-tBcXp9KNphnNH0dfhv8KYkZhjc+H3XBkF5DKtswJblV7KlT9EI2+jeA8DgBjp908WEuYll6pF+UStUCfEpdysA== + +"@esbuild/freebsd-arm64@0.20.2": + version "0.20.2" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.20.2.tgz#d71502d1ee89a1130327e890364666c760a2a911" + integrity sha512-d3qI41G4SuLiCGCFGUrKsSeTXyWG6yem1KcGZVS+3FYlYhtNoNgYrWcvkOoaqMhwXSMrZRl69ArHsGJ9mYdbbw== + +"@esbuild/freebsd-x64@0.20.2": + version "0.20.2" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.20.2.tgz#aa5ea58d9c1dd9af688b8b6f63ef0d3d60cea53c" + integrity sha512-d+DipyvHRuqEeM5zDivKV1KuXn9WeRX6vqSqIDgwIfPQtwMP4jaDsQsDncjTDDsExT4lR/91OLjRo8bmC1e+Cw== + +"@esbuild/linux-arm64@0.20.2": + version "0.20.2" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.20.2.tgz#055b63725df678379b0f6db9d0fa85463755b2e5" + integrity sha512-9pb6rBjGvTFNira2FLIWqDk/uaf42sSyLE8j1rnUpuzsODBq7FvpwHYZxQ/It/8b+QOS1RYfqgGFNLRI+qlq2A== + +"@esbuild/linux-arm@0.20.2": + version "0.20.2" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.20.2.tgz#76b3b98cb1f87936fbc37f073efabad49dcd889c" + integrity sha512-VhLPeR8HTMPccbuWWcEUD1Az68TqaTYyj6nfE4QByZIQEQVWBB8vup8PpR7y1QHL3CpcF6xd5WVBU/+SBEvGTg== + +"@esbuild/linux-ia32@0.20.2": + version "0.20.2" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.20.2.tgz#c0e5e787c285264e5dfc7a79f04b8b4eefdad7fa" + integrity sha512-o10utieEkNPFDZFQm9CoP7Tvb33UutoJqg3qKf1PWVeeJhJw0Q347PxMvBgVVFgouYLGIhFYG0UGdBumROyiig== + +"@esbuild/linux-loong64@0.20.2": + version "0.20.2" + resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.20.2.tgz#a6184e62bd7cdc63e0c0448b83801001653219c5" + integrity sha512-PR7sp6R/UC4CFVomVINKJ80pMFlfDfMQMYynX7t1tNTeivQ6XdX5r2XovMmha/VjR1YN/HgHWsVcTRIMkymrgQ== + +"@esbuild/linux-mips64el@0.20.2": + version "0.20.2" + resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.20.2.tgz#d08e39ce86f45ef8fc88549d29c62b8acf5649aa" + integrity sha512-4BlTqeutE/KnOiTG5Y6Sb/Hw6hsBOZapOVF6njAESHInhlQAghVVZL1ZpIctBOoTFbQyGW+LsVYZ8lSSB3wkjA== + +"@esbuild/linux-ppc64@0.20.2": + version "0.20.2" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.20.2.tgz#8d252f0b7756ffd6d1cbde5ea67ff8fd20437f20" + integrity sha512-rD3KsaDprDcfajSKdn25ooz5J5/fWBylaaXkuotBDGnMnDP1Uv5DLAN/45qfnf3JDYyJv/ytGHQaziHUdyzaAg== + +"@esbuild/linux-riscv64@0.20.2": + version "0.20.2" + resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.20.2.tgz#19f6dcdb14409dae607f66ca1181dd4e9db81300" + integrity sha512-snwmBKacKmwTMmhLlz/3aH1Q9T8v45bKYGE3j26TsaOVtjIag4wLfWSiZykXzXuE1kbCE+zJRmwp+ZbIHinnVg== + +"@esbuild/linux-s390x@0.20.2": + version "0.20.2" + resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.20.2.tgz#3c830c90f1a5d7dd1473d5595ea4ebb920988685" + integrity sha512-wcWISOobRWNm3cezm5HOZcYz1sKoHLd8VL1dl309DiixxVFoFe/o8HnwuIwn6sXre88Nwj+VwZUvJf4AFxkyrQ== + +"@esbuild/linux-x64@0.20.2": + version "0.20.2" + resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.20.2.tgz#86eca35203afc0d9de0694c64ec0ab0a378f6fff" + integrity sha512-1MdwI6OOTsfQfek8sLwgyjOXAu+wKhLEoaOLTjbijk6E2WONYpH9ZU2mNtR+lZ2B4uwr+usqGuVfFT9tMtGvGw== + +"@esbuild/netbsd-x64@0.20.2": + version "0.20.2" + resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.20.2.tgz#e771c8eb0e0f6e1877ffd4220036b98aed5915e6" + integrity sha512-K8/DhBxcVQkzYc43yJXDSyjlFeHQJBiowJ0uVL6Tor3jGQfSGHNNJcWxNbOI8v5k82prYqzPuwkzHt3J1T1iZQ== + +"@esbuild/openbsd-x64@0.20.2": + version "0.20.2" + resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.20.2.tgz#9a795ae4b4e37e674f0f4d716f3e226dd7c39baf" + integrity sha512-eMpKlV0SThJmmJgiVyN9jTPJ2VBPquf6Kt/nAoo6DgHAoN57K15ZghiHaMvqjCye/uU4X5u3YSMgVBI1h3vKrQ== + +"@esbuild/sunos-x64@0.20.2": + version "0.20.2" + resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.20.2.tgz#7df23b61a497b8ac189def6e25a95673caedb03f" + integrity sha512-2UyFtRC6cXLyejf/YEld4Hajo7UHILetzE1vsRcGL3earZEW77JxrFjH4Ez2qaTiEfMgAXxfAZCm1fvM/G/o8w== + +"@esbuild/win32-arm64@0.20.2": + version "0.20.2" + resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.20.2.tgz#f1ae5abf9ca052ae11c1bc806fb4c0f519bacf90" + integrity sha512-GRibxoawM9ZCnDxnP3usoUDO9vUkpAxIIZ6GQI+IlVmr5kP3zUq+l17xELTHMWTWzjxa2guPNyrpq1GWmPvcGQ== + +"@esbuild/win32-ia32@0.20.2": + version "0.20.2" + resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.20.2.tgz#241fe62c34d8e8461cd708277813e1d0ba55ce23" + integrity sha512-HfLOfn9YWmkSKRQqovpnITazdtquEW8/SoHW7pWpuEeguaZI4QnCRW6b+oZTztdBnZOS2hqJ6im/D5cPzBTTlQ== + +"@esbuild/win32-x64@0.20.2": + version "0.20.2" + resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.20.2.tgz#9c907b21e30a52db959ba4f80bb01a0cc403d5cc" + integrity sha512-N49X4lJX27+l9jbLKSqZ6bKNjzQvHaT8IIFUy+YIqmXQdjYCToGWwOItDrfby14c78aDd5NHQl29xingXfCdLQ== "@eslint-community/eslint-utils@^4.2.0", "@eslint-community/eslint-utils@^4.4.0": version "4.4.0" @@ -1406,33 +1513,6 @@ resolved "https://registry.yarnpkg.com/@fal-works/esbuild-plugin-global-externals/-/esbuild-plugin-global-externals-2.1.2.tgz#c05ed35ad82df8e6ac616c68b92c2282bd083ba4" integrity sha512-cEee/Z+I12mZcFJshKcCqC8tuX5hG3s+d+9nZ3LabqKF1vKdF41B92pJVCBggjAGORAeOzyyDDKrZwIkLffeOQ== -"@floating-ui/core@^1.4.2": - version "1.5.2" - resolved "https://registry.yarnpkg.com/@floating-ui/core/-/core-1.5.2.tgz#53a0f7a98c550e63134d504f26804f6b83dbc071" - integrity sha512-Ii3MrfY/GAIN3OhXNzpCKaLxHQfJF9qvwq/kEJYdqDxeIHa01K8sldugal6TmeeXl+WMvhv9cnVzUTaFFJF09A== - dependencies: - "@floating-ui/utils" "^0.1.3" - -"@floating-ui/dom@^1.5.1": - version "1.5.3" - resolved "https://registry.yarnpkg.com/@floating-ui/dom/-/dom-1.5.3.tgz#54e50efcb432c06c23cd33de2b575102005436fa" - integrity sha512-ClAbQnEqJAKCJOEbbLo5IUlZHkNszqhuxS4fHAVxRPXPya6Ysf2G8KypnYcOTpx6I8xcgF9bbHb6g/2KpbV8qA== - dependencies: - "@floating-ui/core" "^1.4.2" - "@floating-ui/utils" "^0.1.3" - -"@floating-ui/react-dom@^2.0.0": - version "2.0.4" - resolved "https://registry.yarnpkg.com/@floating-ui/react-dom/-/react-dom-2.0.4.tgz#b076fafbdfeb881e1d86ae748b7ff95150e9f3ec" - integrity sha512-CF8k2rgKeh/49UrnIBs4BdxPUV6vize/Db1d/YbCLyp9GiVZ0BEwf5AiDSxJRCr6yOkGqTFHtmrULxkEfYZ7dQ== - dependencies: - "@floating-ui/dom" "^1.5.1" - -"@floating-ui/utils@^0.1.3": - version "0.1.6" - resolved "https://registry.yarnpkg.com/@floating-ui/utils/-/utils-0.1.6.tgz#22958c042e10b67463997bd6ea7115fe28cbcaf9" - integrity sha512-OfX7E2oUDYxtBvsuS4e/jSn4Q9Qb6DzgeYtsAdkPZ47znpoNsMgZw0+tVijiv3uGNR6dgNlty6r9rzIzHjtd/A== - "@formatjs/ecma402-abstract@1.18.0": version "1.18.0" resolved "https://registry.yarnpkg.com/@formatjs/ecma402-abstract/-/ecma402-abstract-1.18.0.tgz#e2120e7101020140661b58430a7ff4262705a2f2" @@ -1695,7 +1775,7 @@ jest-haste-map "^29.7.0" slash "^3.0.0" -"@jest/transform@^29.3.1", "@jest/transform@^29.7.0": +"@jest/transform@^29.7.0": version "29.7.0" resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-29.7.0.tgz#df2dd9c346c7d7768b8a06639994640c642e284c" integrity sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw== @@ -1737,6 +1817,15 @@ "@jridgewell/sourcemap-codec" "^1.4.10" "@jridgewell/trace-mapping" "^0.3.9" +"@jridgewell/gen-mapping@^0.3.5": + version "0.3.5" + resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz#dcce6aff74bdf6dad1a95802b69b04a2fcb1fb36" + integrity sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg== + dependencies: + "@jridgewell/set-array" "^1.2.1" + "@jridgewell/sourcemap-codec" "^1.4.10" + "@jridgewell/trace-mapping" "^0.3.24" + "@jridgewell/resolve-uri@^3.1.0": version "3.1.1" resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz#c08679063f279615a3326583ba3a90d1d82cc721" @@ -1747,6 +1836,11 @@ resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.1.2.tgz#7c6cf998d6d20b914c0a55a91ae928ff25965e72" integrity sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw== +"@jridgewell/set-array@^1.2.1": + version "1.2.1" + resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.2.1.tgz#558fb6472ed16a4c850b889530e6b36438c49280" + integrity sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A== + "@jridgewell/source-map@^0.3.3": version "0.3.5" resolved "https://registry.yarnpkg.com/@jridgewell/source-map/-/source-map-0.3.5.tgz#a3bb4d5c6825aab0d281268f47f6ad5853431e91" @@ -1768,7 +1862,7 @@ "@jridgewell/resolve-uri" "^3.1.0" "@jridgewell/sourcemap-codec" "^1.4.14" -"@jridgewell/trace-mapping@^0.3.20": +"@jridgewell/trace-mapping@^0.3.20", "@jridgewell/trace-mapping@^0.3.24", "@jridgewell/trace-mapping@^0.3.25": version "0.3.25" resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz#15f190e98895f3fc23276ee14bc76b675c2e50f0" integrity sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ== @@ -1776,11 +1870,6 @@ "@jridgewell/resolve-uri" "^3.1.0" "@jridgewell/sourcemap-codec" "^1.4.14" -"@juggle/resize-observer@^3.3.1": - version "3.4.0" - resolved "https://registry.yarnpkg.com/@juggle/resize-observer/-/resize-observer-3.4.0.tgz#08d6c5e20cf7e4cc02fd181c4b0c225cd31dbb60" - integrity sha512-dfLbk+PwWvFzSxwk3n5ySL0hfBog779o8h68wK/7/APo/7cgyWp5jcXockbxdk5kFRkbeXWm4Fbi9FrdN381sA== - "@leichtgewicht/ip-codec@^2.0.1": version "2.0.4" resolved "https://registry.yarnpkg.com/@leichtgewicht/ip-codec/-/ip-codec-2.0.4.tgz#b2ac626d6cb9c8718ab459166d4bb405b8ffa78b" @@ -1793,13 +1882,12 @@ dependencies: call-bind "^1.0.5" -"@mdx-js/react@^2.1.5": - version "2.3.0" - resolved "https://registry.yarnpkg.com/@mdx-js/react/-/react-2.3.0.tgz#4208bd6d70f0d0831def28ef28c26149b03180b3" - integrity sha512-zQH//gdOmuu7nt2oJR29vFhDv88oGPmVw6BggmrHeMI+xgEkp1B2dX9/bMBSYtK0dyLX/aOmesKS09g222K1/g== +"@mdx-js/react@^3.0.0": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@mdx-js/react/-/react-3.0.1.tgz#997a19b3a5b783d936c75ae7c47cfe62f967f746" + integrity sha512-9ZrPIU4MGf6et1m1ov3zKf+q9+deetI51zprKB1D/z3NOb+rUxxtEl3mCjW5wTGh6VhRdwPueh1oRzi6ezkA8A== dependencies: "@types/mdx" "^2.0.0" - "@types/react" ">=16" "@ndelangen/get-tarball@^3.0.7": version "3.0.9" @@ -1853,54 +1941,6 @@ resolved "https://registry.yarnpkg.com/@pkgr/core/-/core-0.1.1.tgz#1ec17e2edbec25c8306d424ecfbf13c7de1aaa31" integrity sha512-cq8o4cWH0ibXh9VGi5P20Tu9XF/0fFXl9EUinr9QfTM7a7p0oTA4iJRCQWppXR1Pg8dSM0UCItCkPwsk9qWWYA== -"@pmmmwh/react-refresh-webpack-plugin@^0.5.11": - version "0.5.11" - resolved "https://registry.yarnpkg.com/@pmmmwh/react-refresh-webpack-plugin/-/react-refresh-webpack-plugin-0.5.11.tgz#7c2268cedaa0644d677e8c4f377bc8fb304f714a" - integrity sha512-7j/6vdTym0+qZ6u4XbSAxrWBGYSdCfTzySkj7WAFgDLmSyWlOrWvpyzxlFh5jtw9dn0oL/jtW+06XfFiisN3JQ== - dependencies: - ansi-html-community "^0.0.8" - common-path-prefix "^3.0.0" - core-js-pure "^3.23.3" - error-stack-parser "^2.0.6" - find-up "^5.0.0" - html-entities "^2.1.0" - loader-utils "^2.0.4" - schema-utils "^3.0.0" - source-map "^0.7.3" - -"@radix-ui/number@1.0.1": - version "1.0.1" - resolved "https://registry.yarnpkg.com/@radix-ui/number/-/number-1.0.1.tgz#644161a3557f46ed38a042acf4a770e826021674" - integrity sha512-T5gIdVO2mmPW3NNhjNgEP3cqMXjXL9UbO0BzWcXfvdBs+BohbQxvd/K5hSVKmn9/lbTdsQVKbUcP5WLCwvUbBg== - dependencies: - "@babel/runtime" "^7.13.10" - -"@radix-ui/primitive@1.0.1": - version "1.0.1" - resolved "https://registry.yarnpkg.com/@radix-ui/primitive/-/primitive-1.0.1.tgz#e46f9958b35d10e9f6dc71c497305c22e3e55dbd" - integrity sha512-yQ8oGX2GVsEYMWGxcovu1uGWPCxV5BFfeeYxqPmuAzUyLT9qmaMXSAhXpb0WrspIeqYzdJpkh2vHModJPgRIaw== - dependencies: - "@babel/runtime" "^7.13.10" - -"@radix-ui/react-arrow@1.0.3": - version "1.0.3" - resolved "https://registry.yarnpkg.com/@radix-ui/react-arrow/-/react-arrow-1.0.3.tgz#c24f7968996ed934d57fe6cde5d6ec7266e1d25d" - integrity sha512-wSP+pHsB/jQRaL6voubsQ/ZlrGBHHrOjmBnr19hxYgtS0WvAFwZhK2WP/YY5yF9uKECCEEDGxuLxq1NBK51wFA== - dependencies: - "@babel/runtime" "^7.13.10" - "@radix-ui/react-primitive" "1.0.3" - -"@radix-ui/react-collection@1.0.3": - version "1.0.3" - resolved "https://registry.yarnpkg.com/@radix-ui/react-collection/-/react-collection-1.0.3.tgz#9595a66e09026187524a36c6e7e9c7d286469159" - integrity sha512-3SzW+0PW7yBBoQlT8wNcGtaxaD0XSu0uLUFgrtHY08Acx05TaHaOmVLR73c0j/cqpDy53KBMO7s0dx2wmOIDIA== - dependencies: - "@babel/runtime" "^7.13.10" - "@radix-ui/react-compose-refs" "1.0.1" - "@radix-ui/react-context" "1.0.1" - "@radix-ui/react-primitive" "1.0.3" - "@radix-ui/react-slot" "1.0.2" - "@radix-ui/react-compose-refs@1.0.1": version "1.0.1" resolved "https://registry.yarnpkg.com/@radix-ui/react-compose-refs/-/react-compose-refs-1.0.1.tgz#7ed868b66946aa6030e580b1ffca386dd4d21989" @@ -1908,143 +1948,7 @@ dependencies: "@babel/runtime" "^7.13.10" -"@radix-ui/react-context@1.0.1": - version "1.0.1" - resolved "https://registry.yarnpkg.com/@radix-ui/react-context/-/react-context-1.0.1.tgz#fe46e67c96b240de59187dcb7a1a50ce3e2ec00c" - integrity sha512-ebbrdFoYTcuZ0v4wG5tedGnp9tzcV8awzsxYph7gXUyvnNLuTIcCk1q17JEbnVhXAKG9oX3KtchwiMIAYp9NLg== - dependencies: - "@babel/runtime" "^7.13.10" - -"@radix-ui/react-direction@1.0.1": - version "1.0.1" - resolved "https://registry.yarnpkg.com/@radix-ui/react-direction/-/react-direction-1.0.1.tgz#9cb61bf2ccf568f3421422d182637b7f47596c9b" - integrity sha512-RXcvnXgyvYvBEOhCBuddKecVkoMiI10Jcm5cTI7abJRAHYfFxeu+FBQs/DvdxSYucxR5mna0dNsL6QFlds5TMA== - dependencies: - "@babel/runtime" "^7.13.10" - -"@radix-ui/react-dismissable-layer@1.0.4": - version "1.0.4" - resolved "https://registry.yarnpkg.com/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.0.4.tgz#883a48f5f938fa679427aa17fcba70c5494c6978" - integrity sha512-7UpBa/RKMoHJYjie1gkF1DlK8l1fdU/VKDpoS3rCCo8YBJR294GwcEHyxHw72yvphJ7ld0AXEcSLAzY2F/WyCg== - dependencies: - "@babel/runtime" "^7.13.10" - "@radix-ui/primitive" "1.0.1" - "@radix-ui/react-compose-refs" "1.0.1" - "@radix-ui/react-primitive" "1.0.3" - "@radix-ui/react-use-callback-ref" "1.0.1" - "@radix-ui/react-use-escape-keydown" "1.0.3" - -"@radix-ui/react-focus-guards@1.0.1": - version "1.0.1" - resolved "https://registry.yarnpkg.com/@radix-ui/react-focus-guards/-/react-focus-guards-1.0.1.tgz#1ea7e32092216b946397866199d892f71f7f98ad" - integrity sha512-Rect2dWbQ8waGzhMavsIbmSVCgYxkXLxxR3ZvCX79JOglzdEy4JXMb98lq4hPxUbLr77nP0UOGf4rcMU+s1pUA== - dependencies: - "@babel/runtime" "^7.13.10" - -"@radix-ui/react-focus-scope@1.0.3": - version "1.0.3" - resolved "https://registry.yarnpkg.com/@radix-ui/react-focus-scope/-/react-focus-scope-1.0.3.tgz#9c2e8d4ed1189a1d419ee61edd5c1828726472f9" - integrity sha512-upXdPfqI4islj2CslyfUBNlaJCPybbqRHAi1KER7Isel9Q2AtSJ0zRBZv8mWQiFXD2nyAJ4BhC3yXgZ6kMBSrQ== - dependencies: - "@babel/runtime" "^7.13.10" - "@radix-ui/react-compose-refs" "1.0.1" - "@radix-ui/react-primitive" "1.0.3" - "@radix-ui/react-use-callback-ref" "1.0.1" - -"@radix-ui/react-id@1.0.1": - version "1.0.1" - resolved "https://registry.yarnpkg.com/@radix-ui/react-id/-/react-id-1.0.1.tgz#73cdc181f650e4df24f0b6a5b7aa426b912c88c0" - integrity sha512-tI7sT/kqYp8p96yGWY1OAnLHrqDgzHefRBKQ2YAkBS5ja7QLcZ9Z/uY7bEjPUatf8RomoXM8/1sMj1IJaE5UzQ== - dependencies: - "@babel/runtime" "^7.13.10" - "@radix-ui/react-use-layout-effect" "1.0.1" - -"@radix-ui/react-popper@1.1.2": - version "1.1.2" - resolved "https://registry.yarnpkg.com/@radix-ui/react-popper/-/react-popper-1.1.2.tgz#4c0b96fcd188dc1f334e02dba2d538973ad842e9" - integrity sha512-1CnGGfFi/bbqtJZZ0P/NQY20xdG3E0LALJaLUEoKwPLwl6PPPfbeiCqMVQnhoFRAxjJj4RpBRJzDmUgsex2tSg== - dependencies: - "@babel/runtime" "^7.13.10" - "@floating-ui/react-dom" "^2.0.0" - "@radix-ui/react-arrow" "1.0.3" - "@radix-ui/react-compose-refs" "1.0.1" - "@radix-ui/react-context" "1.0.1" - "@radix-ui/react-primitive" "1.0.3" - "@radix-ui/react-use-callback-ref" "1.0.1" - "@radix-ui/react-use-layout-effect" "1.0.1" - "@radix-ui/react-use-rect" "1.0.1" - "@radix-ui/react-use-size" "1.0.1" - "@radix-ui/rect" "1.0.1" - -"@radix-ui/react-portal@1.0.3": - version "1.0.3" - resolved "https://registry.yarnpkg.com/@radix-ui/react-portal/-/react-portal-1.0.3.tgz#ffb961244c8ed1b46f039e6c215a6c4d9989bda1" - integrity sha512-xLYZeHrWoPmA5mEKEfZZevoVRK/Q43GfzRXkWV6qawIWWK8t6ifIiLQdd7rmQ4Vk1bmI21XhqF9BN3jWf+phpA== - dependencies: - "@babel/runtime" "^7.13.10" - "@radix-ui/react-primitive" "1.0.3" - -"@radix-ui/react-primitive@1.0.3": - version "1.0.3" - resolved "https://registry.yarnpkg.com/@radix-ui/react-primitive/-/react-primitive-1.0.3.tgz#d49ea0f3f0b2fe3ab1cb5667eb03e8b843b914d0" - integrity sha512-yi58uVyoAcK/Nq1inRY56ZSjKypBNKTa/1mcL8qdl6oJeEaDbOldlzrGn7P6Q3Id5d+SYNGc5AJgc4vGhjs5+g== - dependencies: - "@babel/runtime" "^7.13.10" - "@radix-ui/react-slot" "1.0.2" - -"@radix-ui/react-roving-focus@1.0.4": - version "1.0.4" - resolved "https://registry.yarnpkg.com/@radix-ui/react-roving-focus/-/react-roving-focus-1.0.4.tgz#e90c4a6a5f6ac09d3b8c1f5b5e81aab2f0db1974" - integrity sha512-2mUg5Mgcu001VkGy+FfzZyzbmuUWzgWkj3rvv4yu+mLw03+mTzbxZHvfcGyFp2b8EkQeMkpRQ5FiA2Vr2O6TeQ== - dependencies: - "@babel/runtime" "^7.13.10" - "@radix-ui/primitive" "1.0.1" - "@radix-ui/react-collection" "1.0.3" - "@radix-ui/react-compose-refs" "1.0.1" - "@radix-ui/react-context" "1.0.1" - "@radix-ui/react-direction" "1.0.1" - "@radix-ui/react-id" "1.0.1" - "@radix-ui/react-primitive" "1.0.3" - "@radix-ui/react-use-callback-ref" "1.0.1" - "@radix-ui/react-use-controllable-state" "1.0.1" - -"@radix-ui/react-select@^1.2.2": - version "1.2.2" - resolved "https://registry.yarnpkg.com/@radix-ui/react-select/-/react-select-1.2.2.tgz#caa981fa0d672cf3c1b2a5240135524e69b32181" - integrity sha512-zI7McXr8fNaSrUY9mZe4x/HC0jTLY9fWNhO1oLWYMQGDXuV4UCivIGTxwioSzO0ZCYX9iSLyWmAh/1TOmX3Cnw== - dependencies: - "@babel/runtime" "^7.13.10" - "@radix-ui/number" "1.0.1" - "@radix-ui/primitive" "1.0.1" - "@radix-ui/react-collection" "1.0.3" - "@radix-ui/react-compose-refs" "1.0.1" - "@radix-ui/react-context" "1.0.1" - "@radix-ui/react-direction" "1.0.1" - "@radix-ui/react-dismissable-layer" "1.0.4" - "@radix-ui/react-focus-guards" "1.0.1" - "@radix-ui/react-focus-scope" "1.0.3" - "@radix-ui/react-id" "1.0.1" - "@radix-ui/react-popper" "1.1.2" - "@radix-ui/react-portal" "1.0.3" - "@radix-ui/react-primitive" "1.0.3" - "@radix-ui/react-slot" "1.0.2" - "@radix-ui/react-use-callback-ref" "1.0.1" - "@radix-ui/react-use-controllable-state" "1.0.1" - "@radix-ui/react-use-layout-effect" "1.0.1" - "@radix-ui/react-use-previous" "1.0.1" - "@radix-ui/react-visually-hidden" "1.0.3" - aria-hidden "^1.1.1" - react-remove-scroll "2.5.5" - -"@radix-ui/react-separator@1.0.3": - version "1.0.3" - resolved "https://registry.yarnpkg.com/@radix-ui/react-separator/-/react-separator-1.0.3.tgz#be5a931a543d5726336b112f465f58585c04c8aa" - integrity sha512-itYmTy/kokS21aiV5+Z56MZB54KrhPgn6eHDKkFeOLR34HMN2s8PaN47qZZAGnvupcjxHaFZnW4pQEh0BvvVuw== - dependencies: - "@babel/runtime" "^7.13.10" - "@radix-ui/react-primitive" "1.0.3" - -"@radix-ui/react-slot@1.0.2": +"@radix-ui/react-slot@^1.0.2": version "1.0.2" resolved "https://registry.yarnpkg.com/@radix-ui/react-slot/-/react-slot-1.0.2.tgz#a9ff4423eade67f501ffb32ec22064bc9d3099ab" integrity sha512-YeTpuq4deV+6DusvVUW4ivBgnkHwECUu0BiN43L5UCDFgdhsRUWAghhTF5MbvNTPzmiFOx90asDSUjWuCNapwg== @@ -2052,112 +1956,6 @@ "@babel/runtime" "^7.13.10" "@radix-ui/react-compose-refs" "1.0.1" -"@radix-ui/react-toggle-group@1.0.4": - version "1.0.4" - resolved "https://registry.yarnpkg.com/@radix-ui/react-toggle-group/-/react-toggle-group-1.0.4.tgz#f5b5c8c477831b013bec3580c55e20a68179d6ec" - integrity sha512-Uaj/M/cMyiyT9Bx6fOZO0SAG4Cls0GptBWiBmBxofmDbNVnYYoyRWj/2M/6VCi/7qcXFWnHhRUfdfZFvvkuu8A== - dependencies: - "@babel/runtime" "^7.13.10" - "@radix-ui/primitive" "1.0.1" - "@radix-ui/react-context" "1.0.1" - "@radix-ui/react-direction" "1.0.1" - "@radix-ui/react-primitive" "1.0.3" - "@radix-ui/react-roving-focus" "1.0.4" - "@radix-ui/react-toggle" "1.0.3" - "@radix-ui/react-use-controllable-state" "1.0.1" - -"@radix-ui/react-toggle@1.0.3": - version "1.0.3" - resolved "https://registry.yarnpkg.com/@radix-ui/react-toggle/-/react-toggle-1.0.3.tgz#aecb2945630d1dc5c512997556c57aba894e539e" - integrity sha512-Pkqg3+Bc98ftZGsl60CLANXQBBQ4W3mTFS9EJvNxKMZ7magklKV69/id1mlAlOFDDfHvlCms0fx8fA4CMKDJHg== - dependencies: - "@babel/runtime" "^7.13.10" - "@radix-ui/primitive" "1.0.1" - "@radix-ui/react-primitive" "1.0.3" - "@radix-ui/react-use-controllable-state" "1.0.1" - -"@radix-ui/react-toolbar@^1.0.4": - version "1.0.4" - resolved "https://registry.yarnpkg.com/@radix-ui/react-toolbar/-/react-toolbar-1.0.4.tgz#3211a105567fa016e89921b5b514877f833de559" - integrity sha512-tBgmM/O7a07xbaEkYJWYTXkIdU/1pW4/KZORR43toC/4XWyBCURK0ei9kMUdp+gTPPKBgYLxXmRSH1EVcIDp8Q== - dependencies: - "@babel/runtime" "^7.13.10" - "@radix-ui/primitive" "1.0.1" - "@radix-ui/react-context" "1.0.1" - "@radix-ui/react-direction" "1.0.1" - "@radix-ui/react-primitive" "1.0.3" - "@radix-ui/react-roving-focus" "1.0.4" - "@radix-ui/react-separator" "1.0.3" - "@radix-ui/react-toggle-group" "1.0.4" - -"@radix-ui/react-use-callback-ref@1.0.1": - version "1.0.1" - resolved "https://registry.yarnpkg.com/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.0.1.tgz#f4bb1f27f2023c984e6534317ebc411fc181107a" - integrity sha512-D94LjX4Sp0xJFVaoQOd3OO9k7tpBYNOXdVhkltUbGv2Qb9OXdrg/CpsjlZv7ia14Sylv398LswWBVVu5nqKzAQ== - dependencies: - "@babel/runtime" "^7.13.10" - -"@radix-ui/react-use-controllable-state@1.0.1": - version "1.0.1" - resolved "https://registry.yarnpkg.com/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.0.1.tgz#ecd2ced34e6330caf89a82854aa2f77e07440286" - integrity sha512-Svl5GY5FQeN758fWKrjM6Qb7asvXeiZltlT4U2gVfl8Gx5UAv2sMR0LWo8yhsIZh2oQ0eFdZ59aoOOMV7b47VA== - dependencies: - "@babel/runtime" "^7.13.10" - "@radix-ui/react-use-callback-ref" "1.0.1" - -"@radix-ui/react-use-escape-keydown@1.0.3": - version "1.0.3" - resolved "https://registry.yarnpkg.com/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.0.3.tgz#217b840c250541609c66f67ed7bab2b733620755" - integrity sha512-vyL82j40hcFicA+M4Ex7hVkB9vHgSse1ZWomAqV2Je3RleKGO5iM8KMOEtfoSB0PnIelMd2lATjTGMYqN5ylTg== - dependencies: - "@babel/runtime" "^7.13.10" - "@radix-ui/react-use-callback-ref" "1.0.1" - -"@radix-ui/react-use-layout-effect@1.0.1": - version "1.0.1" - resolved "https://registry.yarnpkg.com/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.0.1.tgz#be8c7bc809b0c8934acf6657b577daf948a75399" - integrity sha512-v/5RegiJWYdoCvMnITBkNNx6bCj20fiaJnWtRkU18yITptraXjffz5Qbn05uOiQnOvi+dbkznkoaMltz1GnszQ== - dependencies: - "@babel/runtime" "^7.13.10" - -"@radix-ui/react-use-previous@1.0.1": - version "1.0.1" - resolved "https://registry.yarnpkg.com/@radix-ui/react-use-previous/-/react-use-previous-1.0.1.tgz#b595c087b07317a4f143696c6a01de43b0d0ec66" - integrity sha512-cV5La9DPwiQ7S0gf/0qiD6YgNqM5Fk97Kdrlc5yBcrF3jyEZQwm7vYFqMo4IfeHgJXsRaMvLABFtd0OVEmZhDw== - dependencies: - "@babel/runtime" "^7.13.10" - -"@radix-ui/react-use-rect@1.0.1": - version "1.0.1" - resolved "https://registry.yarnpkg.com/@radix-ui/react-use-rect/-/react-use-rect-1.0.1.tgz#fde50b3bb9fd08f4a1cd204572e5943c244fcec2" - integrity sha512-Cq5DLuSiuYVKNU8orzJMbl15TXilTnJKUCltMVQg53BQOF1/C5toAaGrowkgksdBQ9H+SRL23g0HDmg9tvmxXw== - dependencies: - "@babel/runtime" "^7.13.10" - "@radix-ui/rect" "1.0.1" - -"@radix-ui/react-use-size@1.0.1": - version "1.0.1" - resolved "https://registry.yarnpkg.com/@radix-ui/react-use-size/-/react-use-size-1.0.1.tgz#1c5f5fea940a7d7ade77694bb98116fb49f870b2" - integrity sha512-ibay+VqrgcaI6veAojjofPATwledXiSmX+C0KrBk/xgpX9rBzPV3OsfwlhQdUOFbh+LKQorLYT+xTXW9V8yd0g== - dependencies: - "@babel/runtime" "^7.13.10" - "@radix-ui/react-use-layout-effect" "1.0.1" - -"@radix-ui/react-visually-hidden@1.0.3": - version "1.0.3" - resolved "https://registry.yarnpkg.com/@radix-ui/react-visually-hidden/-/react-visually-hidden-1.0.3.tgz#51aed9dd0fe5abcad7dee2a234ad36106a6984ac" - integrity sha512-D4w41yN5YRKtu464TLnByKzMDG/JlMPHtfZgQAu9v6mNakUqGUI9vUrfQKz8NK41VMm/xbZbh76NUTVtIYqOMA== - dependencies: - "@babel/runtime" "^7.13.10" - "@radix-ui/react-primitive" "1.0.3" - -"@radix-ui/rect@1.0.1": - version "1.0.1" - resolved "https://registry.yarnpkg.com/@radix-ui/rect/-/rect-1.0.1.tgz#bf8e7d947671996da2e30f4904ece343bc4a883f" - integrity sha512-fyrgCaedtvMg9NK3en0pnOYJdtfwxUcNolezkNPUsoX57X8oQk+NkqcvzHXD2uKNij6GXmWU9NDru2IWjrO4BQ== - dependencies: - "@babel/runtime" "^7.13.10" - "@react-aria/actiongroup@^3.7.3": version "3.7.3" resolved "https://registry.yarnpkg.com/@react-aria/actiongroup/-/actiongroup-3.7.3.tgz#ffd25b3b88b75cea104fd8cb34e97868df351826" @@ -4176,155 +3974,156 @@ "@react-spectrum/icon" "^3.7.11" "@swc/helpers" "^0.5.0" -"@storybook/addon-actions@7.6.17": - version "7.6.17" - resolved "https://registry.yarnpkg.com/@storybook/addon-actions/-/addon-actions-7.6.17.tgz#b1be5ab28b22b4a50c6aa0cd0a3671ca5b6f5f71" - integrity sha512-TBphs4v6LRfyTpFo/WINF0TkMaE3rrNog7wW5mbz6n0j8o53kDN4o9ZEcygSL5zQX43CAaghQTeDCss7ueG7ZQ== +"@storybook/addon-actions@8.0.4", "@storybook/addon-actions@^8.0.4": + version "8.0.4" + resolved "https://registry.yarnpkg.com/@storybook/addon-actions/-/addon-actions-8.0.4.tgz#fc2285bd26660f10af497b332d0e62e7e00bfbbc" + integrity sha512-EyCWo+8T11/TJGYNL/AXtW4yaB+q1v2E9mixbumryCLxpTl2NtaeGZ4e0dlwfIMuw/7RWgHk2uIypcIPR/UANQ== dependencies: - "@storybook/core-events" "7.6.17" + "@storybook/core-events" "8.0.4" "@storybook/global" "^5.0.0" "@types/uuid" "^9.0.1" dequal "^2.0.2" polished "^4.2.2" uuid "^9.0.0" -"@storybook/addon-backgrounds@7.6.17": - version "7.6.17" - resolved "https://registry.yarnpkg.com/@storybook/addon-backgrounds/-/addon-backgrounds-7.6.17.tgz#a3c96cb73e6053dc2cf9968cb02b437c4d752812" - integrity sha512-7dize7x8+37PH77kmt69b0xSaeDqOcZ4fpzW6+hk53hIaCVU26eGs4+j+743Xva31eOgZWNLupUhOpUDc6SqZw== +"@storybook/addon-backgrounds@8.0.4": + version "8.0.4" + resolved "https://registry.yarnpkg.com/@storybook/addon-backgrounds/-/addon-backgrounds-8.0.4.tgz#e4250a10fe4768dcc016a3c55a627f2c8a6e8d5b" + integrity sha512-fef0KD2GhJx2zpicOf8iL7k2LiIsNzEbGaQpIIjoy4DMqM1hIfNCt3DGTLH7LN5O8G+NVCLS1xmQg7RLvIVSCA== dependencies: "@storybook/global" "^5.0.0" memoizerific "^1.11.3" ts-dedent "^2.0.0" -"@storybook/addon-controls@7.6.17": - version "7.6.17" - resolved "https://registry.yarnpkg.com/@storybook/addon-controls/-/addon-controls-7.6.17.tgz#354f3f85481e0a3318519b8c8aa5a3b1152e8de0" - integrity sha512-zR0aLaUF7FtV/nMRyfniFbCls/e0DAAoXACuOAUAwNAv0lbIS8AyZZiHSmKucCvziUQ6WceeCC7+du3C+9y0rQ== +"@storybook/addon-controls@8.0.4": + version "8.0.4" + resolved "https://registry.yarnpkg.com/@storybook/addon-controls/-/addon-controls-8.0.4.tgz#536ad2a1b9a2b19371848e4ec4eaedb4f41b9ec3" + integrity sha512-K5EYBTsUOTJlvIdA7p6Xj31wnV+RbZAkk56UKQvA7nJD7oDuLOq3E9u46F/uZD1vxddd9zFhf2iONfMe3KTTwQ== dependencies: - "@storybook/blocks" "7.6.17" + "@storybook/blocks" "8.0.4" lodash "^4.17.21" ts-dedent "^2.0.0" -"@storybook/addon-docs@7.6.17": - version "7.6.17" - resolved "https://registry.yarnpkg.com/@storybook/addon-docs/-/addon-docs-7.6.17.tgz#ea62be2da8b31df2c80a47cac4c30f66af4d2fbf" - integrity sha512-FKa4Mdy7nhgvEVZJHpMkHriDzpVHbohn87zv9NCL+Ctjs1iAmzGwxEm0culszyDS1HN2ToVoY0h8CSi2RSSZqA== - dependencies: - "@jest/transform" "^29.3.1" - "@mdx-js/react" "^2.1.5" - "@storybook/blocks" "7.6.17" - "@storybook/client-logger" "7.6.17" - "@storybook/components" "7.6.17" - "@storybook/csf-plugin" "7.6.17" - "@storybook/csf-tools" "7.6.17" +"@storybook/addon-docs@8.0.4": + version "8.0.4" + resolved "https://registry.yarnpkg.com/@storybook/addon-docs/-/addon-docs-8.0.4.tgz#5cee04859c017bdd0f46bc7337a5064cd415c211" + integrity sha512-m0Y7qGAMnNPLEOEgzW/SBm8GX0xabJBaRN+aYijO6UKTln7F6oXXVve+xPC0Y4s6Gc9HZFdJY8WXZr1YSGEUVA== + dependencies: + "@babel/core" "^7.12.3" + "@mdx-js/react" "^3.0.0" + "@storybook/blocks" "8.0.4" + "@storybook/client-logger" "8.0.4" + "@storybook/components" "8.0.4" + "@storybook/csf-plugin" "8.0.4" + "@storybook/csf-tools" "8.0.4" "@storybook/global" "^5.0.0" - "@storybook/mdx2-csf" "^1.0.0" - "@storybook/node-logger" "7.6.17" - "@storybook/postinstall" "7.6.17" - "@storybook/preview-api" "7.6.17" - "@storybook/react-dom-shim" "7.6.17" - "@storybook/theming" "7.6.17" - "@storybook/types" "7.6.17" + "@storybook/node-logger" "8.0.4" + "@storybook/preview-api" "8.0.4" + "@storybook/react-dom-shim" "8.0.4" + "@storybook/theming" "8.0.4" + "@storybook/types" "8.0.4" + "@types/react" "^16.8.0 || ^17.0.0 || ^18.0.0" fs-extra "^11.1.0" - remark-external-links "^8.0.0" - remark-slug "^6.0.0" + react "^16.8.0 || ^17.0.0 || ^18.0.0" + react-dom "^16.8.0 || ^17.0.0 || ^18.0.0" + rehype-external-links "^3.0.0" + rehype-slug "^6.0.0" ts-dedent "^2.0.0" -"@storybook/addon-essentials@7.6.17": - version "7.6.17" - resolved "https://registry.yarnpkg.com/@storybook/addon-essentials/-/addon-essentials-7.6.17.tgz#d49d9a77edc999518c6871b66032a647787c39f4" - integrity sha512-qlSpamxuYfT2taF953nC9QijGF2pSbg1ewMNpdwLTj16PTZvR/d8NCDMTJujI1bDwM2m18u8Yc43ibh5LEmxCw== - dependencies: - "@storybook/addon-actions" "7.6.17" - "@storybook/addon-backgrounds" "7.6.17" - "@storybook/addon-controls" "7.6.17" - "@storybook/addon-docs" "7.6.17" - "@storybook/addon-highlight" "7.6.17" - "@storybook/addon-measure" "7.6.17" - "@storybook/addon-outline" "7.6.17" - "@storybook/addon-toolbars" "7.6.17" - "@storybook/addon-viewport" "7.6.17" - "@storybook/core-common" "7.6.17" - "@storybook/manager-api" "7.6.17" - "@storybook/node-logger" "7.6.17" - "@storybook/preview-api" "7.6.17" +"@storybook/addon-essentials@^8.0.4": + version "8.0.4" + resolved "https://registry.yarnpkg.com/@storybook/addon-essentials/-/addon-essentials-8.0.4.tgz#64b6bf4ccf82c8f212472457f37ecce0acd5cfb1" + integrity sha512-mUIqhAkSz6Qv7nRqAAyCqMLiXBWVsY/8qN7HEIoaMQgdFq38KW3rYwNdzd2JLeXNWP1bBXwfvfcFe7/eqhYJFA== + dependencies: + "@storybook/addon-actions" "8.0.4" + "@storybook/addon-backgrounds" "8.0.4" + "@storybook/addon-controls" "8.0.4" + "@storybook/addon-docs" "8.0.4" + "@storybook/addon-highlight" "8.0.4" + "@storybook/addon-measure" "8.0.4" + "@storybook/addon-outline" "8.0.4" + "@storybook/addon-toolbars" "8.0.4" + "@storybook/addon-viewport" "8.0.4" + "@storybook/core-common" "8.0.4" + "@storybook/manager-api" "8.0.4" + "@storybook/node-logger" "8.0.4" + "@storybook/preview-api" "8.0.4" ts-dedent "^2.0.0" -"@storybook/addon-highlight@7.6.17": - version "7.6.17" - resolved "https://registry.yarnpkg.com/@storybook/addon-highlight/-/addon-highlight-7.6.17.tgz#6d8549aa95eb007888f4d272e9ab7316cbcc001c" - integrity sha512-R1yBPUUqGn+60aJakn8q+5Zt34E/gU3n3VmgPdryP0LJUdZ5q1/RZShoVDV+yYQ40htMH6oaCv3OyyPzFAGJ6A== +"@storybook/addon-highlight@8.0.4": + version "8.0.4" + resolved "https://registry.yarnpkg.com/@storybook/addon-highlight/-/addon-highlight-8.0.4.tgz#32f21a1f850394e83277a1cd553a33c86cb603f4" + integrity sha512-tnEiVaJlXL07v8JBox+QtRPVruoy0YovOTAOWY7fKDiKzF1I9wLaJjQF3wOsvwspHTHu00OZw2gsazgXiH4wLQ== dependencies: "@storybook/global" "^5.0.0" -"@storybook/addon-links@7.6.17": - version "7.6.17" - resolved "https://registry.yarnpkg.com/@storybook/addon-links/-/addon-links-7.6.17.tgz#5a678ff09c1b5056b67cb345c115cfcd343ffe86" - integrity sha512-iFUwKObRn0EKI0zMETsil2p9a/81rCuSMEWECsi+khkCAs1FUnD2cT6Ag5ydcNcBXsdtdfDJdtXQrkw+TSoStQ== +"@storybook/addon-links@^8.0.4": + version "8.0.4" + resolved "https://registry.yarnpkg.com/@storybook/addon-links/-/addon-links-8.0.4.tgz#34c4a3818a52023b09c98fcfe4c00e9ac08a0d10" + integrity sha512-SzE+JPZ4mxjprZqbLHf8Hx7UA2fXfMajFjeY9c3JREKQrDoOF1e4r28nAoVsZYF+frWxQB51U4+hOqjlx06wEA== dependencies: "@storybook/csf" "^0.1.2" "@storybook/global" "^5.0.0" ts-dedent "^2.0.0" -"@storybook/addon-measure@7.6.17": - version "7.6.17" - resolved "https://registry.yarnpkg.com/@storybook/addon-measure/-/addon-measure-7.6.17.tgz#a348b40dfa592c66b348457bd4f535f4ba481279" - integrity sha512-O5vnHZNkduvZ95jf1UssbOl6ivIxzl5tv+4EpScPYId7w700bxWsJH+QX7ip6KlrCf2o3iUhmPe8bm05ghG2KA== +"@storybook/addon-measure@8.0.4": + version "8.0.4" + resolved "https://registry.yarnpkg.com/@storybook/addon-measure/-/addon-measure-8.0.4.tgz#b4d492da2f1f3744c3a792475bd44e1305239412" + integrity sha512-GZYKo2ss5Br+dfHinoK3bgTaS90z3oKKDkhv6lrFfjjU1mDYzzMJpxajQhd3apCYxHLr3MbUqMQibWu2T/q2DQ== dependencies: "@storybook/global" "^5.0.0" tiny-invariant "^1.3.1" -"@storybook/addon-outline@7.6.17": - version "7.6.17" - resolved "https://registry.yarnpkg.com/@storybook/addon-outline/-/addon-outline-7.6.17.tgz#f87c7bea4ecba783c79a3026f8fc7e0acc26c460" - integrity sha512-9o9JXDsYjNaDgz/cY5+jv694+aik/1aiRGGvsCv68e1p/ob0glkGKav4lnJe2VJqD+gCmaARoD8GOJlhoQl8JQ== +"@storybook/addon-outline@8.0.4": + version "8.0.4" + resolved "https://registry.yarnpkg.com/@storybook/addon-outline/-/addon-outline-8.0.4.tgz#a368a493dafe3f0cea79af160103efa9e9b1e21b" + integrity sha512-6J9ezNDUxdA3rMCh8sUEQbUwAgkrr+M9QdiFr1t+gKrk5FKP5gwubw1sr3sF1IRB9+s/AjljcOtJAVulSfq05w== dependencies: "@storybook/global" "^5.0.0" ts-dedent "^2.0.0" -"@storybook/addon-toolbars@7.6.17": - version "7.6.17" - resolved "https://registry.yarnpkg.com/@storybook/addon-toolbars/-/addon-toolbars-7.6.17.tgz#98c1cee88a8f5f61464d28a09648994884d7bd0a" - integrity sha512-UMrchbUHiyWrh6WuGnpy34Jqzkx/63B+MSgb3CW7YsQaXz64kE0Rol0TNSznnB+mYXplcqH+ndI4r4kFsmgwDg== +"@storybook/addon-toolbars@8.0.4": + version "8.0.4" + resolved "https://registry.yarnpkg.com/@storybook/addon-toolbars/-/addon-toolbars-8.0.4.tgz#a1de4472089bf685dfca9863bc648e2faf360063" + integrity sha512-yodRXDYog/90cNEy84kg6s7L+nxQ+egBjHBTsav1L4cJmQI/uAX8yISHHiX4I5ppNc120Jz3UdHdRxXRlo345g== -"@storybook/addon-viewport@7.6.17": - version "7.6.17" - resolved "https://registry.yarnpkg.com/@storybook/addon-viewport/-/addon-viewport-7.6.17.tgz#db3c1f14bb4185f20d745c4e8cf2bd10f70ea336" - integrity sha512-sA0QCcf4QAMixWvn8uvRYPfkKCSl6JajJaAspoPqXSxHEpK7uwOlpg3kqFU5XJJPXD0X957M+ONgNvBzYqSpEw== +"@storybook/addon-viewport@8.0.4": + version "8.0.4" + resolved "https://registry.yarnpkg.com/@storybook/addon-viewport/-/addon-viewport-8.0.4.tgz#628322ea0a94252015814edd4c548e26f2c79f4a" + integrity sha512-E5IKOsxKcOtlOYc0cWgzVJohQB+dVBWwaJcg5FlslToknfVB9M0kfQ/SQcp3KB0C9/cOmJK1Jm388InW+EjrBQ== dependencies: memoizerific "^1.11.3" -"@storybook/addons@^7.0.0": - version "7.6.5" - resolved "https://registry.yarnpkg.com/@storybook/addons/-/addons-7.6.5.tgz#b21e88c89ef8579c5be6b759e15bedb5ca0c6627" - integrity sha512-v+d8io1MsgTd7rruYInfKXY0c1uXn+ADLxAppUI0PUwPFYwg9tLn3cvwgt5SVum9E5IkVQwXoW6JNkDC5fC8XQ== +"@storybook/addon-webpack5-compiler-babel@^3.0.3": + version "3.0.3" + resolved "https://registry.yarnpkg.com/@storybook/addon-webpack5-compiler-babel/-/addon-webpack5-compiler-babel-3.0.3.tgz#4aa5a57ea30f6ab82de90ef5eca8e3e3f05e112a" + integrity sha512-rVQTTw+oxJltbVKaejIWSHwVKOBJs3au21f/pYXhV0aiNgNhxEa3vr79t/j0j8ox8uJtzM8XYOb7FlkvGfHlwQ== dependencies: - "@storybook/manager-api" "7.6.5" - "@storybook/preview-api" "7.6.5" - "@storybook/types" "7.6.5" + "@babel/core" "^7.23.7" + babel-loader "^9.1.3" -"@storybook/blocks@7.6.17": - version "7.6.17" - resolved "https://registry.yarnpkg.com/@storybook/blocks/-/blocks-7.6.17.tgz#1329885be158f08104f806e5f23b7eb7f99c8b1c" - integrity sha512-PsNVoe0bX1mMn4Kk3nbKZ0ItDZZ0YJnYAFJ6toAbsyBAbgzg1sce88sQinzvbn58/RT9MPKeWMPB45ZS7ggiNg== +"@storybook/blocks@8.0.4": + version "8.0.4" + resolved "https://registry.yarnpkg.com/@storybook/blocks/-/blocks-8.0.4.tgz#844d5882f04f3fc06c62ee057e303e72ffe53499" + integrity sha512-9dRXk9zLJVPOmEWsSXm10XUmIfvS/tVgeBgFXNbusFQZXPpexIPNdRgB004pDGg9RvlY78ykpnd3yP143zaXMg== dependencies: - "@storybook/channels" "7.6.17" - "@storybook/client-logger" "7.6.17" - "@storybook/components" "7.6.17" - "@storybook/core-events" "7.6.17" + "@storybook/channels" "8.0.4" + "@storybook/client-logger" "8.0.4" + "@storybook/components" "8.0.4" + "@storybook/core-events" "8.0.4" "@storybook/csf" "^0.1.2" - "@storybook/docs-tools" "7.6.17" + "@storybook/docs-tools" "8.0.4" "@storybook/global" "^5.0.0" - "@storybook/manager-api" "7.6.17" - "@storybook/preview-api" "7.6.17" - "@storybook/theming" "7.6.17" - "@storybook/types" "7.6.17" + "@storybook/icons" "^1.2.5" + "@storybook/manager-api" "8.0.4" + "@storybook/preview-api" "8.0.4" + "@storybook/theming" "8.0.4" + "@storybook/types" "8.0.4" "@types/lodash" "^4.14.167" color-convert "^2.0.1" dequal "^2.0.2" lodash "^4.17.21" - markdown-to-jsx "^7.1.8" + markdown-to-jsx "7.3.2" memoizerific "^1.11.3" polished "^4.2.2" react-colorful "^5.1.2" @@ -4333,46 +4132,41 @@ ts-dedent "^2.0.0" util-deprecate "^1.0.2" -"@storybook/builder-manager@7.6.17": - version "7.6.17" - resolved "https://registry.yarnpkg.com/@storybook/builder-manager/-/builder-manager-7.6.17.tgz#0d329bea94b5c4a7f88eaee02c42d49c4370c8b4" - integrity sha512-Sj8hcDYiPCCMfeLzus37czl0zdrAxAz4IyYam2jBjVymrIrcDAFyL1OCZvnq33ft179QYQWhUs9qwzVmlR/ZWg== +"@storybook/builder-manager@8.0.4": + version "8.0.4" + resolved "https://registry.yarnpkg.com/@storybook/builder-manager/-/builder-manager-8.0.4.tgz#5c9baa6138beae7cd75c85e4b836cb1e3115d3c4" + integrity sha512-BafYVxq77uuTmXdjYo5by42OyOrb6qcpWYKva3ntWK2ZhTaLJlwwqAOdahT1DVzi4VeUP6465YvsTCzIE8fuIw== dependencies: "@fal-works/esbuild-plugin-global-externals" "^2.1.2" - "@storybook/core-common" "7.6.17" - "@storybook/manager" "7.6.17" - "@storybook/node-logger" "7.6.17" + "@storybook/core-common" "8.0.4" + "@storybook/manager" "8.0.4" + "@storybook/node-logger" "8.0.4" "@types/ejs" "^3.1.1" - "@types/find-cache-dir" "^3.2.1" "@yarnpkg/esbuild-plugin-pnp" "^3.0.0-rc.10" browser-assert "^1.2.1" ejs "^3.1.8" - esbuild "^0.18.0" + esbuild "^0.18.0 || ^0.19.0 || ^0.20.0" esbuild-plugin-alias "^0.2.1" express "^4.17.3" - find-cache-dir "^3.0.0" fs-extra "^11.1.0" process "^0.11.10" util "^0.12.4" -"@storybook/builder-webpack5@7.6.17": - version "7.6.17" - resolved "https://registry.yarnpkg.com/@storybook/builder-webpack5/-/builder-webpack5-7.6.17.tgz#1bf52b4cf62c66cbfe95e189fa4303a542bb645a" - integrity sha512-GMaBd8/RzivuAmWrYSt9Rga3j8WLcu5LCMYiPVs+XKXsKAC8lTkV0WRWh8Nk6wTmfzsRQ2acwFjSG5oE4ClZKA== - dependencies: - "@babel/core" "^7.23.2" - "@storybook/channels" "7.6.17" - "@storybook/client-logger" "7.6.17" - "@storybook/core-common" "7.6.17" - "@storybook/core-events" "7.6.17" - "@storybook/core-webpack" "7.6.17" - "@storybook/node-logger" "7.6.17" - "@storybook/preview" "7.6.17" - "@storybook/preview-api" "7.6.17" - "@swc/core" "^1.3.82" +"@storybook/builder-webpack5@8.0.4": + version "8.0.4" + resolved "https://registry.yarnpkg.com/@storybook/builder-webpack5/-/builder-webpack5-8.0.4.tgz#94c864d63443edeb4349d67b2c553a76a6c681f4" + integrity sha512-FKXIGfDjZJ7KCq6w8e3NEp2+KATsh4U24UV/K8cFjCrRIU++jDpO274D9ozdpzEmhvHOfxK/QlgalqS4G599Aw== + dependencies: + "@storybook/channels" "8.0.4" + "@storybook/client-logger" "8.0.4" + "@storybook/core-common" "8.0.4" + "@storybook/core-events" "8.0.4" + "@storybook/core-webpack" "8.0.4" + "@storybook/node-logger" "8.0.4" + "@storybook/preview" "8.0.4" + "@storybook/preview-api" "8.0.4" "@types/node" "^18.0.0" "@types/semver" "^7.3.4" - babel-loader "^9.0.0" browser-assert "^1.2.1" case-sensitive-paths-webpack-plugin "^2.4.0" cjs-module-lexer "^1.2.3" @@ -4388,7 +4182,6 @@ process "^0.11.10" semver "^7.3.7" style-loader "^3.3.1" - swc-loader "^0.2.3" terser-webpack-plugin "^5.3.1" ts-dedent "^2.0.0" url "^0.11.0" @@ -4399,47 +4192,33 @@ webpack-hot-middleware "^2.25.1" webpack-virtual-modules "^0.5.0" -"@storybook/channels@7.6.17": - version "7.6.17" - resolved "https://registry.yarnpkg.com/@storybook/channels/-/channels-7.6.17.tgz#5be1d1222a3ffdc90e1868230c2b2ee5dfc7a97f" - integrity sha512-GFG40pzaSxk1hUr/J/TMqW5AFDDPUSu+HkeE/oqSWJbOodBOLJzHN6CReJS6y1DjYSZLNFt1jftPWZZInG/XUA== - dependencies: - "@storybook/client-logger" "7.6.17" - "@storybook/core-events" "7.6.17" - "@storybook/global" "^5.0.0" - qs "^6.10.0" - telejson "^7.2.0" - tiny-invariant "^1.3.1" - -"@storybook/channels@7.6.5": - version "7.6.5" - resolved "https://registry.yarnpkg.com/@storybook/channels/-/channels-7.6.5.tgz#cd2c977052bc83b6d4980fe2d2e0da5c91eadd68" - integrity sha512-FIlNkyfQy9uHoJfAFL2/wO3ASGJELFvBzURBE2rcEF/TS7GcUiqWnBfiDxAbwSEjSOm2F0eEq3UXhaZEjpJHDw== +"@storybook/channels@8.0.4": + version "8.0.4" + resolved "https://registry.yarnpkg.com/@storybook/channels/-/channels-8.0.4.tgz#f237bc4d3a00ed05a2f62b310b1db08d6f685dcc" + integrity sha512-haKV+8RbiSzLjicowUfc7h2fTClZHX/nz9SRUecf4IEZUEu2T78OgM/TzqZvL7rA3+/fKqp5iI+3PN3OA75Sdg== dependencies: - "@storybook/client-logger" "7.6.5" - "@storybook/core-events" "7.6.5" + "@storybook/client-logger" "8.0.4" + "@storybook/core-events" "8.0.4" "@storybook/global" "^5.0.0" - qs "^6.10.0" telejson "^7.2.0" tiny-invariant "^1.3.1" -"@storybook/cli@7.6.17": - version "7.6.17" - resolved "https://registry.yarnpkg.com/@storybook/cli/-/cli-7.6.17.tgz#04462c97a926e3dfcc18f3df02519effe29740e2" - integrity sha512-1sCo+nCqyR+nKfTcEidVu8XzNoECC7Y1l+uW38/r7s2f/TdDorXaIGAVrpjbSaXSoQpx5DxYJVaKCcQuOgqwcA== +"@storybook/cli@8.0.4": + version "8.0.4" + resolved "https://registry.yarnpkg.com/@storybook/cli/-/cli-8.0.4.tgz#37037ed476c895f728cda528a7b76df01b9a35a0" + integrity sha512-8jb8hrulRMfyFyNXFEapxHBS51xb42ZZGfVAacXIsHOJtjOd5CnOoSUYn0aOkVl19VF/snoa9JOW7BaW/50Eqw== dependencies: - "@babel/core" "^7.23.2" - "@babel/preset-env" "^7.23.2" + "@babel/core" "^7.23.0" "@babel/types" "^7.23.0" "@ndelangen/get-tarball" "^3.0.7" - "@storybook/codemod" "7.6.17" - "@storybook/core-common" "7.6.17" - "@storybook/core-events" "7.6.17" - "@storybook/core-server" "7.6.17" - "@storybook/csf-tools" "7.6.17" - "@storybook/node-logger" "7.6.17" - "@storybook/telemetry" "7.6.17" - "@storybook/types" "7.6.17" + "@storybook/codemod" "8.0.4" + "@storybook/core-common" "8.0.4" + "@storybook/core-events" "8.0.4" + "@storybook/core-server" "8.0.4" + "@storybook/csf-tools" "8.0.4" + "@storybook/node-logger" "8.0.4" + "@storybook/telemetry" "8.0.4" + "@storybook/types" "8.0.4" "@types/semver" "^7.3.4" "@yarnpkg/fslib" "2.10.3" "@yarnpkg/libzip" "2.3.0" @@ -4449,115 +4228,82 @@ detect-indent "^6.1.0" envinfo "^7.7.3" execa "^5.0.0" - express "^4.17.3" find-up "^5.0.0" fs-extra "^11.1.0" get-npm-tarball-url "^2.0.3" - get-port "^5.1.1" giget "^1.0.0" globby "^11.0.2" jscodeshift "^0.15.1" leven "^3.1.0" ora "^5.4.1" - prettier "^2.8.0" + prettier "^3.1.1" prompts "^2.4.0" - puppeteer-core "^2.1.1" read-pkg-up "^7.0.1" semver "^7.3.7" strip-json-comments "^3.0.1" tempy "^1.0.1" + tiny-invariant "^1.3.1" ts-dedent "^2.0.0" - util-deprecate "^1.0.2" -"@storybook/client-logger@7.6.17": - version "7.6.17" - resolved "https://registry.yarnpkg.com/@storybook/client-logger/-/client-logger-7.6.17.tgz#5031c47b7df8d8792fe9dfed5828222f515e5803" - integrity sha512-6WBYqixAXNAXlSaBWwgljWpAu10tPRBJrcFvx2gPUne58EeMM20Gi/iHYBz2kMCY+JLAgeIH7ZxInqwO8vDwiQ== +"@storybook/client-logger@8.0.4": + version "8.0.4" + resolved "https://registry.yarnpkg.com/@storybook/client-logger/-/client-logger-8.0.4.tgz#d603399ad65ef1d38bf013033fcb18728d0d4317" + integrity sha512-2SeEg3PT/d0l/+EAVtyj9hmMLTyTPp+bRBSzxYouBjtJPM1jrdKpFagj1o3uBRovwWm9SIVX6/ZsoRC33PEV1g== dependencies: "@storybook/global" "^5.0.0" -"@storybook/client-logger@7.6.5": - version "7.6.5" - resolved "https://registry.yarnpkg.com/@storybook/client-logger/-/client-logger-7.6.5.tgz#a9d11ce436134884ecfec908e8bb4a970e233789" - integrity sha512-S5aROWgssqg7tcs9lgW5wmCAz4SxMAtioiyVj5oFecmPCbQtFVIAREYzeoxE4GfJL+plrfRkum4BzziANn8EhQ== - dependencies: - "@storybook/global" "^5.0.0" - -"@storybook/codemod@7.6.17": - version "7.6.17" - resolved "https://registry.yarnpkg.com/@storybook/codemod/-/codemod-7.6.17.tgz#c93d87d74f43fd475d48edb178233e89329b72c2" - integrity sha512-JuTmf2u3C4fCnjO7o3dqRgrq3ozNYfWlrRP8xuIdvT7niMap7a396hJtSKqS10FxCgKFcMAOsRgrCalH1dWxUg== +"@storybook/codemod@8.0.4": + version "8.0.4" + resolved "https://registry.yarnpkg.com/@storybook/codemod/-/codemod-8.0.4.tgz#815c7c729b973f958282df37040581a7ba416324" + integrity sha512-bysG46P4wjlR3RCpr/ntNAUaupWpzLcWYWti3iNtIyZ/iPrX6KtXoA9QCIwJZrlv41us6F+KEZbzLzkgWbymtQ== dependencies: "@babel/core" "^7.23.2" "@babel/preset-env" "^7.23.2" "@babel/types" "^7.23.0" "@storybook/csf" "^0.1.2" - "@storybook/csf-tools" "7.6.17" - "@storybook/node-logger" "7.6.17" - "@storybook/types" "7.6.17" + "@storybook/csf-tools" "8.0.4" + "@storybook/node-logger" "8.0.4" + "@storybook/types" "8.0.4" "@types/cross-spawn" "^6.0.2" cross-spawn "^7.0.3" globby "^11.0.2" jscodeshift "^0.15.1" lodash "^4.17.21" - prettier "^2.8.0" - recast "^0.23.1" - -"@storybook/components@7.6.17": - version "7.6.17" - resolved "https://registry.yarnpkg.com/@storybook/components/-/components-7.6.17.tgz#f02a47ad42432f8ea518321a145a074e4c11649f" - integrity sha512-lbh7GynMidA+CZcJnstVku6Nhs+YkqjYaZ+mKPugvlVhGVWv0DaaeQFVuZ8cJtUGJ/5FFU4Y+n+gylYUHkGBMA== - dependencies: - "@radix-ui/react-select" "^1.2.2" - "@radix-ui/react-toolbar" "^1.0.4" - "@storybook/client-logger" "7.6.17" - "@storybook/csf" "^0.1.2" - "@storybook/global" "^5.0.0" - "@storybook/theming" "7.6.17" - "@storybook/types" "7.6.17" - memoizerific "^1.11.3" - use-resize-observer "^9.1.0" - util-deprecate "^1.0.2" + prettier "^3.1.1" + recast "^0.23.5" + tiny-invariant "^1.3.1" -"@storybook/components@^7.0.0": - version "7.6.5" - resolved "https://registry.yarnpkg.com/@storybook/components/-/components-7.6.5.tgz#188a7f9ba75b04e7414b4b720d274700df286323" - integrity sha512-w4ZucbBBZ+NKMWlJKVj2I/bMBBq7gzDp9lzc4+8QaQ3vUPXKqc1ilIPYo/7UR5oxwDVMZocmMSgl9L8lvf7+Mw== +"@storybook/components@8.0.4", "@storybook/components@^8.0.0": + version "8.0.4" + resolved "https://registry.yarnpkg.com/@storybook/components/-/components-8.0.4.tgz#45f6548bfe42004bbb1533b45d9a3e65043b5fd7" + integrity sha512-i5ngl5GTOLB9nZ1cmpxTjtWct5IuH9UxzFC73a0jHMkCwN26w16IqufRVDaoQv0AvZN4pd4fNM2in/XVHA10dw== dependencies: - "@radix-ui/react-select" "^1.2.2" - "@radix-ui/react-toolbar" "^1.0.4" - "@storybook/client-logger" "7.6.5" + "@radix-ui/react-slot" "^1.0.2" + "@storybook/client-logger" "8.0.4" "@storybook/csf" "^0.1.2" "@storybook/global" "^5.0.0" - "@storybook/theming" "7.6.5" - "@storybook/types" "7.6.5" + "@storybook/icons" "^1.2.5" + "@storybook/theming" "8.0.4" + "@storybook/types" "8.0.4" memoizerific "^1.11.3" - use-resize-observer "^9.1.0" util-deprecate "^1.0.2" -"@storybook/core-client@7.6.17": - version "7.6.17" - resolved "https://registry.yarnpkg.com/@storybook/core-client/-/core-client-7.6.17.tgz#eace9819b64febf0d5ab2743f65ec5dfe4e3a410" - integrity sha512-LuDbADK+DPNAOOCXOlvY09hdGVueXlDetsdOJ/DgYnSa9QSWv9Uv+F8QcEgR3QckZJbPlztKJIVLgP2n/Xkijw== +"@storybook/core-common@8.0.4": + version "8.0.4" + resolved "https://registry.yarnpkg.com/@storybook/core-common/-/core-common-8.0.4.tgz#ba851052f1cd61ba2784e07ad64499e3c36bb9a1" + integrity sha512-dzFRLm5FxUa2EFE6Rx/KLDTJNLBIp1S2/+Q1K+rG8V+CLvewCc2Cd486rStZqSXEKI7vDnsRs/aMla+N0X/++Q== dependencies: - "@storybook/client-logger" "7.6.17" - "@storybook/preview-api" "7.6.17" - -"@storybook/core-common@7.6.17": - version "7.6.17" - resolved "https://registry.yarnpkg.com/@storybook/core-common/-/core-common-7.6.17.tgz#12760703f08d8f741de0f1fe7026346438251951" - integrity sha512-me2TP3Q9/qzqCLoDHUSsUF+VS1MHxfHbTVF6vAz0D/COTxzsxLpu9TxTbzJoBCxse6XRb6wWI1RgF1mIcjic7g== - dependencies: - "@storybook/core-events" "7.6.17" - "@storybook/node-logger" "7.6.17" - "@storybook/types" "7.6.17" - "@types/find-cache-dir" "^3.2.1" - "@types/node" "^18.0.0" - "@types/node-fetch" "^2.6.4" - "@types/pretty-hrtime" "^1.0.0" + "@storybook/core-events" "8.0.4" + "@storybook/csf-tools" "8.0.4" + "@storybook/node-logger" "8.0.4" + "@storybook/types" "8.0.4" + "@yarnpkg/fslib" "2.10.3" + "@yarnpkg/libzip" "2.3.0" chalk "^4.1.0" - esbuild "^0.18.0" + cross-spawn "^7.0.3" + esbuild "^0.18.0 || ^0.19.0 || ^0.20.0" esbuild-register "^3.5.0" + execa "^5.0.0" file-system-cache "2.3.0" find-cache-dir "^3.0.0" find-up "^5.0.0" @@ -4570,42 +4316,41 @@ pkg-dir "^5.0.0" pretty-hrtime "^1.0.3" resolve-from "^5.0.0" + semver "^7.3.7" + tempy "^1.0.1" + tiny-invariant "^1.3.1" ts-dedent "^2.0.0" + util "^0.12.4" -"@storybook/core-events@7.6.17", "@storybook/core-events@^7.0.0": - version "7.6.17" - resolved "https://registry.yarnpkg.com/@storybook/core-events/-/core-events-7.6.17.tgz#9e1a795558193089fb227cfe2cf768c99418a640" - integrity sha512-AriWMCm/k1cxlv10f+jZ1wavThTRpLaN3kY019kHWbYT9XgaSuLU67G7GPr3cGnJ6HuA6uhbzu8qtqVCd6OfXA== - dependencies: - ts-dedent "^2.0.0" - -"@storybook/core-events@7.6.5": - version "7.6.5" - resolved "https://registry.yarnpkg.com/@storybook/core-events/-/core-events-7.6.5.tgz#bcac8a2625c1f63d290d4ca0b70bb7a953939750" - integrity sha512-zk2q/qicYXAzHA4oV3GDbIql+Kd4TOHUgDE8e4jPCOPp856z2ScqEKUAbiJizs6eEJOH4nW9Db1kuzgrBVEykQ== +"@storybook/core-events@8.0.4", "@storybook/core-events@^8.0.0": + version "8.0.4" + resolved "https://registry.yarnpkg.com/@storybook/core-events/-/core-events-8.0.4.tgz#14e87ffcbf826139226306de9079a84e0286860b" + integrity sha512-1FgLacIGi9i6/fyxw7ZJDC621RK47IMaA3keH4lc11ASRzCSwJ4YOrXjBFjfPc79EF2BuX72DDJNbhj6ynfF3g== dependencies: ts-dedent "^2.0.0" -"@storybook/core-server@7.6.17": - version "7.6.17" - resolved "https://registry.yarnpkg.com/@storybook/core-server/-/core-server-7.6.17.tgz#bf5b7a9db7abe157a14dba6279936e43efa79250" - integrity sha512-KWGhTTaL1Q14FolcoKKZgytlPJUbH6sbJ1Ptj/84EYWFewcnEgVs0Zlnh1VStRZg+Rd1WC1V4yVd/bbDzxrvQA== +"@storybook/core-server@8.0.4": + version "8.0.4" + resolved "https://registry.yarnpkg.com/@storybook/core-server/-/core-server-8.0.4.tgz#c0f94848109137b303cdf2dbc4a9d451ce5732c8" + integrity sha512-/633Pp7LPcDWXkPLSW+W9VUYUbVkdVBG6peXjuzogV0vzdM0dM9af/T0uV2NQxUhzoy6/7QdSDljE+eEOBs2Lw== dependencies: "@aw-web-design/x-default-browser" "1.4.126" + "@babel/core" "^7.23.9" "@discoveryjs/json-ext" "^0.5.3" - "@storybook/builder-manager" "7.6.17" - "@storybook/channels" "7.6.17" - "@storybook/core-common" "7.6.17" - "@storybook/core-events" "7.6.17" + "@storybook/builder-manager" "8.0.4" + "@storybook/channels" "8.0.4" + "@storybook/core-common" "8.0.4" + "@storybook/core-events" "8.0.4" "@storybook/csf" "^0.1.2" - "@storybook/csf-tools" "7.6.17" - "@storybook/docs-mdx" "^0.1.0" + "@storybook/csf-tools" "8.0.4" + "@storybook/docs-mdx" "3.0.0" "@storybook/global" "^5.0.0" - "@storybook/manager" "7.6.17" - "@storybook/node-logger" "7.6.17" - "@storybook/preview-api" "7.6.17" - "@storybook/telemetry" "7.6.17" - "@storybook/types" "7.6.17" + "@storybook/manager" "8.0.4" + "@storybook/manager-api" "8.0.4" + "@storybook/node-logger" "8.0.4" + "@storybook/preview-api" "8.0.4" + "@storybook/telemetry" "8.0.4" + "@storybook/types" "8.0.4" "@types/detect-port" "^1.3.0" "@types/node" "^18.0.0" "@types/pretty-hrtime" "^1.0.0" @@ -4633,38 +4378,38 @@ watchpack "^2.2.0" ws "^8.2.3" -"@storybook/core-webpack@7.6.17": - version "7.6.17" - resolved "https://registry.yarnpkg.com/@storybook/core-webpack/-/core-webpack-7.6.17.tgz#0cca6bd165d4cea0e53856520c6409127869f6b7" - integrity sha512-PyGrFhRM8sTONGwwLWLqBQ1HO+LBnVZ+5TOQO7ejQfdV2FWyNOzjBXm2e5jL/C6XlqiEhmL5pyHEyDBaQJQ3KA== +"@storybook/core-webpack@8.0.4": + version "8.0.4" + resolved "https://registry.yarnpkg.com/@storybook/core-webpack/-/core-webpack-8.0.4.tgz#6b1f861ac173a43b626ff2e98d9090ad31672278" + integrity sha512-sECeoJtT6iFWzgZaQbS1TEZvBrXIT4qb9fa0x2/I5YhCTPnprCNL1yyN90hFQTpdLco5vfQ86YnpzMRntODn7A== dependencies: - "@storybook/core-common" "7.6.17" - "@storybook/node-logger" "7.6.17" - "@storybook/types" "7.6.17" + "@storybook/core-common" "8.0.4" + "@storybook/node-logger" "8.0.4" + "@storybook/types" "8.0.4" "@types/node" "^18.0.0" ts-dedent "^2.0.0" -"@storybook/csf-plugin@7.6.17": - version "7.6.17" - resolved "https://registry.yarnpkg.com/@storybook/csf-plugin/-/csf-plugin-7.6.17.tgz#6acf738b62e14a74a90ef68d7567e2fc1d1bd68f" - integrity sha512-xTHv9BUh3bkDVCvcbmdfVF0/e96BdrEgqPJ3G3RmKbSzWLOkQ2U9yiPfHzT0KJWPhVwj12fjfZp0zunu+pcS6Q== +"@storybook/csf-plugin@8.0.4": + version "8.0.4" + resolved "https://registry.yarnpkg.com/@storybook/csf-plugin/-/csf-plugin-8.0.4.tgz#b5b1a02a51c91e386f3dc8fecdbc02975abe82d0" + integrity sha512-pEgctWuS/qeKMFZJJUM2JuKwjKBt27ye+216ft7xhNqpsrmCgumJYrkU/ii2CsFJU/qr5Fu9EYw+N+vof1OalQ== dependencies: - "@storybook/csf-tools" "7.6.17" + "@storybook/csf-tools" "8.0.4" unplugin "^1.3.1" -"@storybook/csf-tools@7.6.17": - version "7.6.17" - resolved "https://registry.yarnpkg.com/@storybook/csf-tools/-/csf-tools-7.6.17.tgz#366bb2348fc1a62f90cdbd6cce4aa5e7293984eb" - integrity sha512-dAQtam0EBPeTJYcQPLxXgz4L9JFqD+HWbLFG9CmNIhMMjticrB0mpk1EFIS6vPXk/VsVWpBgMLD7dZlD6YMKcQ== +"@storybook/csf-tools@8.0.4": + version "8.0.4" + resolved "https://registry.yarnpkg.com/@storybook/csf-tools/-/csf-tools-8.0.4.tgz#8322a3310d411a16a19efb36127aec5afdd3b298" + integrity sha512-dMSZxWnXBhmXGOZZOAJ4DKZRCYdA0HaqqZ4/eF9MLLsI+qvW4EklcpjVY6bsIzACgubRWtRZkTpxTnjExi/N1A== dependencies: "@babel/generator" "^7.23.0" "@babel/parser" "^7.23.0" "@babel/traverse" "^7.23.2" "@babel/types" "^7.23.0" "@storybook/csf" "^0.1.2" - "@storybook/types" "7.6.17" + "@storybook/types" "8.0.4" fs-extra "^11.1.0" - recast "^0.23.1" + recast "^0.23.5" ts-dedent "^2.0.0" "@storybook/csf@^0.1.2": @@ -4674,19 +4419,19 @@ dependencies: type-fest "^2.19.0" -"@storybook/docs-mdx@^0.1.0": - version "0.1.0" - resolved "https://registry.yarnpkg.com/@storybook/docs-mdx/-/docs-mdx-0.1.0.tgz#33ba0e39d1461caf048b57db354b2cc410705316" - integrity sha512-JDaBR9lwVY4eSH5W8EGHrhODjygPd6QImRbwjAuJNEnY0Vw4ie3bPkeGfnacB3OBW6u/agqPv2aRlR46JcAQLg== +"@storybook/docs-mdx@3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@storybook/docs-mdx/-/docs-mdx-3.0.0.tgz#5c9b5ce35dcb00ad8aa5dddbabf52ad09fab3974" + integrity sha512-NmiGXl2HU33zpwTv1XORe9XG9H+dRUC1Jl11u92L4xr062pZtrShLmD4VKIsOQujxhhOrbxpwhNOt+6TdhyIdQ== -"@storybook/docs-tools@7.6.17": - version "7.6.17" - resolved "https://registry.yarnpkg.com/@storybook/docs-tools/-/docs-tools-7.6.17.tgz#4c38025be46c991bfe994bd82996708210e51d2f" - integrity sha512-bYrLoj06adqklyLkEwD32C0Ww6t+9ZVvrJHiVT42bIhTRpFiFPAetl1a9KPHtFLnfduh4n2IxIr1jv32ThPDTA== +"@storybook/docs-tools@8.0.4": + version "8.0.4" + resolved "https://registry.yarnpkg.com/@storybook/docs-tools/-/docs-tools-8.0.4.tgz#24ebbca47d4075041376c356a5c4d0902f832991" + integrity sha512-PONfG8j/AOHi79NbEkneFRZIscrShbA0sgA+62zeejH4r9+fuIkIKtLnKcAxvr8Bm6uo9aSQbISJZUcBG42WhQ== dependencies: - "@storybook/core-common" "7.6.17" - "@storybook/preview-api" "7.6.17" - "@storybook/types" "7.6.17" + "@storybook/core-common" "8.0.4" + "@storybook/preview-api" "8.0.4" + "@storybook/types" "8.0.4" "@types/doctrine" "^0.0.3" assert "^2.1.0" doctrine "^3.0.0" @@ -4697,88 +4442,74 @@ resolved "https://registry.yarnpkg.com/@storybook/global/-/global-5.0.0.tgz#b793d34b94f572c1d7d9e0f44fac4e0dbc9572ed" integrity sha512-FcOqPAXACP0I3oJ/ws6/rrPT9WGhu915Cg8D02a9YxLo0DE9zI+a9A5gRGvmQ09fiWPukqI8ZAEoQEdWUKMQdQ== -"@storybook/manager-api@7.6.17", "@storybook/manager-api@^7.0.0": - version "7.6.17" - resolved "https://registry.yarnpkg.com/@storybook/manager-api/-/manager-api-7.6.17.tgz#cdf0bb8e5bdc3da2559150125b3d6a3ff72f0def" - integrity sha512-IJIV1Yc6yw1dhCY4tReHCfBnUKDqEBnMyHp3mbXpsaHxnxJZrXO45WjRAZIKlQKhl/Ge1CrnznmHRCmYgqmrWg== +"@storybook/icons@^1.2.5": + version "1.2.9" + resolved "https://registry.yarnpkg.com/@storybook/icons/-/icons-1.2.9.tgz#bb4a51a79e186b62e2dd0e04928b8617ac573838" + integrity sha512-cOmylsz25SYXaJL/gvTk/dl3pyk7yBFRfeXTsHvTA3dfhoU/LWSq0NKL9nM7WBasJyn6XPSGnLS4RtKXLw5EUg== + +"@storybook/instrumenter@8.0.4": + version "8.0.4" + resolved "https://registry.yarnpkg.com/@storybook/instrumenter/-/instrumenter-8.0.4.tgz#ab91a068e9ab5af2a5bafd3377e58513193ca2e7" + integrity sha512-lkHv1na12oMTZvuDbzufgqrtFlV1XqdXrAAg7YXZOia/oMz6Z/XMldEqwLPUCLGVodbFJofrpE67Wtw8dNTDQg== dependencies: - "@storybook/channels" "7.6.17" - "@storybook/client-logger" "7.6.17" - "@storybook/core-events" "7.6.17" - "@storybook/csf" "^0.1.2" + "@storybook/channels" "8.0.4" + "@storybook/client-logger" "8.0.4" + "@storybook/core-events" "8.0.4" "@storybook/global" "^5.0.0" - "@storybook/router" "7.6.17" - "@storybook/theming" "7.6.17" - "@storybook/types" "7.6.17" - dequal "^2.0.2" - lodash "^4.17.21" - memoizerific "^1.11.3" - store2 "^2.14.2" - telejson "^7.2.0" - ts-dedent "^2.0.0" + "@storybook/preview-api" "8.0.4" + "@vitest/utils" "^1.3.1" + util "^0.12.4" -"@storybook/manager-api@7.6.5": - version "7.6.5" - resolved "https://registry.yarnpkg.com/@storybook/manager-api/-/manager-api-7.6.5.tgz#6ddcf6ee3903c119ca1a3fbd41d4fdbf15e5d73a" - integrity sha512-tE3OShOcs6A3XtI3NJd6hYQOZLaP++Fn0dCtowBwYh/vS1EN/AyroVmL97tsxn1DZTyoRt0GidwbB6dvLMBOwA== +"@storybook/manager-api@8.0.4", "@storybook/manager-api@^8.0.0": + version "8.0.4" + resolved "https://registry.yarnpkg.com/@storybook/manager-api/-/manager-api-8.0.4.tgz#46bea1b3b65e3085adad77eb2de29a0d5706ed95" + integrity sha512-TudiRmWlsi8kdjwqW0DDLen76Zp4Sci/AnvTbZvZOWe8C2mruxcr6aaGwuIug6y+uxIyXDvURF6Cek5Twz4isg== dependencies: - "@storybook/channels" "7.6.5" - "@storybook/client-logger" "7.6.5" - "@storybook/core-events" "7.6.5" + "@storybook/channels" "8.0.4" + "@storybook/client-logger" "8.0.4" + "@storybook/core-events" "8.0.4" "@storybook/csf" "^0.1.2" "@storybook/global" "^5.0.0" - "@storybook/router" "7.6.5" - "@storybook/theming" "7.6.5" - "@storybook/types" "7.6.5" + "@storybook/icons" "^1.2.5" + "@storybook/router" "8.0.4" + "@storybook/theming" "8.0.4" + "@storybook/types" "8.0.4" dequal "^2.0.2" lodash "^4.17.21" memoizerific "^1.11.3" - semver "^7.3.7" store2 "^2.14.2" telejson "^7.2.0" - ts-dedent "^2.0.0" - -"@storybook/manager@7.6.17": - version "7.6.17" - resolved "https://registry.yarnpkg.com/@storybook/manager/-/manager-7.6.17.tgz#56e820ede16f6b824ec6b016082d1d10dbb02759" - integrity sha512-A1LDDIqMpwRzq/dqkbbiza0QI04o4ZHCl2a3UMDZUV/+QLc2nsr2DAaLk4CVL4/cIc5zGqmIcaOTvprx2YKVBw== - -"@storybook/mdx2-csf@^1.0.0": - version "1.1.0" - resolved "https://registry.yarnpkg.com/@storybook/mdx2-csf/-/mdx2-csf-1.1.0.tgz#97f6df04d0bf616991cc1005a073ac004a7281e5" - integrity sha512-TXJJd5RAKakWx4BtpwvSNdgTDkKM6RkXU8GK34S/LhidQ5Pjz3wcnqb0TxEkfhK/ztbP8nKHqXFwLfa2CYkvQw== - -"@storybook/node-logger@7.6.17": - version "7.6.17" - resolved "https://registry.yarnpkg.com/@storybook/node-logger/-/node-logger-7.6.17.tgz#2747cee5395c3644408df2423d98502663c4bcf6" - integrity sha512-w59MQuXhhUNrUVmVkXhMwIg2nvFWjdDczLTwYLorhfsE36CWeUOY5QCZWQy0Qf/h+jz8Uo7Evy64qn18v9C4wA== - -"@storybook/postinstall@7.6.17": - version "7.6.17" - resolved "https://registry.yarnpkg.com/@storybook/postinstall/-/postinstall-7.6.17.tgz#7218b416dfa6d36b5bdbd3e61afc9a2381f82c28" - integrity sha512-WaWqB8o9vUc9aaVls+povQSVirf1Xd1LZcVhUKfAocAF3mzYUsnJsVqvnbjRj/F96UFVihOyDt9Zjl/9OvrCvQ== + ts-dedent "^2.0.0" -"@storybook/preset-react-webpack@7.6.17": - version "7.6.17" - resolved "https://registry.yarnpkg.com/@storybook/preset-react-webpack/-/preset-react-webpack-7.6.17.tgz#af112cef0fb93ebfb22363b8a7eec6be7128fde6" - integrity sha512-gn/LvIbll9loOkzwbFlxzOZGmJ6t1vF2/gfi+p/N/AifDYe8+LVM1QV4KRVKt6UEJwsQd79lKf7vPH92AQaKKQ== - dependencies: - "@babel/preset-flow" "^7.22.15" - "@babel/preset-react" "^7.22.15" - "@pmmmwh/react-refresh-webpack-plugin" "^0.5.11" - "@storybook/core-webpack" "7.6.17" - "@storybook/docs-tools" "7.6.17" - "@storybook/node-logger" "7.6.17" - "@storybook/react" "7.6.17" +"@storybook/manager@8.0.4": + version "8.0.4" + resolved "https://registry.yarnpkg.com/@storybook/manager/-/manager-8.0.4.tgz#94c4b27ada34f6d3004b86b6e028a9caf71e7c02" + integrity sha512-M5IofDSxbIQIdAglxUtZOGKjZ1EAq1Mdbh4UolVsF1PKF6dAvBQJLVW6TiLjEbmPBtqgeYKMgrmmYiFNqVcdBQ== + +"@storybook/node-logger@8.0.4": + version "8.0.4" + resolved "https://registry.yarnpkg.com/@storybook/node-logger/-/node-logger-8.0.4.tgz#0701a71dfc0bcfa178e7175a00aadd78b965629c" + integrity sha512-cALLHuX53vLQsoJamGRlquh2pfhPq9copXou2JTmFT6mrCcipo77SzhBDfeeuhaGv6vUWPfmGjPBEHXWGPe4+g== + +"@storybook/preset-react-webpack@8.0.4": + version "8.0.4" + resolved "https://registry.yarnpkg.com/@storybook/preset-react-webpack/-/preset-react-webpack-8.0.4.tgz#17fa28421c6523a86ed638710a16d6657ab8371c" + integrity sha512-XldgoZJOXNbZLGhvP6FqVeRnXigEZXV88uhEveREH4zRceYxXUmiCjFUnFy5aXaYZLcv09GHpqTPCqRoOZ+upg== + dependencies: + "@storybook/core-webpack" "8.0.4" + "@storybook/docs-tools" "8.0.4" + "@storybook/node-logger" "8.0.4" + "@storybook/react" "8.0.4" "@storybook/react-docgen-typescript-plugin" "1.0.6--canary.9.0c3f3b7.0" "@types/node" "^18.0.0" "@types/semver" "^7.3.4" - babel-plugin-add-react-displayname "^0.0.5" + find-up "^5.0.0" fs-extra "^11.1.0" magic-string "^0.30.5" react-docgen "^7.0.0" - react-refresh "^0.14.0" + resolve "^1.22.8" semver "^7.3.7" + tsconfig-paths "^4.2.0" webpack "5" "@storybook/preset-scss@^1.0.3": @@ -4786,50 +4517,30 @@ resolved "https://registry.yarnpkg.com/@storybook/preset-scss/-/preset-scss-1.0.3.tgz#8ac834545c642dada0f64f510ef08dfb882e9737" integrity sha512-o9Iz6wxPeNENrQa2mKlsDKynBfqU2uWaRP80HeWp4TkGgf7/x3DVF2O7yi9N0x/PI1qzzTTpxlQ90D62XmpiTw== -"@storybook/preview-api@7.6.17": - version "7.6.17" - resolved "https://registry.yarnpkg.com/@storybook/preview-api/-/preview-api-7.6.17.tgz#03dd399bf3bb8ac6f4aad3c738365b86b8790157" - integrity sha512-wLfDdI9RWo1f2zzFe54yRhg+2YWyxLZvqdZnSQ45mTs4/7xXV5Wfbv3QNTtcdw8tT3U5KRTrN1mTfTCiRJc0Kw== - dependencies: - "@storybook/channels" "7.6.17" - "@storybook/client-logger" "7.6.17" - "@storybook/core-events" "7.6.17" - "@storybook/csf" "^0.1.2" - "@storybook/global" "^5.0.0" - "@storybook/types" "7.6.17" - "@types/qs" "^6.9.5" - dequal "^2.0.2" - lodash "^4.17.21" - memoizerific "^1.11.3" - qs "^6.10.0" - synchronous-promise "^2.0.15" - ts-dedent "^2.0.0" - util-deprecate "^1.0.2" - -"@storybook/preview-api@7.6.5": - version "7.6.5" - resolved "https://registry.yarnpkg.com/@storybook/preview-api/-/preview-api-7.6.5.tgz#fe2a84d2538a6450395e715a6691926a45d3cdfa" - integrity sha512-9XzuDXXgNuA6dDZ3DXsUwEG6ElxeTbzLuYuzcjtS1FusSICZ2iYmxfS0GfSud9MjPPYOJYoSOvMdIHjorjgByA== +"@storybook/preview-api@8.0.4": + version "8.0.4" + resolved "https://registry.yarnpkg.com/@storybook/preview-api/-/preview-api-8.0.4.tgz#523e6cb7333b2953cd0b68d43c292e391d5b1337" + integrity sha512-uZCgZ/7BZkFTNudCBWx3YPFVdReMQSZJj9EfQVhQaPmfGORHGMvZMRsQXl0ONhPy7zDD4rVQxu5dSKWmIiYoWQ== dependencies: - "@storybook/channels" "7.6.5" - "@storybook/client-logger" "7.6.5" - "@storybook/core-events" "7.6.5" + "@storybook/channels" "8.0.4" + "@storybook/client-logger" "8.0.4" + "@storybook/core-events" "8.0.4" "@storybook/csf" "^0.1.2" "@storybook/global" "^5.0.0" - "@storybook/types" "7.6.5" + "@storybook/types" "8.0.4" "@types/qs" "^6.9.5" dequal "^2.0.2" lodash "^4.17.21" memoizerific "^1.11.3" qs "^6.10.0" - synchronous-promise "^2.0.15" + tiny-invariant "^1.3.1" ts-dedent "^2.0.0" util-deprecate "^1.0.2" -"@storybook/preview@7.6.17": - version "7.6.17" - resolved "https://registry.yarnpkg.com/@storybook/preview/-/preview-7.6.17.tgz#e0c9727c7cfbd8f1d504848a57acaab8e54abe90" - integrity sha512-LvkMYK/y6alGjwRVNDIKL1lFlbyZ0H0c8iAbcQkiMoaFiujMQyVswMDKlWcj42Upfr/B1igydiruomc+eUt0mw== +"@storybook/preview@8.0.4": + version "8.0.4" + resolved "https://registry.yarnpkg.com/@storybook/preview/-/preview-8.0.4.tgz#d1ffc175390bd400c5e41b1d0576c9d58c11a81d" + integrity sha512-dJa13bIxQBfa5ZsXAeL6X/oXI6b87Fy31pvpKPkW1o+7M6MC4OvwGQBqgAd7m8yn6NuIHxrdwjEupa7l7PGb6w== "@storybook/react-docgen-typescript-plugin@1.0.6--canary.9.0c3f3b7.0": version "1.0.6--canary.9.0c3f3b7.0" @@ -4844,33 +4555,32 @@ react-docgen-typescript "^2.2.2" tslib "^2.0.0" -"@storybook/react-dom-shim@7.6.17": - version "7.6.17" - resolved "https://registry.yarnpkg.com/@storybook/react-dom-shim/-/react-dom-shim-7.6.17.tgz#5875915316f687bf658cc6686ea49f2928eae4b2" - integrity sha512-32Sa/G+WnvaPiQ1Wvjjw5UM9rr2c4GDohwCcWVv3/LJuiFPqNS6zglAtmnsrlIBnUwRBMLMh/ekCTdqMiUmfDw== +"@storybook/react-dom-shim@8.0.4": + version "8.0.4" + resolved "https://registry.yarnpkg.com/@storybook/react-dom-shim/-/react-dom-shim-8.0.4.tgz#c935e52f7f7df899232872de7c364c8e25b52713" + integrity sha512-H8bci23e+G40WsdYPuPrhAjCeeXypXuAV6mTVvLHGKH+Yb+3wiB1weaXrot/TgzPbkDNybuhTI3Qm48FPLt0bw== -"@storybook/react-webpack5@7.6.17": - version "7.6.17" - resolved "https://registry.yarnpkg.com/@storybook/react-webpack5/-/react-webpack5-7.6.17.tgz#c1ab808bac41dde4a9bd96c3afc800b914eda9e6" - integrity sha512-qGc2JxaSmvPXV7ndxA/8qPtPLK7lAwejL/QdrzLXhxEmVdZLMew640FBYgOV/zWnehV3BnivThln/8PsQyst/g== +"@storybook/react-webpack5@^8.0.4": + version "8.0.4" + resolved "https://registry.yarnpkg.com/@storybook/react-webpack5/-/react-webpack5-8.0.4.tgz#c37525b5910eb29c8b63dbbfa513e8dcee871b01" + integrity sha512-HZzcIQLWR6pg28CRwNF9VnFWGTEFEnIAAGKbUAc7mMGysN9AgZgz618FZu0W0JmAUBtWwEJ+pYyLJCIOFoDE6w== dependencies: - "@storybook/builder-webpack5" "7.6.17" - "@storybook/preset-react-webpack" "7.6.17" - "@storybook/react" "7.6.17" + "@storybook/builder-webpack5" "8.0.4" + "@storybook/preset-react-webpack" "8.0.4" + "@storybook/react" "8.0.4" "@types/node" "^18.0.0" -"@storybook/react@7.6.17": - version "7.6.17" - resolved "https://registry.yarnpkg.com/@storybook/react/-/react-7.6.17.tgz#3e585b37f4a45d01b60543e1952a46ae3da70e81" - integrity sha512-lVqzQSU03rRJWYW+gK2gq6mSo3/qtnVICY8B8oP7gc36jVu4ksDIu45bTfukM618ODkUZy0vZe6T4engK3azjA== +"@storybook/react@8.0.4", "@storybook/react@^8.0.4": + version "8.0.4" + resolved "https://registry.yarnpkg.com/@storybook/react/-/react-8.0.4.tgz#8789dfbc17103ea996123ec8e91f5fe53438de6e" + integrity sha512-p4wQSJIhG48UD2fZ6tFDT9zaqrVnvZxjV18+VjSi3dez/pDoEMJ3SWZWcmeDenKwvvk+SPdRH7k5mUHW1Rh0xg== dependencies: - "@storybook/client-logger" "7.6.17" - "@storybook/core-client" "7.6.17" - "@storybook/docs-tools" "7.6.17" + "@storybook/client-logger" "8.0.4" + "@storybook/docs-tools" "8.0.4" "@storybook/global" "^5.0.0" - "@storybook/preview-api" "7.6.17" - "@storybook/react-dom-shim" "7.6.17" - "@storybook/types" "7.6.17" + "@storybook/preview-api" "8.0.4" + "@storybook/react-dom-shim" "8.0.4" + "@storybook/types" "8.0.4" "@types/escodegen" "^0.0.6" "@types/estree" "^0.0.51" "@types/node" "^18.0.0" @@ -4882,159 +4592,70 @@ lodash "^4.17.21" prop-types "^15.7.2" react-element-to-jsx-string "^15.0.0" + semver "^7.3.7" ts-dedent "^2.0.0" type-fest "~2.19" util-deprecate "^1.0.2" -"@storybook/router@7.6.17": - version "7.6.17" - resolved "https://registry.yarnpkg.com/@storybook/router/-/router-7.6.17.tgz#de5016086191846ed12af7495aeddcc373cbd0d4" - integrity sha512-GnyC0j6Wi5hT4qRhSyT8NPtJfGmf82uZw97LQRWeyYu5gWEshUdM7aj40XlNiScd5cZDp0owO1idduVF2k2l2A== - dependencies: - "@storybook/client-logger" "7.6.17" - memoizerific "^1.11.3" - qs "^6.10.0" - -"@storybook/router@7.6.5": - version "7.6.5" - resolved "https://registry.yarnpkg.com/@storybook/router/-/router-7.6.5.tgz#9dfc5f8844e254fc14524373d48e0c357f3ca553" - integrity sha512-QiTC86gRuoepzzmS6HNJZTwfz/n27NcqtaVEIxJi1Yvsx2/kLa9NkRhylNkfTuZ1gEry9stAlKWanMsB2aKyjQ== +"@storybook/router@8.0.4": + version "8.0.4" + resolved "https://registry.yarnpkg.com/@storybook/router/-/router-8.0.4.tgz#37eb06023e74703703f3c93f4b4d1bbfe38e50c0" + integrity sha512-hlR80QvmLBflAqMeGcgtDuSe6TJlzdizwEAkBLE1lDvFI6tvvEyAliCAXBpIDdOZTe0u/zeeJkOUXKSx33caoQ== dependencies: - "@storybook/client-logger" "7.6.5" + "@storybook/client-logger" "8.0.4" memoizerific "^1.11.3" qs "^6.10.0" -"@storybook/telemetry@7.6.17": - version "7.6.17" - resolved "https://registry.yarnpkg.com/@storybook/telemetry/-/telemetry-7.6.17.tgz#472dd6a8d87240c1fcc01bb9d6247e134e539b5b" - integrity sha512-WOcOAmmengYnGInH98Px44F47DSpLyk20BM+Z/IIQDzfttGOLlxNqBBG1XTEhNRn+AYuk4aZ2JEed2lCjVIxcA== +"@storybook/telemetry@8.0.4": + version "8.0.4" + resolved "https://registry.yarnpkg.com/@storybook/telemetry/-/telemetry-8.0.4.tgz#39a39b372db7173e04d61092e27603c377f9c66a" + integrity sha512-Q3ITY6J46R/TrrPRIU1fs3WNs69ExpTJZ9UlB8087qOUyV90Ex33SYk3i10xVWRczxCmyC1V58Xuht6nxz7mNQ== dependencies: - "@storybook/client-logger" "7.6.17" - "@storybook/core-common" "7.6.17" - "@storybook/csf-tools" "7.6.17" + "@storybook/client-logger" "8.0.4" + "@storybook/core-common" "8.0.4" + "@storybook/csf-tools" "8.0.4" chalk "^4.1.0" detect-package-manager "^2.0.1" fetch-retry "^5.0.2" fs-extra "^11.1.0" read-pkg-up "^7.0.1" -"@storybook/testing-library@^0.2.2": - version "0.2.2" - resolved "https://registry.yarnpkg.com/@storybook/testing-library/-/testing-library-0.2.2.tgz#c8e089cc8d7354f6066fdb580fae3eedf568aa7c" - integrity sha512-L8sXFJUHmrlyU2BsWWZGuAjv39Jl1uAqUHdxmN42JY15M4+XCMjGlArdCCjDe1wpTSW6USYISA9axjZojgtvnw== - dependencies: - "@testing-library/dom" "^9.0.0" - "@testing-library/user-event" "^14.4.0" - ts-dedent "^2.2.0" - -"@storybook/theming@7.6.17", "@storybook/theming@^7.0.0": - version "7.6.17" - resolved "https://registry.yarnpkg.com/@storybook/theming/-/theming-7.6.17.tgz#8170e3e72b921380c51a3970890d4cb479a65c2f" - integrity sha512-ZbaBt3KAbmBtfjNqgMY7wPMBshhSJlhodyMNQypv+95xLD/R+Az6aBYbpVAOygLaUQaQk4ar7H/Ww6lFIoiFbA== - dependencies: - "@emotion/use-insertion-effect-with-fallbacks" "^1.0.0" - "@storybook/client-logger" "7.6.17" - "@storybook/global" "^5.0.0" - memoizerific "^1.11.3" +"@storybook/test@^8.0.4": + version "8.0.4" + resolved "https://registry.yarnpkg.com/@storybook/test/-/test-8.0.4.tgz#62806e71e11f881f80847f67f3db2214907b6eb4" + integrity sha512-/uvE8Rtu7tIcuyQBUzKq7uuDCsjmADI18BApLdwo/qthmN8ERDxRSz0Ngj2gvBMQFv99At8ESi/xh6oFGu3rWg== + dependencies: + "@storybook/client-logger" "8.0.4" + "@storybook/core-events" "8.0.4" + "@storybook/instrumenter" "8.0.4" + "@storybook/preview-api" "8.0.4" + "@testing-library/dom" "^9.3.4" + "@testing-library/jest-dom" "^6.4.2" + "@testing-library/user-event" "^14.5.2" + "@vitest/expect" "1.3.1" + "@vitest/spy" "^1.3.1" + chai "^4.4.1" + util "^0.12.4" -"@storybook/theming@7.6.5": - version "7.6.5" - resolved "https://registry.yarnpkg.com/@storybook/theming/-/theming-7.6.5.tgz#b73e81c6ca8b136d38bbb3bf4cd0d7a4e373d813" - integrity sha512-RpcWT0YEgiobO41McVPDfQQHHFnjyr1sJnNTPJIvOUgSfURdgSj17mQVxtD5xcXcPWUdle5UhIOrCixHbL/NNw== +"@storybook/theming@8.0.4", "@storybook/theming@^8.0.0", "@storybook/theming@^8.0.4": + version "8.0.4" + resolved "https://registry.yarnpkg.com/@storybook/theming/-/theming-8.0.4.tgz#e72aa3b5d27d3db5ed769782ac91d6fdcb1c830e" + integrity sha512-NxtTU2wMC0lj375ejoT3Npdcqwv6NeUpLaJl6EZCMXSR41ve9WG4suUNWQ63olhqKxirjzAz0IL7ggH7c3hPvA== dependencies: - "@emotion/use-insertion-effect-with-fallbacks" "^1.0.0" - "@storybook/client-logger" "7.6.5" + "@emotion/use-insertion-effect-with-fallbacks" "^1.0.1" + "@storybook/client-logger" "8.0.4" "@storybook/global" "^5.0.0" memoizerific "^1.11.3" -"@storybook/types@7.6.17": - version "7.6.17" - resolved "https://registry.yarnpkg.com/@storybook/types/-/types-7.6.17.tgz#0b3c27cb1708c0545a9ea1a23b73aa8852dd47c4" - integrity sha512-GRY0xEJQ0PrL7DY2qCNUdIfUOE0Gsue6N+GBJw9ku1IUDFLJRDOF+4Dx2BvYcVCPI5XPqdWKlEyZdMdKjiQN7Q== - dependencies: - "@storybook/channels" "7.6.17" - "@types/babel__core" "^7.0.0" - "@types/express" "^4.7.0" - file-system-cache "2.3.0" - -"@storybook/types@7.6.5": - version "7.6.5" - resolved "https://registry.yarnpkg.com/@storybook/types/-/types-7.6.5.tgz#25c20d6bb350117f740b7f1f138f354f25d506bc" - integrity sha512-Q757v+fYZZSaEpks/zDL5YgXRozxkgKakXFc+BoQHK5q5sVhJ+0jvpLJiAQAniIIaMIkqY/G24Kd6Uo6UdKBCg== +"@storybook/types@8.0.4": + version "8.0.4" + resolved "https://registry.yarnpkg.com/@storybook/types/-/types-8.0.4.tgz#650d87808ecab4ba7325fecef5a6f63ac6226f27" + integrity sha512-OO7QY+qZFCYkItDUBACtIV32p75O7sNziAiyS1V2Oxgo7Ln7fwZwr3mJcA1ruBed6ZcrW3c87k7Xs40T2zAWcg== dependencies: - "@storybook/channels" "7.6.5" - "@types/babel__core" "^7.0.0" + "@storybook/channels" "8.0.4" "@types/express" "^4.7.0" file-system-cache "2.3.0" -"@swc/core-darwin-arm64@1.3.100": - version "1.3.100" - resolved "https://registry.yarnpkg.com/@swc/core-darwin-arm64/-/core-darwin-arm64-1.3.100.tgz#f582c5bbc9c49506f728fc1d14dff33c2cc226d5" - integrity sha512-XVWFsKe6ei+SsDbwmsuRkYck1SXRpO60Hioa4hoLwR8fxbA9eVp6enZtMxzVVMBi8ej5seZ4HZQeAWepbukiBw== - -"@swc/core-darwin-x64@1.3.100": - version "1.3.100" - resolved "https://registry.yarnpkg.com/@swc/core-darwin-x64/-/core-darwin-x64-1.3.100.tgz#d84f5c0bb4603c252884d011a698ed7c634b1505" - integrity sha512-KF/MXrnH1nakm1wbt4XV8FS7kvqD9TGmVxeJ0U4bbvxXMvzeYUurzg3AJUTXYmXDhH/VXOYJE5N5RkwZZPs5iA== - -"@swc/core-linux-arm64-gnu@1.3.100": - version "1.3.100" - resolved "https://registry.yarnpkg.com/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.3.100.tgz#1ed4b92b373882d8f338c4e0a0aa64cdaa6106f1" - integrity sha512-p8hikNnAEJrw5vHCtKiFT4hdlQxk1V7vqPmvUDgL/qe2menQDK/i12tbz7/3BEQ4UqUPnvwpmVn2d19RdEMNxw== - -"@swc/core-linux-arm64-musl@1.3.100": - version "1.3.100" - resolved "https://registry.yarnpkg.com/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.3.100.tgz#9db560f7459e42e65ec02670d6a8316e7c850cfc" - integrity sha512-BWx/0EeY89WC4q3AaIaBSGfQxkYxIlS3mX19dwy2FWJs/O+fMvF9oLk/CyJPOZzbp+1DjGeeoGFuDYpiNO91JA== - -"@swc/core-linux-x64-gnu@1.3.100": - version "1.3.100" - resolved "https://registry.yarnpkg.com/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.3.100.tgz#228826ea48879bf1e73683fbef4373e3e762e424" - integrity sha512-XUdGu3dxAkjsahLYnm8WijPfKebo+jHgHphDxaW0ovI6sTdmEGFDew7QzKZRlbYL2jRkUuuKuDGvD6lO5frmhA== - -"@swc/core-linux-x64-musl@1.3.100": - version "1.3.100" - resolved "https://registry.yarnpkg.com/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.3.100.tgz#09a234dbbf625d071ecb663680e997a62d230d49" - integrity sha512-PhoXKf+f0OaNW/GCuXjJ0/KfK9EJX7z2gko+7nVnEA0p3aaPtbP6cq1Ubbl6CMoPL+Ci3gZ7nYumDqXNc3CtLQ== - -"@swc/core-win32-arm64-msvc@1.3.100": - version "1.3.100" - resolved "https://registry.yarnpkg.com/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.3.100.tgz#add1c82884c10a9054ed6a48f884097aa85c6d2b" - integrity sha512-PwLADZN6F9cXn4Jw52FeP/MCLVHm8vwouZZSOoOScDtihjY495SSjdPnlosMaRSR4wJQssGwiD/4MbpgQPqbAw== - -"@swc/core-win32-ia32-msvc@1.3.100": - version "1.3.100" - resolved "https://registry.yarnpkg.com/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.3.100.tgz#e0b6c5ae7f3250adeeb88dae83558d3f45148c56" - integrity sha512-0f6nicKSLlDKlyPRl2JEmkpBV4aeDfRQg6n8mPqgL7bliZIcDahG0ej+HxgNjZfS3e0yjDxsNRa6sAqWU2Z60A== - -"@swc/core-win32-x64-msvc@1.3.100": - version "1.3.100" - resolved "https://registry.yarnpkg.com/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.3.100.tgz#34721dff151d7dcf165675f18aeed0a12264d88c" - integrity sha512-b7J0rPoMkRTa3XyUGt8PwCaIBuYWsL2DqbirrQKRESzgCvif5iNpqaM6kjIjI/5y5q1Ycv564CB51YDpiS8EtQ== - -"@swc/core@^1.3.82": - version "1.3.100" - resolved "https://registry.yarnpkg.com/@swc/core/-/core-1.3.100.tgz#8fa36f26a35137620234b084224c9fa9b8a0fee2" - integrity sha512-7dKgTyxJjlrMwFZYb1auj3Xq0D8ZBe+5oeIgfMlRU05doXZypYJe0LAk0yjj3WdbwYzpF+T1PLxwTWizI0pckw== - dependencies: - "@swc/counter" "^0.1.1" - "@swc/types" "^0.1.5" - optionalDependencies: - "@swc/core-darwin-arm64" "1.3.100" - "@swc/core-darwin-x64" "1.3.100" - "@swc/core-linux-arm64-gnu" "1.3.100" - "@swc/core-linux-arm64-musl" "1.3.100" - "@swc/core-linux-x64-gnu" "1.3.100" - "@swc/core-linux-x64-musl" "1.3.100" - "@swc/core-win32-arm64-msvc" "1.3.100" - "@swc/core-win32-ia32-msvc" "1.3.100" - "@swc/core-win32-x64-msvc" "1.3.100" - -"@swc/counter@^0.1.1": - version "0.1.2" - resolved "https://registry.yarnpkg.com/@swc/counter/-/counter-0.1.2.tgz#bf06d0770e47c6f1102270b744e17b934586985e" - integrity sha512-9F4ys4C74eSTEUNndnER3VJ15oru2NumfQxS8geE+f3eB5xvfxpWyqE5XlVnxb/R14uoXi6SLbBwwiDSkv+XEw== - "@swc/helpers@^0.4.14": version "0.4.36" resolved "https://registry.yarnpkg.com/@swc/helpers/-/helpers-0.4.36.tgz#fcfff76ed52c214f357e8e9d3f37b568908072d9" @@ -5050,11 +4671,6 @@ dependencies: tslib "^2.4.0" -"@swc/types@^0.1.5": - version "0.1.5" - resolved "https://registry.yarnpkg.com/@swc/types/-/types-0.1.5.tgz#043b731d4f56a79b4897a3de1af35e75d56bc63a" - integrity sha512-myfUej5naTBWnqOCc/MdVOLVjXUXtIA+NpDrDBKJtLLg2shUjBu3cZmB/85RyitKc55+lUUyl7oRfLOvkr2hsw== - "@testing-library/dom@^8.0.0": version "8.20.1" resolved "https://registry.yarnpkg.com/@testing-library/dom/-/dom-8.20.1.tgz#2e52a32e46fc88369eef7eef634ac2a192decd9f" @@ -5069,10 +4685,10 @@ lz-string "^1.5.0" pretty-format "^27.0.2" -"@testing-library/dom@^9.0.0": - version "9.3.3" - resolved "https://registry.yarnpkg.com/@testing-library/dom/-/dom-9.3.3.tgz#108c23a5b0ef51121c26ae92eb3179416b0434f5" - integrity sha512-fB0R+fa3AUqbLHWyxXa2kGVtf1Fe1ZZFr0Zp6AIbIAzXb2mKbEXl+PCQNUOaq5lbTab5tfctfXRNsWXxa2f7Aw== +"@testing-library/dom@^9.3.4": + version "9.3.4" + resolved "https://registry.yarnpkg.com/@testing-library/dom/-/dom-9.3.4.tgz#50696ec28376926fec0a1bf87d9dbac5e27f60ce" + integrity sha512-FlS4ZWlp97iiNWig0Muq8p+3rVDjRiYE+YKGbAqXOu9nwJFFOdL00kFpz42M+4huzYi86vAK1sOOfyOG45muIQ== dependencies: "@babel/code-frame" "^7.10.4" "@babel/runtime" "^7.12.5" @@ -5098,6 +4714,20 @@ lodash "^4.17.15" redent "^3.0.0" +"@testing-library/jest-dom@^6.4.2": + version "6.4.2" + resolved "https://registry.yarnpkg.com/@testing-library/jest-dom/-/jest-dom-6.4.2.tgz#38949f6b63722900e2d75ba3c6d9bf8cffb3300e" + integrity sha512-CzqH0AFymEMG48CpzXFriYYkOjk6ZGPCLMhW9e9jg3KMCn5OfJecF8GtGW7yGfR/IgCe3SX8BSwjdzI6BBbZLw== + dependencies: + "@adobe/css-tools" "^4.3.2" + "@babel/runtime" "^7.9.2" + aria-query "^5.0.0" + chalk "^3.0.0" + css.escape "^1.5.1" + dom-accessibility-api "^0.6.3" + lodash "^4.17.15" + redent "^3.0.0" + "@testing-library/react@12.1.5": version "12.1.5" resolved "https://registry.yarnpkg.com/@testing-library/react/-/react-12.1.5.tgz#bb248f72f02a5ac9d949dea07279095fa577963b" @@ -5107,7 +4737,7 @@ "@testing-library/dom" "^8.0.0" "@types/react-dom" "<18.0.0" -"@testing-library/user-event@^14.4.0", "@testing-library/user-event@^14.4.3": +"@testing-library/user-event@^14.4.3", "@testing-library/user-event@^14.5.2": version "14.5.2" resolved "https://registry.yarnpkg.com/@testing-library/user-event/-/user-event-14.5.2.tgz#db7257d727c891905947bd1c1a99da20e03c2ebd" integrity sha512-YAh82Wh4TIrxYLmfGcixwD18oIjyC1pFQC2Y01F2lzV2HTMiYrI0nze0FD0ocB//CKS/7jIUgae+adPqxK5yCQ== @@ -5134,7 +4764,7 @@ resolved "https://registry.yarnpkg.com/@types/aria-query/-/aria-query-5.0.4.tgz#1a31c3d378850d2778dabb6374d036dcba4ba708" integrity sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw== -"@types/babel__core@^7.0.0", "@types/babel__core@^7.1.14", "@types/babel__core@^7.18.0": +"@types/babel__core@^7.1.14", "@types/babel__core@^7.18.0": version "7.20.5" resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.20.5.tgz#3df15f27ba85319caa07ba08d0721889bb39c017" integrity sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA== @@ -5290,11 +4920,6 @@ "@types/qs" "*" "@types/serve-static" "*" -"@types/find-cache-dir@^3.2.1": - version "3.2.1" - resolved "https://registry.yarnpkg.com/@types/find-cache-dir/-/find-cache-dir-3.2.1.tgz#7b959a4b9643a1e6a1a5fe49032693cc36773501" - integrity sha512-frsJrz2t/CeGifcu/6uRo4b+SzAwT4NYCVPu1GN8IB9XTzrpPkGuV0tmh9mN+/L0PklAlsC3u5Fxt0ju00LXIw== - "@types/fined@*": version "1.1.5" resolved "https://registry.yarnpkg.com/@types/fined/-/fined-1.1.5.tgz#504b87a0de8813e06e7d226f34c1cefb70d9afb0" @@ -5320,6 +4945,13 @@ dependencies: "@types/node" "*" +"@types/hast@^3.0.0": + version "3.0.4" + resolved "https://registry.yarnpkg.com/@types/hast/-/hast-3.0.4.tgz#1d6b39993b82cea6ad783945b0508c25903e15aa" + integrity sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ== + dependencies: + "@types/unist" "*" + "@types/html-minifier-terser@^6.0.0": version "6.1.0" resolved "https://registry.yarnpkg.com/@types/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz#4fc33a00c1d0c16987b1a20cf92d20614c55ac35" @@ -5404,11 +5036,6 @@ resolved "https://registry.yarnpkg.com/@types/mdx/-/mdx-2.0.10.tgz#0d7b57fb1d83e27656156e4ee0dfba96532930e4" integrity sha512-Rllzc5KHk0Al5/WANwgSPl1/CwjqCy+AZrGd78zuK+jO9aDM6ffblZ+zIjgPNAaEBmlO0RYDvLNh7wD0zKVgEg== -"@types/mime-types@^2.1.0": - version "2.1.4" - resolved "https://registry.yarnpkg.com/@types/mime-types/-/mime-types-2.1.4.tgz#93a1933e24fed4fb9e4adc5963a63efcbb3317a2" - integrity sha512-lfU4b34HOri+kAY5UheuFMWPDOI+OPceBSHZKp69gEyTL/mmJ4cnU6Y/rlme3UL3GyOn6Y42hyIEw0/q8sWx5w== - "@types/mime@*": version "3.0.4" resolved "https://registry.yarnpkg.com/@types/mime/-/mime-3.0.4.tgz#2198ac274de6017b44d941e00261d5bc6a0e0a45" @@ -5424,14 +5051,6 @@ resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-5.1.2.tgz#07508b45797cb81ec3f273011b054cd0755eddca" integrity sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA== -"@types/node-fetch@^2.6.4": - version "2.6.9" - resolved "https://registry.yarnpkg.com/@types/node-fetch/-/node-fetch-2.6.9.tgz#15f529d247f1ede1824f7e7acdaa192d5f28071e" - integrity sha512-bQVlnMLFJ2d35DkPNjEPmd9ueO/rh5EiaZt2bhqiSarPjZIuIV6bPQVqcrEyvNo+AfTrRGVazle1tl597w3gfA== - dependencies: - "@types/node" "*" - form-data "^4.0.0" - "@types/node-forge@^1.3.0": version "1.3.10" resolved "https://registry.yarnpkg.com/@types/node-forge/-/node-forge-1.3.10.tgz#62a19d4f75a8b03290578c2b04f294b1a5a71b07" @@ -5490,7 +5109,7 @@ dependencies: "@types/react" "^17" -"@types/react@17.0.50", "@types/react@>=16", "@types/react@^17": +"@types/react@17.0.50", "@types/react@^16.8.0 || ^17.0.0 || ^18.0.0", "@types/react@^17": version "17.0.50" resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.50.tgz#39abb4f7098f546cfcd6b51207c90c4295ee81fc" integrity sha512-ZCBHzpDb5skMnc1zFXAXnL3l1FAdi+xZvwxK+PkglMmBrwjpp9nKaWuEvrGnSifCJmBFGxZOOFuwC6KH/s0NuA== @@ -5574,10 +5193,10 @@ resolved "https://registry.yarnpkg.com/@types/tough-cookie/-/tough-cookie-4.0.5.tgz#cb6e2a691b70cb177c6e3ae9c1d2e8b2ea8cd304" integrity sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA== -"@types/unist@^2.0.0": - version "2.0.10" - resolved "https://registry.yarnpkg.com/@types/unist/-/unist-2.0.10.tgz#04ffa7f406ab628f7f7e97ca23e290cd8ab15efc" - integrity sha512-IfYcSBWE3hLpBg8+X2SEa8LVkJdJEkT2Ese2aaLs3ptGdVtABxndrMaxuFlQ1qdFf9Q5rDvDpxI3WwgvKFAsQA== +"@types/unist@*", "@types/unist@^3.0.0": + version "3.0.2" + resolved "https://registry.yarnpkg.com/@types/unist/-/unist-3.0.2.tgz#6dd61e43ef60b34086287f83683a5c1b2dc53d20" + integrity sha512-dqId9J8K/vGi5Zr7oo212BGii5m3q5Hxlkwy3WpYuKPklmBEvsbMYYyLxAQpSffdLl/gdW0XUpKWFvYmyoWCoQ== "@types/uuid@^9.0.1": version "9.0.7" @@ -5737,11 +5356,54 @@ "@typescript-eslint/types" "6.21.0" eslint-visitor-keys "^3.4.1" -"@ungap/structured-clone@^1.2.0": +"@ungap/structured-clone@^1.0.0", "@ungap/structured-clone@^1.2.0": version "1.2.0" resolved "https://registry.yarnpkg.com/@ungap/structured-clone/-/structured-clone-1.2.0.tgz#756641adb587851b5ccb3e095daf27ae581c8406" integrity sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ== +"@vitest/expect@1.3.1": + version "1.3.1" + resolved "https://registry.yarnpkg.com/@vitest/expect/-/expect-1.3.1.tgz#d4c14b89c43a25fd400a6b941f51ba27fe0cb918" + integrity sha512-xofQFwIzfdmLLlHa6ag0dPV8YsnKOCP1KdAeVVh34vSjN2dcUiXYCD9htu/9eM7t8Xln4v03U9HLxLpPlsXdZw== + dependencies: + "@vitest/spy" "1.3.1" + "@vitest/utils" "1.3.1" + chai "^4.3.10" + +"@vitest/spy@1.3.1": + version "1.3.1" + resolved "https://registry.yarnpkg.com/@vitest/spy/-/spy-1.3.1.tgz#814245d46d011b99edd1c7528f5725c64e85a88b" + integrity sha512-xAcW+S099ylC9VLU7eZfdT9myV67Nor9w9zhf0mGCYJSO+zM2839tOeROTdikOi/8Qeusffvxb/MyBSOja1Uig== + dependencies: + tinyspy "^2.2.0" + +"@vitest/spy@^1.3.1": + version "1.4.0" + resolved "https://registry.yarnpkg.com/@vitest/spy/-/spy-1.4.0.tgz#cf953c93ae54885e801cbe6b408a547ae613f26c" + integrity sha512-Ywau/Qs1DzM/8Uc+yA77CwSegizMlcgTJuYGAi0jujOteJOUf1ujunHThYo243KG9nAyWT3L9ifPYZ5+As/+6Q== + dependencies: + tinyspy "^2.2.0" + +"@vitest/utils@1.3.1": + version "1.3.1" + resolved "https://registry.yarnpkg.com/@vitest/utils/-/utils-1.3.1.tgz#7b05838654557544f694a372de767fcc9594d61a" + integrity sha512-d3Waie/299qqRyHTm2DjADeTaNdNSVsnwHPWrs20JMpjh6eiVq7ggggweO8rc4arhf6rRkWuHKwvxGvejUXZZQ== + dependencies: + diff-sequences "^29.6.3" + estree-walker "^3.0.3" + loupe "^2.3.7" + pretty-format "^29.7.0" + +"@vitest/utils@^1.3.1": + version "1.4.0" + resolved "https://registry.yarnpkg.com/@vitest/utils/-/utils-1.4.0.tgz#ea6297e0d329f9ff0a106f4e1f6daf3ff6aad3f0" + integrity sha512-mx3Yd1/6e2Vt/PUC98DcqTirtfxUyAZ32uK82r8rZzbtBeBo+nqgnjx/LvqQdWsrvNtm14VmurNgcf4nqY5gJg== + dependencies: + diff-sequences "^29.6.3" + estree-walker "^3.0.3" + loupe "^2.3.7" + pretty-format "^29.7.0" + "@webassemblyjs/ast@1.11.6", "@webassemblyjs/ast@^1.11.5": version "1.11.6" resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.11.6.tgz#db046555d3c413f8966ca50a95176a0e2c642e24" @@ -5979,11 +5641,6 @@ address@^1.0.1: resolved "https://registry.yarnpkg.com/address/-/address-1.2.2.tgz#2b5248dac5485a6390532c6a517fda2e3faac89e" integrity sha512-4B/qKCfeE/ODUaAUpSwfzazo5x29WD4r3vXiWsB7I2mSDAihwEqKO+g8GELZUQSSAo5e1XTYh3ZVfLyxBc12nA== -agent-base@5: - version "5.1.1" - resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-5.1.1.tgz#e8fb3f242959db44d63be665db7a8e739537a32c" - integrity sha512-TMeqbNl2fMW0nMjTEPOwe3J/PRFP4vqeoNuQMG0HlMrtm5QxKqdvAkZ1pRBQ/ulIyDD5Yq0nJ7YbdD8ey0TO3g== - agent-base@6: version "6.0.2" resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.2.tgz#49fff58577cfee3f37176feab4c22e00f86d7f77" @@ -6129,13 +5786,6 @@ argparse@^2.0.1: resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== -aria-hidden@^1.1.1: - version "1.2.3" - resolved "https://registry.yarnpkg.com/aria-hidden/-/aria-hidden-1.2.3.tgz#14aeb7fb692bbb72d69bebfa47279c1fd725e954" - integrity sha512-xcLxITLe2HYa1cnYnwCjkOO1PqUHQpozB8x9AR0OgWN2woOBi5kSDVxKfd0b7sb1hw5qFeJhXm9H1nu3xSfLeQ== - dependencies: - tslib "^2.0.0" - aria-query@5.1.3: version "5.1.3" resolved "https://registry.yarnpkg.com/aria-query/-/aria-query-5.1.3.tgz#19db27cd101152773631396f7a95a3b58c22c35e" @@ -6261,6 +5911,11 @@ assert@^2.0.0, assert@^2.1.0: object.assign "^4.1.4" util "^0.12.5" +assertion-error@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/assertion-error/-/assertion-error-1.1.0.tgz#e60b6b0e8f301bd97e5375215bda406c85118c0b" + integrity sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw== + ast-types-flow@^0.0.8: version "0.0.8" resolved "https://registry.yarnpkg.com/ast-types-flow/-/ast-types-flow-0.0.8.tgz#0a85e1c92695769ac13a428bb653e7538bea27d6" @@ -6273,11 +5928,6 @@ ast-types@^0.16.1: dependencies: tslib "^2.0.1" -async-limiter@~1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.1.tgz#dd379e94f0db8310b08291f9d64c3209766617fd" - integrity sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ== - async@^3.2.3: version "3.2.5" resolved "https://registry.yarnpkg.com/async/-/async-3.2.5.tgz#ebd52a8fdaf7a2289a24df399f8d8485c8a46b66" @@ -6330,7 +5980,7 @@ babel-jest@^29.3.1, babel-jest@^29.7.0: graceful-fs "^4.2.9" slash "^3.0.0" -babel-loader@^9.0.0, babel-loader@^9.1.0: +babel-loader@^9.1.0, babel-loader@^9.1.3: version "9.1.3" resolved "https://registry.yarnpkg.com/babel-loader/-/babel-loader-9.1.3.tgz#3d0e01b4e69760cc694ee306fe16d358aa1c6f9a" integrity sha512-xG3ST4DglodGf8qSwv0MdeWLhrDsw/32QMdTO5T1ZIp9gQur0HkCyFs7Awskr10JKXFXwpAhiCuYX5oGXnRGbw== @@ -6338,11 +5988,6 @@ babel-loader@^9.0.0, babel-loader@^9.1.0: find-cache-dir "^4.0.0" schema-utils "^4.0.0" -babel-plugin-add-react-displayname@^0.0.5: - version "0.0.5" - resolved "https://registry.yarnpkg.com/babel-plugin-add-react-displayname/-/babel-plugin-add-react-displayname-0.0.5.tgz#339d4cddb7b65fd62d1df9db9fe04de134122bd5" - integrity sha512-LY3+Y0XVDYcShHHorshrDbt4KFWL4bSeniCtl4SYZbask+Syngk1uMPCeN9+nSiZo6zX5s0RTq/J9Pnaaf/KHw== - babel-plugin-istanbul@^6.1.1: version "6.1.1" resolved "https://registry.yarnpkg.com/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz#fa88ec59232fd9b4e36dbbc540a8ec9a9b47da73" @@ -6558,11 +6203,6 @@ bser@2.1.1: dependencies: node-int64 "^0.4.0" -buffer-crc32@~0.2.3: - version "0.2.13" - resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242" - integrity sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ== - buffer-from@^1.0.0: version "1.1.2" resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" @@ -6642,6 +6282,19 @@ case-sensitive-paths-webpack-plugin@^2.4.0: resolved "https://registry.yarnpkg.com/case-sensitive-paths-webpack-plugin/-/case-sensitive-paths-webpack-plugin-2.4.0.tgz#db64066c6422eed2e08cc14b986ca43796dbc6d4" integrity sha512-roIFONhcxog0JSSWbvVAh3OocukmSgpqOH6YpMkCvav/ySIV3JKg4Dc8vYtQjYi/UxpNE36r/9v+VqTQqgkYmw== +chai@^4.3.10, chai@^4.4.1: + version "4.4.1" + resolved "https://registry.yarnpkg.com/chai/-/chai-4.4.1.tgz#3603fa6eba35425b0f2ac91a009fe924106e50d1" + integrity sha512-13sOfMv2+DWduEU+/xbun3LScLoqN17nBeTLUsmDfKdoiC1fr0n9PU4guu4AhRcOVFk/sW8LyZWHuhWtQZiF+g== + dependencies: + assertion-error "^1.1.0" + check-error "^1.0.3" + deep-eql "^4.1.3" + get-func-name "^2.0.2" + loupe "^2.3.6" + pathval "^1.1.1" + type-detect "^4.0.8" + chalk@^2.0.1, chalk@^2.1.0, chalk@^2.3.2, chalk@^2.4.2: version "2.4.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" @@ -6700,6 +6353,13 @@ chardet@^0.7.0: resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e" integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA== +check-error@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/check-error/-/check-error-1.0.3.tgz#a6502e4312a7ee969f646e83bb3ddd56281bd694" + integrity sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg== + dependencies: + get-func-name "^2.0.2" + chokidar@^3.4.0, chokidar@^3.5.3: version "3.5.3" resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd" @@ -6953,16 +6613,6 @@ concat-map@0.0.1: resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== -concat-stream@^1.6.2: - version "1.6.2" - resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.2.tgz#904bdf194cd3122fc675c77fc4ac3d4ff0fd1a34" - integrity sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw== - dependencies: - buffer-from "^1.0.0" - inherits "^2.0.3" - readable-stream "^2.2.2" - typedarray "^0.0.6" - concurrently@^8.0.0: version "8.2.2" resolved "https://registry.yarnpkg.com/concurrently/-/concurrently-8.2.2.tgz#353141985c198cfa5e4a3ef90082c336b5851784" @@ -7031,11 +6681,6 @@ core-js-compat@^3.31.0, core-js-compat@^3.33.1: dependencies: browserslist "^4.22.2" -core-js-pure@^3.23.3: - version "3.34.0" - resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.34.0.tgz#981e462500708664c91b827a75b011f04a8134a0" - integrity sha512-pmhivkYXkymswFfbXsANmBAewXx86UBfmagP+w0wkK06kLsLlTK5oQmsURPivzMkIBQiYq2cjamcZExIwlFQIg== - core-util-is@~1.0.0: version "1.0.3" resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.3.tgz#a6042d3634c2b27e9328f837b965fac83808db85" @@ -7304,7 +6949,7 @@ date-fns@^2.30.0: dependencies: "@babel/runtime" "^7.21.0" -debug@2.6.9, debug@^2.6.9: +debug@2.6.9: version "2.6.9" resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== @@ -7333,6 +6978,13 @@ dedent@^1.0.0: resolved "https://registry.yarnpkg.com/dedent/-/dedent-1.5.1.tgz#4f3fc94c8b711e9bb2800d185cd6ad20f2a90aff" integrity sha512-+LxW+KLWxu3HW3M2w2ympwtqPrqYRzU8fqi6Fhd18fBALe15blJPI/I4+UHveMVG6lJqB4JNd4UG0S5cnVHwIg== +deep-eql@^4.1.3: + version "4.1.3" + resolved "https://registry.yarnpkg.com/deep-eql/-/deep-eql-4.1.3.tgz#7c7775513092f7df98d8df9996dd085eb668cc6d" + integrity sha512-WaEtAOpRA1MQ0eohqZjpGD8zdI0Ovsm8mmFhaDN8dvDZzyoUMcYDnf5Y6iu7HTXxf8JDS23qWa4a+hKCDyOPzw== + dependencies: + type-detect "^4.0.0" + deep-equal@^2.0.5: version "2.2.3" resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-2.2.3.tgz#af89dafb23a396c7da3e862abc0be27cf51d56e1" @@ -7505,11 +7157,6 @@ detect-newline@^3.0.0: resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-3.1.0.tgz#576f5dfc63ae1a192ff192d8ad3af6308991b651" integrity sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA== -detect-node-es@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/detect-node-es/-/detect-node-es-1.1.0.tgz#163acdf643330caa0b4cd7c21e7ee7755d6fa493" - integrity sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ== - detect-node@^2.0.4: version "2.1.0" resolved "https://registry.yarnpkg.com/detect-node/-/detect-node-2.1.0.tgz#c9c70775a49c3d03bc2c06d9a73be550f978f8b1" @@ -7573,6 +7220,11 @@ dom-accessibility-api@^0.5.6, dom-accessibility-api@^0.5.9: resolved "https://registry.yarnpkg.com/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz#5a7429e6066eb3664d911e33fb0e45de8eb08453" integrity sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg== +dom-accessibility-api@^0.6.3: + version "0.6.3" + resolved "https://registry.yarnpkg.com/dom-accessibility-api/-/dom-accessibility-api-0.6.3.tgz#993e925cc1d73f2c662e7d75dd5a5445259a8fd8" + integrity sha512-7ZgogeTnjuHbo+ct10G9Ffp0mif17idi0IyWNVA/wcwcm7NPOD/WEHVP3n7n3MhXqxoIYm8d6MuZohYWIZ4T3w== + dom-converter@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/dom-converter/-/dom-converter-0.2.0.tgz#6721a9daee2e293682955b6afe416771627bb768" @@ -7746,13 +7398,6 @@ error-ex@^1.3.1: dependencies: is-arrayish "^0.2.1" -error-stack-parser@^2.0.6: - version "2.1.4" - resolved "https://registry.yarnpkg.com/error-stack-parser/-/error-stack-parser-2.1.4.tgz#229cb01cdbfa84440bfa91876285b94680188286" - integrity sha512-Sk5V6wVazPhq5MhpO+AUxJn5x7XSXGl1R93Vn7i+zS15KDVxQijejNCrz8340/2bgLBjR9GtEG8ZVKONDjcqGQ== - dependencies: - stackframe "^1.3.4" - es-abstract@^1.22.1: version "1.22.3" resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.22.3.tgz#48e79f5573198de6dee3589195727f4f74bc4f32" @@ -7901,33 +7546,34 @@ esbuild-register@^3.5.0: dependencies: debug "^4.3.4" -esbuild@^0.18.0: - version "0.18.20" - resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.18.20.tgz#4709f5a34801b43b799ab7d6d82f7284a9b7a7a6" - integrity sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA== +"esbuild@^0.18.0 || ^0.19.0 || ^0.20.0": + version "0.20.2" + resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.20.2.tgz#9d6b2386561766ee6b5a55196c6d766d28c87ea1" + integrity sha512-WdOOppmUNU+IbZ0PaDiTst80zjnrOkyJNHoKupIcVyU8Lvla3Ugx94VzkQ32Ijqd7UhHJy75gNWDMUekcrSJ6g== optionalDependencies: - "@esbuild/android-arm" "0.18.20" - "@esbuild/android-arm64" "0.18.20" - "@esbuild/android-x64" "0.18.20" - "@esbuild/darwin-arm64" "0.18.20" - "@esbuild/darwin-x64" "0.18.20" - "@esbuild/freebsd-arm64" "0.18.20" - "@esbuild/freebsd-x64" "0.18.20" - "@esbuild/linux-arm" "0.18.20" - "@esbuild/linux-arm64" "0.18.20" - "@esbuild/linux-ia32" "0.18.20" - "@esbuild/linux-loong64" "0.18.20" - "@esbuild/linux-mips64el" "0.18.20" - "@esbuild/linux-ppc64" "0.18.20" - "@esbuild/linux-riscv64" "0.18.20" - "@esbuild/linux-s390x" "0.18.20" - "@esbuild/linux-x64" "0.18.20" - "@esbuild/netbsd-x64" "0.18.20" - "@esbuild/openbsd-x64" "0.18.20" - "@esbuild/sunos-x64" "0.18.20" - "@esbuild/win32-arm64" "0.18.20" - "@esbuild/win32-ia32" "0.18.20" - "@esbuild/win32-x64" "0.18.20" + "@esbuild/aix-ppc64" "0.20.2" + "@esbuild/android-arm" "0.20.2" + "@esbuild/android-arm64" "0.20.2" + "@esbuild/android-x64" "0.20.2" + "@esbuild/darwin-arm64" "0.20.2" + "@esbuild/darwin-x64" "0.20.2" + "@esbuild/freebsd-arm64" "0.20.2" + "@esbuild/freebsd-x64" "0.20.2" + "@esbuild/linux-arm" "0.20.2" + "@esbuild/linux-arm64" "0.20.2" + "@esbuild/linux-ia32" "0.20.2" + "@esbuild/linux-loong64" "0.20.2" + "@esbuild/linux-mips64el" "0.20.2" + "@esbuild/linux-ppc64" "0.20.2" + "@esbuild/linux-riscv64" "0.20.2" + "@esbuild/linux-s390x" "0.20.2" + "@esbuild/linux-x64" "0.20.2" + "@esbuild/netbsd-x64" "0.20.2" + "@esbuild/openbsd-x64" "0.20.2" + "@esbuild/sunos-x64" "0.20.2" + "@esbuild/win32-arm64" "0.20.2" + "@esbuild/win32-ia32" "0.20.2" + "@esbuild/win32-x64" "0.20.2" escalade@^3.1.1: version "3.1.1" @@ -8162,6 +7808,13 @@ estraverse@^5.1.0, estraverse@^5.2.0, estraverse@^5.3.0: resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123" integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA== +estree-walker@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-3.0.3.tgz#67c3e549ec402a487b4fc193d1953a524752340d" + integrity sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g== + dependencies: + "@types/estree" "^1.0.0" + esutils@^2.0.2: version "2.0.3" resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" @@ -8264,7 +7917,7 @@ ext@^1.1.2: dependencies: type "^2.7.2" -extend@^3.0.0, extend@^3.0.2: +extend@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== @@ -8278,16 +7931,6 @@ external-editor@^3.1.0: iconv-lite "^0.4.24" tmp "^0.0.33" -extract-zip@^1.6.6: - version "1.7.0" - resolved "https://registry.yarnpkg.com/extract-zip/-/extract-zip-1.7.0.tgz#556cc3ae9df7f452c493a0cfb51cc30277940927" - integrity sha512-xoh5G1W/PB0/27lXgMQyIhP5DSY/LhoCsOyZgb+6iMmRtCwVBo55uKaMoEYrDCKQhWvqEip5ZPKAc6eFNyf/MA== - dependencies: - concat-stream "^1.6.2" - debug "^2.6.9" - mkdirp "^0.5.4" - yauzl "^2.10.0" - fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3, fast-deep-equal@~3.1.3: version "3.1.3" resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" @@ -8355,13 +7998,6 @@ fb-watchman@^2.0.0: dependencies: bser "2.1.1" -fd-slicer@~1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/fd-slicer/-/fd-slicer-1.1.0.tgz#25c7c89cb1f9077f8891bbe61d8f390eae256f1e" - integrity sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g== - dependencies: - pend "~1.2.0" - fetch-retry@^5.0.2: version "5.0.6" resolved "https://registry.yarnpkg.com/fetch-retry/-/fetch-retry-5.0.6.tgz#17d0bc90423405b7a88b74355bf364acd2a7fa56" @@ -8686,6 +8322,11 @@ get-east-asian-width@^1.0.0: resolved "https://registry.yarnpkg.com/get-east-asian-width/-/get-east-asian-width-1.2.0.tgz#5e6ebd9baee6fb8b7b6bd505221065f0cd91f64e" integrity sha512-2nk+7SIVb14QrgXFHcm84tD4bKQz0RxPuMT8Ag5KPOq7J5fEmAg0UbXdTOSHqNuHSU28k55qnceesxXRZGzKWA== +get-func-name@^2.0.1, get-func-name@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/get-func-name/-/get-func-name-2.0.2.tgz#0d7cf20cd13fda808669ffa88f4ffc7a3943fc41" + integrity sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ== + get-intrinsic@^1.0.2, get-intrinsic@^1.1.1, get-intrinsic@^1.1.3, get-intrinsic@^1.2.0, get-intrinsic@^1.2.1, get-intrinsic@^1.2.2: version "1.2.2" resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.2.tgz#281b7622971123e1ef4b3c90fd7539306da93f3b" @@ -8696,11 +8337,6 @@ get-intrinsic@^1.0.2, get-intrinsic@^1.1.1, get-intrinsic@^1.1.3, get-intrinsic@ has-symbols "^1.0.3" hasown "^2.0.0" -get-nonce@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/get-nonce/-/get-nonce-1.0.1.tgz#fdf3f0278073820d2ce9426c18f07481b1e0cdf3" - integrity sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q== - get-npm-tarball-url@^2.0.3: version "2.1.0" resolved "https://registry.yarnpkg.com/get-npm-tarball-url/-/get-npm-tarball-url-2.1.0.tgz#cbd6bb25884622bc3191c761466c93ac83343213" @@ -8711,11 +8347,6 @@ get-package-type@^0.1.0: resolved "https://registry.yarnpkg.com/get-package-type/-/get-package-type-0.1.0.tgz#8de2d803cff44df3bc6c456e6668b36c3926e11a" integrity sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q== -get-port@^5.1.1: - version "5.1.1" - resolved "https://registry.yarnpkg.com/get-port/-/get-port-5.1.1.tgz#0469ed07563479de6efb986baf053dcd7d4e3193" - integrity sha512-g/Q1aTSDOxFpchXC4i8ZWvxA1lnPqx/JHqcpIw0/LX9T8x/GBbi6YnlN5nhaKIFkT8oFsscUKgDJYxfwfS6QsQ== - get-stream@^6.0.0: version "6.0.1" resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-6.0.1.tgz#a262d8eef67aced57c2852ad6167526a43cbf7b7" @@ -8742,10 +8373,10 @@ giget@^1.0.0: pathe "^1.1.1" tar "^6.2.0" -github-slugger@^1.0.0: - version "1.5.0" - resolved "https://registry.yarnpkg.com/github-slugger/-/github-slugger-1.5.0.tgz#17891bbc73232051474d68bd867a34625c955f7d" - integrity sha512-wIh+gKBI9Nshz2o46B0B3f5k/W+WI9ZAv6y5Dn5WJ5SK1t0TnDimB4WE5rmTD05ZAIn8HALCZVmCsvj0w0v0lw== +github-slugger@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/github-slugger/-/github-slugger-2.0.0.tgz#52cf2f9279a21eb6c59dd385b410f0c0adda8f1a" + integrity sha512-IaOQ9puYtjrkq7Y0Ygl9KDZnrf/aiUJYUpVf89y8kyaxbRG7Y1SrX/jaumrv81vc61+kiMempujsM3Yw7w5qcw== glob-parent@^5.1.2, glob-parent@~5.1.2: version "5.1.2" @@ -8959,6 +8590,27 @@ hasown@^2.0.0: dependencies: function-bind "^1.1.2" +hast-util-heading-rank@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/hast-util-heading-rank/-/hast-util-heading-rank-3.0.0.tgz#2d5c6f2807a7af5c45f74e623498dd6054d2aba8" + integrity sha512-EJKb8oMUXVHcWZTDepnr+WNbfnXKFNf9duMesmr4S8SXTJBJ9M4Yok08pu9vxdJwdlGRhVumk9mEhkEvKGifwA== + dependencies: + "@types/hast" "^3.0.0" + +hast-util-is-element@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/hast-util-is-element/-/hast-util-is-element-3.0.0.tgz#6e31a6532c217e5b533848c7e52c9d9369ca0932" + integrity sha512-Val9mnv2IWpLbNPqc/pUem+a7Ipj2aHacCwgNfTiK0vJKl0LF+4Ba4+v1oPHFpf3bLYmreq0/l3Gud9S5OH42g== + dependencies: + "@types/hast" "^3.0.0" + +hast-util-to-string@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/hast-util-to-string/-/hast-util-to-string-3.0.0.tgz#2a131948b4b1b26461a2c8ac876e2c88d02946bd" + integrity sha512-OGkAxX1Ua3cbcW6EJ5pT/tslVb90uViVkcJ4ZZIMW/R33DX/AkcJcRrPebPwJkHYwlDHXz4aIwvAAaAdtrACFA== + dependencies: + "@types/hast" "^3.0.0" + he@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" @@ -9110,14 +8762,6 @@ http-proxy@^1.18.1: follow-redirects "^1.0.0" requires-port "^1.0.0" -https-proxy-agent@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-4.0.0.tgz#702b71fb5520a132a66de1f67541d9e62154d82b" - integrity sha512-zoDhWrkR3of1l9QAL8/scJZyLu8j/gBkcwcaQOZh7Gyh/+uJQzGVETdgT30akuwkpL8HTRfssqI3BZuV18teDg== - dependencies: - agent-base "5" - debug "4" - https-proxy-agent@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz#c59ef224a04fe8b754f3db0063a25ea30d0005d6" @@ -9284,13 +8928,6 @@ intl-messageformat@^10.1.0: "@formatjs/icu-messageformat-parser" "2.7.3" tslib "^2.4.0" -invariant@^2.2.4: - version "2.2.4" - resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6" - integrity sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA== - dependencies: - loose-envify "^1.0.0" - ip@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/ip/-/ip-2.0.1.tgz#e8f3595d33a3ea66490204234b77636965307105" @@ -9306,10 +8943,10 @@ ipaddr.js@^2.0.1: resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-2.1.0.tgz#2119bc447ff8c257753b196fc5f1ce08a4cdf39f" integrity sha512-LlbxQ7xKzfBusov6UMi4MFpEg0m+mAm9xyNGEduwXMEDuf4WfzB/RZwMVYEd7IKGvh4IUkEXYxtAVu9T3OelJQ== -is-absolute-url@^3.0.0: - version "3.0.3" - resolved "https://registry.yarnpkg.com/is-absolute-url/-/is-absolute-url-3.0.3.tgz#96c6a22b6a23929b11ea0afb1836c36ad4a5d698" - integrity sha512-opmNIX7uFnS96NtPmhWQgQx6/NYFgsUXYMllcfzwWKUMwfo8kku1TvE6hkNcH+Q1ts5cMVrsY7j0bxXQDciu9Q== +is-absolute-url@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/is-absolute-url/-/is-absolute-url-4.0.1.tgz#16e4d487d4fded05cfe0685e53ec86804a5e94dc" + integrity sha512-/51/TKE88Lmm7Gc4/8btclNXWS+g50wXhYJq8HWIBAGUBnoAdRu1aXeh364t/O7wXDAcTJDP8PNuNKWUDWie+A== is-absolute@^1.0.0: version "1.0.0" @@ -10289,7 +9926,7 @@ json5@^1.0.1: dependencies: minimist "^1.2.0" -json5@^2.1.2, json5@^2.2.2, json5@^2.2.3: +json5@^2.2.2, json5@^2.2.3: version "2.2.3" resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283" integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg== @@ -10412,15 +10049,6 @@ loader-utils@^1.2.3: emojis-list "^3.0.0" json5 "^1.0.1" -loader-utils@^2.0.4: - version "2.0.4" - resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-2.0.4.tgz#8b5cb38b5c34a9a018ee1fc0e6a066d1dfcc528c" - integrity sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw== - dependencies: - big.js "^5.2.2" - emojis-list "^3.0.0" - json5 "^2.1.2" - locate-path@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-3.0.0.tgz#dbec3b3ab759758071b58fe59fc41871af21400e" @@ -10506,13 +10134,20 @@ loglevelnext@^1.0.1: es6-symbol "^3.1.1" object.assign "^4.1.0" -loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.4.0: +loose-envify@^1.1.0, loose-envify@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q== dependencies: js-tokens "^3.0.0 || ^4.0.0" +loupe@^2.3.6, loupe@^2.3.7: + version "2.3.7" + resolved "https://registry.yarnpkg.com/loupe/-/loupe-2.3.7.tgz#6e69b7d4db7d3ab436328013d37d1c8c3540c697" + integrity sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA== + dependencies: + get-func-name "^2.0.1" + lower-case@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/lower-case/-/lower-case-2.0.2.tgz#6fa237c63dbdc4a82ca0fd882e4722dc5e634e28" @@ -10602,23 +10237,11 @@ map-or-similar@^1.5.0: resolved "https://registry.yarnpkg.com/map-or-similar/-/map-or-similar-1.5.0.tgz#6de2653174adfb5d9edc33c69d3e92a1b76faf08" integrity sha512-0aF7ZmVon1igznGI4VS30yugpduQW3y3GkcgGJOp7d8x8QrizhigUxjI/m2UojsXXto+jLAH3KSz+xOJTiORjg== -markdown-to-jsx@^7.1.8: +markdown-to-jsx@7.3.2: version "7.3.2" resolved "https://registry.yarnpkg.com/markdown-to-jsx/-/markdown-to-jsx-7.3.2.tgz#f286b4d112dad3028acc1e77dfe1f653b347e131" integrity sha512-B+28F5ucp83aQm+OxNrPkS8z0tMKaeHiy0lHJs3LqCyDQFtWuenaIrkaVTgAm1pf1AU85LXltva86hlaT17i8Q== -mdast-util-definitions@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/mdast-util-definitions/-/mdast-util-definitions-4.0.0.tgz#c5c1a84db799173b4dcf7643cda999e440c24db2" - integrity sha512-k8AJ6aNnUkB7IE+5azR9h81O5EQ/cTDXtWdMq9Kk5KcEW/8ritU5CeLg/9HhOC++nALHBlaogJ5jz0Ybk3kPMQ== - dependencies: - unist-util-visit "^2.0.0" - -mdast-util-to-string@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/mdast-util-to-string/-/mdast-util-to-string-1.1.0.tgz#27055500103f51637bd07d01da01eb1967a43527" - integrity sha512-jVU0Nr2B9X3MU4tSK7JP1CMkSvOj7X5l/GboG1tKRw52lLF1x2Ju92Ms9tNetCcbfX3hzlM73zYo2NKkWSfF/A== - media-typer@0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" @@ -10671,7 +10294,7 @@ mime-db@1.52.0, "mime-db@>= 1.43.0 < 2": resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== -mime-types@^2.1.12, mime-types@^2.1.25, mime-types@^2.1.27, mime-types@^2.1.31, mime-types@~2.1.17, mime-types@~2.1.24, mime-types@~2.1.34: +mime-types@^2.1.12, mime-types@^2.1.27, mime-types@^2.1.31, mime-types@~2.1.17, mime-types@~2.1.24, mime-types@~2.1.34: version "2.1.35" resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== @@ -10683,11 +10306,6 @@ mime@1.6.0: resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== -mime@^2.0.3: - version "2.6.0" - resolved "https://registry.yarnpkg.com/mime/-/mime-2.6.0.tgz#a2a682a95cd4d0cb1d6257e28f83da7e35800367" - integrity sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg== - mimic-fn@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" @@ -10759,13 +10377,6 @@ mkdirp-classic@^0.5.2: resolved "https://registry.yarnpkg.com/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz#fa10c9115cc6d8865be221ba47ee9bed78601113" integrity sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A== -mkdirp@^0.5.4: - version "0.5.6" - resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.6.tgz#7def03d2432dcae4ba1d611445c48396062255f6" - integrity sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw== - dependencies: - minimist "^1.2.6" - mkdirp@^1.0.3: version "1.0.4" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" @@ -11359,6 +10970,11 @@ pathe@^1.1.1: resolved "https://registry.yarnpkg.com/pathe/-/pathe-1.1.1.tgz#1dd31d382b974ba69809adc9a7a347e65d84829a" integrity sha512-d+RQGp0MAYTIaDBIMmOfMwz3E+LOZnxx1HZd5R18mmCZY0QBlK0LDZfPc8FW8Ed2DlvsuE6PRjroDY+wg4+j/Q== +pathval@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/pathval/-/pathval-1.1.1.tgz#8534e77a77ce7ac5a2512ea21e0fdb8fcf6c3d8d" + integrity sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ== + peek-stream@^1.1.0: version "1.1.3" resolved "https://registry.yarnpkg.com/peek-stream/-/peek-stream-1.1.3.tgz#3b35d84b7ccbbd262fff31dc10da56856ead6d67" @@ -11368,11 +10984,6 @@ peek-stream@^1.1.0: duplexify "^3.5.0" through2 "^2.0.3" -pend@~1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50" - integrity sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg== - picocolors@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c" @@ -11521,11 +11132,16 @@ prettier-linter-helpers@^1.0.0: dependencies: fast-diff "^1.1.2" -prettier@2.8.8, prettier@^2.8.0: +prettier@2.8.8: version "2.8.8" resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.8.tgz#e8c5d7e98a4305ffe3de2e1fc4aca1a71c28b1da" integrity sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q== +prettier@^3.1.1: + version "3.2.5" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.2.5.tgz#e52bc3090586e824964a8813b09aba6233b28368" + integrity sha512-3/GWa9aOC0YeD7LUfvOG2NiDyhOWRvt1k+rcKhOuYnMY24iiCphgneUfJDyFXd6rZCAnuLBv6UeAULtrhT/F4A== + pretty-error@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/pretty-error/-/pretty-error-4.0.0.tgz#90a703f46dd7234adb46d0f84823e9d1cb8f10d6" @@ -11567,11 +11183,6 @@ process@^0.11.10: resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182" integrity sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A== -progress@^2.0.1: - version "2.0.3" - resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8" - integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA== - prompts@^2.0.1, prompts@^2.4.0: version "2.4.2" resolved "https://registry.yarnpkg.com/prompts/-/prompts-2.4.2.tgz#7b57e73b3a48029ad10ebd44f74b01722a4cb069" @@ -11597,11 +11208,6 @@ proxy-addr@~2.0.7: forwarded "0.2.0" ipaddr.js "1.9.1" -proxy-from-env@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2" - integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg== - psl@^1.1.33: version "1.9.0" resolved "https://registry.yarnpkg.com/psl/-/psl-1.9.0.tgz#d0df2a137f00794565fcaf3b2c00cd09f8d5a5a7" @@ -11642,22 +11248,6 @@ punycode@^2.1.0, punycode@^2.1.1: resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.1.tgz#027422e2faec0b25e1549c3e1bd8309b9133b6e5" integrity sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg== -puppeteer-core@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/puppeteer-core/-/puppeteer-core-2.1.1.tgz#e9b3fbc1237b4f66e25999832229e9db3e0b90ed" - integrity sha512-n13AWriBMPYxnpbb6bnaY5YoY6rGj8vPLrz6CZF3o0qJNEwlcfJVxBzYZ0NJsQ21UbdJoijPCDrM++SUVEz7+w== - dependencies: - "@types/mime-types" "^2.1.0" - debug "^4.1.0" - extract-zip "^1.6.6" - https-proxy-agent "^4.0.0" - mime "^2.0.3" - mime-types "^2.1.25" - progress "^2.0.1" - proxy-from-env "^1.0.0" - rimraf "^2.6.1" - ws "^6.1.0" - pure-rand@^6.0.0: version "6.0.4" resolved "https://registry.yarnpkg.com/pure-rand/-/pure-rand-6.0.4.tgz#50b737f6a925468679bff00ad20eade53f37d5c7" @@ -11763,6 +11353,14 @@ react-dom@17.0.2: object-assign "^4.1.1" scheduler "^0.20.2" +"react-dom@^16.8.0 || ^17.0.0 || ^18.0.0": + version "18.2.0" + resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-18.2.0.tgz#22aaf38708db2674ed9ada224ca4aa708d821e3d" + integrity sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g== + dependencies: + loose-envify "^1.1.0" + scheduler "^0.23.0" + react-element-to-jsx-string@^15.0.0: version "15.0.0" resolved "https://registry.yarnpkg.com/react-element-to-jsx-string/-/react-element-to-jsx-string-15.0.0.tgz#1cafd5b6ad41946ffc8755e254da3fc752a01ac6" @@ -11797,39 +11395,6 @@ react-lifecycles-compat@^3.0.4: resolved "https://registry.yarnpkg.com/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz#4f1a273afdfc8f3488a8c516bfda78f872352362" integrity sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA== -react-refresh@^0.14.0: - version "0.14.0" - resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.14.0.tgz#4e02825378a5f227079554d4284889354e5f553e" - integrity sha512-wViHqhAd8OHeLS/IRMJjTSDHF3U9eWi62F/MledQGPdJGDhodXJ9PBLNGr6WWL7qlH12Mt3TyTpbS+hGXMjCzQ== - -react-remove-scroll-bar@^2.3.3: - version "2.3.4" - resolved "https://registry.yarnpkg.com/react-remove-scroll-bar/-/react-remove-scroll-bar-2.3.4.tgz#53e272d7a5cb8242990c7f144c44d8bd8ab5afd9" - integrity sha512-63C4YQBUt0m6ALadE9XV56hV8BgJWDmmTPY758iIJjfQKt2nYwoUrPk0LXRXcB/yIj82T1/Ixfdpdk68LwIB0A== - dependencies: - react-style-singleton "^2.2.1" - tslib "^2.0.0" - -react-remove-scroll@2.5.5: - version "2.5.5" - resolved "https://registry.yarnpkg.com/react-remove-scroll/-/react-remove-scroll-2.5.5.tgz#1e31a1260df08887a8a0e46d09271b52b3a37e77" - integrity sha512-ImKhrzJJsyXJfBZ4bzu8Bwpka14c/fQt0k+cyFp/PBhTfyDnU5hjOtM4AG/0AMyy8oKzOTR0lDgJIM7pYXI0kw== - dependencies: - react-remove-scroll-bar "^2.3.3" - react-style-singleton "^2.2.1" - tslib "^2.1.0" - use-callback-ref "^1.3.0" - use-sidecar "^1.1.2" - -react-style-singleton@^2.2.1: - version "2.2.1" - resolved "https://registry.yarnpkg.com/react-style-singleton/-/react-style-singleton-2.2.1.tgz#f99e420492b2d8f34d38308ff660b60d0b1205b4" - integrity sha512-ZWj0fHEMyWkHzKYUr2Bs/4zU6XLmq9HsgBURm7g5pAVfyn49DgUiNgY2d4lXRlYSiCif9YBGpQleewkcqddc7g== - dependencies: - get-nonce "^1.0.0" - invariant "^2.2.4" - tslib "^2.0.0" - react-transition-group@^4.4.5: version "4.4.5" resolved "https://registry.yarnpkg.com/react-transition-group/-/react-transition-group-4.4.5.tgz#e53d4e3f3344da8521489fbef8f2581d42becdd1" @@ -11848,6 +11413,13 @@ react@17.0.2: loose-envify "^1.1.0" object-assign "^4.1.1" +"react@^16.8.0 || ^17.0.0 || ^18.0.0": + version "18.2.0" + resolved "https://registry.yarnpkg.com/react/-/react-18.2.0.tgz#555bd98592883255fa00de14f1151a917b5d77d5" + integrity sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ== + dependencies: + loose-envify "^1.1.0" + read-pkg-up@^7.0.1: version "7.0.1" resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-7.0.1.tgz#f3a6135758459733ae2b95638056e1854e7ef507" @@ -11867,7 +11439,7 @@ read-pkg@^5.2.0: parse-json "^5.0.0" type-fest "^0.6.0" -readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.2.2, readable-stream@~2.3.6: +readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@~2.3.6: version "2.3.8" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.8.tgz#91125e8042bba1b9887f49345f6277027ce8be9b" integrity sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA== @@ -11896,7 +11468,7 @@ readdirp@~3.6.0: dependencies: picomatch "^2.2.1" -recast@^0.23.1, recast@^0.23.3: +recast@^0.23.3: version "0.23.4" resolved "https://registry.yarnpkg.com/recast/-/recast-0.23.4.tgz#ca1bac7bfd3011ea5a28dfecb5df678559fb1ddf" integrity sha512-qtEDqIZGVcSZCHniWwZWbRy79Dc6Wp3kT/UmDA2RJKBPg7+7k51aQBZirHmUGn5uvHf2rg8DkjizrN26k61ATw== @@ -11907,6 +11479,17 @@ recast@^0.23.1, recast@^0.23.3: source-map "~0.6.1" tslib "^2.0.1" +recast@^0.23.5: + version "0.23.6" + resolved "https://registry.yarnpkg.com/recast/-/recast-0.23.6.tgz#198fba74f66143a30acc81929302d214ce4e3bfa" + integrity sha512-9FHoNjX1yjuesMwuthAmPKabxYQdOgihFYmT5ebXfYGBcnqXZf3WOVz+5foEZ8Y83P4ZY6yQD5GMmtV+pgCCAQ== + dependencies: + ast-types "^0.16.1" + esprima "~4.0.0" + source-map "~0.6.1" + tiny-invariant "^1.3.3" + tslib "^2.0.1" + rechoir@^0.8.0: version "0.8.0" resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.8.0.tgz#49f866e0d32146142da3ad8f0eff352b3215ff22" @@ -11986,31 +11569,34 @@ regjsparser@^0.9.1: dependencies: jsesc "~0.5.0" +rehype-external-links@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/rehype-external-links/-/rehype-external-links-3.0.0.tgz#2b28b5cda1932f83f045b6f80a3e1b15f168c6f6" + integrity sha512-yp+e5N9V3C6bwBeAC4n796kc86M4gJCdlVhiMTxIrJG5UHDMh+PJANf9heqORJbt1nrCbDwIlAZKjANIaVBbvw== + dependencies: + "@types/hast" "^3.0.0" + "@ungap/structured-clone" "^1.0.0" + hast-util-is-element "^3.0.0" + is-absolute-url "^4.0.0" + space-separated-tokens "^2.0.0" + unist-util-visit "^5.0.0" + +rehype-slug@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/rehype-slug/-/rehype-slug-6.0.0.tgz#1d21cf7fc8a83ef874d873c15e6adaee6344eaf1" + integrity sha512-lWyvf/jwu+oS5+hL5eClVd3hNdmwM1kAC0BUvEGD19pajQMIzcNUd/k9GsfQ+FfECvX+JE+e9/btsKH0EjJT6A== + dependencies: + "@types/hast" "^3.0.0" + github-slugger "^2.0.0" + hast-util-heading-rank "^3.0.0" + hast-util-to-string "^3.0.0" + unist-util-visit "^5.0.0" + relateurl@^0.2.7: version "0.2.7" resolved "https://registry.yarnpkg.com/relateurl/-/relateurl-0.2.7.tgz#54dbf377e51440aca90a4cd274600d3ff2d888a9" integrity sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog== -remark-external-links@^8.0.0: - version "8.0.0" - resolved "https://registry.yarnpkg.com/remark-external-links/-/remark-external-links-8.0.0.tgz#308de69482958b5d1cd3692bc9b725ce0240f345" - integrity sha512-5vPSX0kHoSsqtdftSHhIYofVINC8qmp0nctkeU9YoJwV3YfiBRiI6cbFRJ0oI/1F9xS+bopXG0m2KS8VFscuKA== - dependencies: - extend "^3.0.0" - is-absolute-url "^3.0.0" - mdast-util-definitions "^4.0.0" - space-separated-tokens "^1.0.0" - unist-util-visit "^2.0.0" - -remark-slug@^6.0.0: - version "6.1.0" - resolved "https://registry.yarnpkg.com/remark-slug/-/remark-slug-6.1.0.tgz#0503268d5f0c4ecb1f33315c00465ccdd97923ce" - integrity sha512-oGCxDF9deA8phWvxFuyr3oSJsdyUAxMFbA0mZ7Y1Sas+emILtO+e5WutF9564gDsEN4IXaQXm5pFo6MLH+YmwQ== - dependencies: - github-slugger "^1.0.0" - mdast-util-to-string "^1.0.0" - unist-util-visit "^2.0.0" - renderkid@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/renderkid/-/renderkid-3.0.0.tgz#5fd823e4d6951d37358ecc9a58b1f06836b6268a" @@ -12072,7 +11658,7 @@ resolve.exports@^2.0.0: resolved "https://registry.yarnpkg.com/resolve.exports/-/resolve.exports-2.0.2.tgz#f8c934b8e6a13f539e38b7098e2e36134f01e800" integrity sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg== -resolve@^1.10.0, resolve@^1.14.2, resolve@^1.20.0, resolve@^1.22.1, resolve@^1.22.4: +resolve@^1.10.0, resolve@^1.14.2, resolve@^1.20.0, resolve@^1.22.1, resolve@^1.22.4, resolve@^1.22.8: version "1.22.8" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.8.tgz#b6c87a9f2aa06dfab52e3d70ac8cde321fa5a48d" integrity sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw== @@ -12116,7 +11702,7 @@ reusify@^1.0.4: resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== -rimraf@^2.6.1, rimraf@^2.6.3: +rimraf@^2.6.3: version "2.7.1" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec" integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w== @@ -12215,7 +11801,14 @@ scheduler@^0.20.2: loose-envify "^1.1.0" object-assign "^4.1.1" -schema-utils@^3.0.0, schema-utils@^3.1.1, schema-utils@^3.2.0: +scheduler@^0.23.0: + version "0.23.0" + resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.23.0.tgz#ba8041afc3d30eb206a487b6b384002e4e61fdfe" + integrity sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw== + dependencies: + loose-envify "^1.1.0" + +schema-utils@^3.1.1, schema-utils@^3.2.0: version "3.3.0" resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-3.3.0.tgz#f50a88877c3c01652a15b622ae9e9795df7a60fe" integrity sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg== @@ -12462,15 +12055,15 @@ source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.0, source-map@~0.6.1: resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== -source-map@^0.7.3, source-map@^0.7.4: +source-map@^0.7.4: version "0.7.4" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.7.4.tgz#a9bbe705c9d8846f4e08ff6765acf0f1b0898656" integrity sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA== -space-separated-tokens@^1.0.0: - version "1.1.5" - resolved "https://registry.yarnpkg.com/space-separated-tokens/-/space-separated-tokens-1.1.5.tgz#85f32c3d10d9682007e917414ddc5c26d1aa6899" - integrity sha512-q/JSVd1Lptzhf5bkYm4ob4iWPjx0KiRe3sRFBNrVqbJkFaBm5vbbowy1mymoPNLRa52+oadOhJ+K49wsSeSjTA== +space-separated-tokens@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz#1ecd9d2350a3844572c3f4a312bceb018348859f" + integrity sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q== spawn-command@0.0.2: version "0.0.2" @@ -12546,11 +12139,6 @@ stack-utils@^2.0.3: dependencies: escape-string-regexp "^2.0.0" -stackframe@^1.3.4: - version "1.3.4" - resolved "https://registry.yarnpkg.com/stackframe/-/stackframe-1.3.4.tgz#b881a004c8c149a5e8efef37d51b16e412943310" - integrity sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw== - statuses@2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/statuses/-/statuses-2.0.1.tgz#55cb000ccf1d48728bd23c685a063998cf1a1b63" @@ -12588,17 +12176,17 @@ storybook-addon-react-docgen@^1.2.44: react-lifecycles-compat "^3.0.4" storybook-pretty-props "^1.2.1" -storybook-dark-mode@^3.0.3: - version "3.0.3" - resolved "https://registry.yarnpkg.com/storybook-dark-mode/-/storybook-dark-mode-3.0.3.tgz#7301c58646aae05f754de23046305aa5250546db" - integrity sha512-ZLBLVpkuKTdtUv3DTuOjeP/bE7DHhOxVpDROKc0NtEYq9JHLUu6z05LLZinE3v6QPXQZ9TMQPm3Xe/0BcLEZlw== +storybook-dark-mode@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/storybook-dark-mode/-/storybook-dark-mode-4.0.1.tgz#899cedb527f43aec1a1a782b767ba2ac32a113da" + integrity sha512-9l3qY8NdgwZnY+NlO1XHB3eUb6FmZo9GazJeUSeFkjRqwA5FmnMSeq0YVqEOqfwniM/TvQwOiTYd5g/hC2wugA== dependencies: - "@storybook/addons" "^7.0.0" - "@storybook/components" "^7.0.0" - "@storybook/core-events" "^7.0.0" + "@storybook/components" "^8.0.0" + "@storybook/core-events" "^8.0.0" "@storybook/global" "^5.0.0" - "@storybook/manager-api" "^7.0.0" - "@storybook/theming" "^7.0.0" + "@storybook/icons" "^1.2.5" + "@storybook/manager-api" "^8.0.0" + "@storybook/theming" "^8.0.0" fast-deep-equal "^3.1.3" memoizerific "^1.11.3" @@ -12607,12 +12195,12 @@ storybook-pretty-props@^1.2.1: resolved "https://registry.yarnpkg.com/storybook-pretty-props/-/storybook-pretty-props-1.2.1.tgz#04c6e7c80efc0190a5dd94dceaf50579c159e182" integrity sha512-3dUtu0UbBA6idA3Qo0i+CYGGz8GiqlXzhgCJdT065jnuJ3y9intKxZpv05ZbnQXCPnsPVSDos+hgOZ444hf6xA== -storybook@7.6.17: - version "7.6.17" - resolved "https://registry.yarnpkg.com/storybook/-/storybook-7.6.17.tgz#d7fdbbf57d61d386b3ccc6721285bc914f54269b" - integrity sha512-8+EIo91bwmeFWPg1eysrxXlhIYv3OsXrznTr4+4Eq0NikqAoq6oBhtlN5K2RGS2lBVF537eN+9jTCNbR+WrzDA== +storybook@^8.0.4: + version "8.0.4" + resolved "https://registry.yarnpkg.com/storybook/-/storybook-8.0.4.tgz#f2191d4082d8a1bfa065f358e63e8205533d9efc" + integrity sha512-FUr3Uc2dSAQ80jINH5fSXz7zD7Ncn08OthROjwRtHAH+jMf4wxyZ+RhF3heFy9xLot2/HXOLIWyHyzZZMtGhxg== dependencies: - "@storybook/cli" "7.6.17" + "@storybook/cli" "8.0.4" stream-shift@^1.0.0: version "1.0.1" @@ -12787,21 +12375,11 @@ supports-preserve-symlinks-flag@^1.0.0: resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== -swc-loader@^0.2.3: - version "0.2.3" - resolved "https://registry.yarnpkg.com/swc-loader/-/swc-loader-0.2.3.tgz#6792f1c2e4c9ae9bf9b933b3e010210e270c186d" - integrity sha512-D1p6XXURfSPleZZA/Lipb3A8pZ17fP4NObZvFCDjK/OKljroqDpPmsBdTraWhVBqUNpcWBQY1imWdoPScRlQ7A== - symbol-tree@^3.2.4: version "3.2.4" resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.4.tgz#430637d248ba77e078883951fb9aa0eed7c63fa2" integrity sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw== -synchronous-promise@^2.0.15: - version "2.0.17" - resolved "https://registry.yarnpkg.com/synchronous-promise/-/synchronous-promise-2.0.17.tgz#38901319632f946c982152586f2caf8ddc25c032" - integrity sha512-AsS729u2RHUfEra9xJrE39peJcc2stq2+poBXX8bcM08Y6g9j/i/PUzwNQqkaJde7Ntg1TO7bSREbR5sdosQ+g== - synckit@^0.8.6: version "0.8.8" resolved "https://registry.yarnpkg.com/synckit/-/synckit-0.8.8.tgz#fe7fe446518e3d3d49f5e429f443cf08b6edfcd7" @@ -12941,6 +12519,16 @@ tiny-invariant@^1.3.1: resolved "https://registry.yarnpkg.com/tiny-invariant/-/tiny-invariant-1.3.1.tgz#8560808c916ef02ecfd55e66090df23a4b7aa642" integrity sha512-AD5ih2NlSssTCwsMznbvwMZpJ1cbhkGd2uueNxzv2jDlEeZdU04JQfRnggJQ8DrcVBGjAsCKwFBbDlVNtEMlzw== +tiny-invariant@^1.3.3: + version "1.3.3" + resolved "https://registry.yarnpkg.com/tiny-invariant/-/tiny-invariant-1.3.3.tgz#46680b7a873a0d5d10005995eb90a70d74d60127" + integrity sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg== + +tinyspy@^2.2.0: + version "2.2.1" + resolved "https://registry.yarnpkg.com/tinyspy/-/tinyspy-2.2.1.tgz#117b2342f1f38a0dbdcc73a50a454883adf861d1" + integrity sha512-KYad6Vy5VDWV4GH3fjpseMQ/XU2BhIYP7Vzd0LG44qRWm/Yt2WCOTicFdvmgo6gWaqooMQCawTtILVQJupKu7A== + title-case@^3.0.3: version "3.0.3" resolved "https://registry.yarnpkg.com/title-case/-/title-case-3.0.3.tgz#bc689b46f02e411f1d1e1d081f7c3deca0489982" @@ -13021,7 +12609,7 @@ ts-api-utils@^1.0.1: resolved "https://registry.yarnpkg.com/ts-api-utils/-/ts-api-utils-1.0.3.tgz#f12c1c781d04427313dbac808f453f050e54a331" integrity sha512-wNMeqtMz5NtwpT/UZGY5alT+VoKdSsOOP/kqHFcUW1P/VRhH2wJ48+DN2WwUliNbQ976ETwDL0Ifd2VVvgonvg== -ts-dedent@^2.0.0, ts-dedent@^2.2.0: +ts-dedent@^2.0.0: version "2.2.0" resolved "https://registry.yarnpkg.com/ts-dedent/-/ts-dedent-2.2.0.tgz#39e4bd297cd036292ae2394eb3412be63f563bb5" integrity sha512-q5W7tVM71e2xjHZTlgfTDoPF/SmqKG5hddq9SzR49CH2hayqRKJtQ4mtRlSxKaJlR/+9rEM+mnBHf7I2/BQcpQ== @@ -13060,7 +12648,7 @@ tsconfig-paths-webpack-plugin@^4.0.0: enhanced-resolve "^5.7.0" tsconfig-paths "^4.1.2" -tsconfig-paths@^4.1.2: +tsconfig-paths@^4.1.2, tsconfig-paths@^4.2.0: version "4.2.0" resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-4.2.0.tgz#ef78e19039133446d244beac0fd6a1632e2d107c" integrity sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg== @@ -13098,7 +12686,7 @@ type-check@^0.4.0, type-check@~0.4.0: dependencies: prelude-ls "^1.2.1" -type-detect@4.0.8: +type-detect@4.0.8, type-detect@^4.0.0, type-detect@^4.0.8: version "4.0.8" resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c" integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g== @@ -13190,11 +12778,6 @@ typed-array-length@^1.0.4: for-each "^0.3.3" is-typed-array "^1.1.9" -typedarray@^0.0.6: - version "0.0.6" - resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" - integrity sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA== - typescript@^5.3.3: version "5.3.3" resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.3.3.tgz#b3ce6ba258e72e6305ba66f5c9b452aaee3ffe37" @@ -13255,27 +12838,29 @@ unique-string@^2.0.0: dependencies: crypto-random-string "^2.0.0" -unist-util-is@^4.0.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/unist-util-is/-/unist-util-is-4.1.0.tgz#976e5f462a7a5de73d94b706bac1b90671b57797" - integrity sha512-ZOQSsnce92GrxSqlnEEseX0gi7GH9zTJZ0p9dtu87WRb/37mMPO2Ilx1s/t9vBHrFhbgweUwb+t7cIn5dxPhZg== +unist-util-is@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/unist-util-is/-/unist-util-is-6.0.0.tgz#b775956486aff107a9ded971d996c173374be424" + integrity sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw== + dependencies: + "@types/unist" "^3.0.0" -unist-util-visit-parents@^3.0.0: - version "3.1.1" - resolved "https://registry.yarnpkg.com/unist-util-visit-parents/-/unist-util-visit-parents-3.1.1.tgz#65a6ce698f78a6b0f56aa0e88f13801886cdaef6" - integrity sha512-1KROIZWo6bcMrZEwiH2UrXDyalAa0uqzWCxCJj6lPOvTve2WkfgCytoDTPaMnodXh1WrXOq0haVYHj99ynJlsg== +unist-util-visit-parents@^6.0.0: + version "6.0.1" + resolved "https://registry.yarnpkg.com/unist-util-visit-parents/-/unist-util-visit-parents-6.0.1.tgz#4d5f85755c3b8f0dc69e21eca5d6d82d22162815" + integrity sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw== dependencies: - "@types/unist" "^2.0.0" - unist-util-is "^4.0.0" + "@types/unist" "^3.0.0" + unist-util-is "^6.0.0" -unist-util-visit@^2.0.0: - version "2.0.3" - resolved "https://registry.yarnpkg.com/unist-util-visit/-/unist-util-visit-2.0.3.tgz#c3703893146df47203bb8a9795af47d7b971208c" - integrity sha512-iJ4/RczbJMkD0712mGktuGpm/U4By4FfDonL7N/9tATGIF4imikjOuagyMY53tnZq3NP6BcmlrHhEKAfGWjh7Q== +unist-util-visit@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/unist-util-visit/-/unist-util-visit-5.0.0.tgz#a7de1f31f72ffd3519ea71814cccf5fd6a9217d6" + integrity sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg== dependencies: - "@types/unist" "^2.0.0" - unist-util-is "^4.0.0" - unist-util-visit-parents "^3.0.0" + "@types/unist" "^3.0.0" + unist-util-is "^6.0.0" + unist-util-visit-parents "^6.0.0" universalify@^0.2.0: version "0.2.0" @@ -13352,28 +12937,6 @@ url@^0.11.0: punycode "^1.4.1" qs "^6.11.2" -use-callback-ref@^1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/use-callback-ref/-/use-callback-ref-1.3.0.tgz#772199899b9c9a50526fedc4993fc7fa1f7e32d5" - integrity sha512-3FT9PRuRdbB9HfXhEq35u4oZkvpJ5kuYbpqhCfmiZyReuRgpnhDlbr2ZEnnuS0RrJAPn6l23xjFg9kpDM+Ms7w== - dependencies: - tslib "^2.0.0" - -use-resize-observer@^9.1.0: - version "9.1.0" - resolved "https://registry.yarnpkg.com/use-resize-observer/-/use-resize-observer-9.1.0.tgz#14735235cf3268569c1ea468f8a90c5789fc5c6c" - integrity sha512-R25VqO9Wb3asSD4eqtcxk8sJalvIOYBqS8MNZlpDSQ4l4xMQxC/J7Id9HoTqPq8FwULIn0PVW+OAqF2dyYbjow== - dependencies: - "@juggle/resize-observer" "^3.3.1" - -use-sidecar@^1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/use-sidecar/-/use-sidecar-1.1.2.tgz#2f43126ba2d7d7e117aa5855e5d8f0276dfe73c2" - integrity sha512-epTbsLuzZ7lPClpz2TyryBfztm7m+28DlEv2ZCQ3MDr5ssiwyOwGH/e5F9CkfWjJ1t4clvI58yF822/GUkjjhw== - dependencies: - detect-node-es "^1.1.0" - tslib "^2.0.0" - util-deprecate@^1.0.1, util-deprecate@^1.0.2, util-deprecate@~1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" @@ -14175,13 +13738,6 @@ write-file-atomic@^4.0.2: imurmurhash "^0.1.4" signal-exit "^3.0.7" -ws@^6.1.0: - version "6.2.2" - resolved "https://registry.yarnpkg.com/ws/-/ws-6.2.2.tgz#dd5cdbd57a9979916097652d78f1cc5faea0c32e" - integrity sha512-zmhltoSR8u1cnDsD43TX59mzoMZsLKqUweyYBAIvTngR3shc0W6aOZylZmq/7hqyVxPdi+5Ud2QInblgyE72fw== - dependencies: - async-limiter "~1.0.0" - ws@^8.11.0, ws@^8.13.0, ws@^8.2.3: version "8.15.1" resolved "https://registry.yarnpkg.com/ws/-/ws-8.15.1.tgz#271ba33a45ca0cc477940f7f200cd7fba7ee1997" @@ -14258,14 +13814,6 @@ yargs@~17.6.0: y18n "^5.0.5" yargs-parser "^21.1.1" -yauzl@^2.10.0: - version "2.10.0" - resolved "https://registry.yarnpkg.com/yauzl/-/yauzl-2.10.0.tgz#c7eb17c93e112cb1086fa6d8e51fb0667b79a5f9" - integrity sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g== - dependencies: - buffer-crc32 "~0.2.3" - fd-slicer "~1.1.0" - yocto-queue@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b"