diff --git a/ui/dashboard/package.json b/ui/dashboard/package.json
index df672e9d9..ed441c08c 100644
--- a/ui/dashboard/package.json
+++ b/ui/dashboard/package.json
@@ -64,11 +64,13 @@
"@tailwindcss/forms": "0.5.3",
"@tailwindcss/line-clamp": "0.4.2",
"@tailwindcss/typography": "0.5.7",
+ "@testing-library/jest-dom": "5.16.5",
+ "@testing-library/react": "13.4.0",
"@tsconfig/create-react-app": "1.0.2",
"@types/echarts": "4.9.16",
"@types/jest": "29.0.3",
"@types/lodash": "4.14.185",
- "@types/node": "18.7.22",
+ "@types/node": "18.7.23",
"@types/react": "18.0.21",
"@types/react-dom": "18.0.6",
"autoprefixer": "10.4.12",
diff --git a/ui/dashboard/src/components/dashboards/layout/Dashboard/DashboardProgress.tsx b/ui/dashboard/src/components/dashboards/layout/Dashboard/DashboardProgress.tsx
index 482615824..345ba5e75 100644
--- a/ui/dashboard/src/components/dashboards/layout/Dashboard/DashboardProgress.tsx
+++ b/ui/dashboard/src/components/dashboards/layout/Dashboard/DashboardProgress.tsx
@@ -1,11 +1,13 @@
-import { DashboardRunState } from "../../../../hooks/useDashboard";
+import { useDashboard } from "../../../../hooks/useDashboard";
-interface DashboardProgressProps {
- state?: DashboardRunState;
- progress?: number;
-}
+const DashboardProgress = () => {
+ const { dataMode, progress, state } = useDashboard();
+
+ // We only show a progress indicator in live mode
+ if (dataMode === "snapshot") {
+ return null;
+ }
-const DashboardProgress = ({ state, progress }: DashboardProgressProps) => {
return (
{state === "ready" ? (
diff --git a/ui/dashboard/src/components/dashboards/layout/Dashboard/index.tsx b/ui/dashboard/src/components/dashboards/layout/Dashboard/index.tsx
index b74b75e91..1e7c012a7 100644
--- a/ui/dashboard/src/components/dashboards/layout/Dashboard/index.tsx
+++ b/ui/dashboard/src/components/dashboards/layout/Dashboard/index.tsx
@@ -2,9 +2,9 @@ import Children from "../common/Children";
import DashboardProgress from "./DashboardProgress";
import LayoutPanel from "../common/LayoutPanel";
import PanelDetail from "../PanelDetail";
+import SnapshotRenderComplete from "../../../snapshot/SnapshotRenderComplete";
import {
DashboardDefinition,
- DashboardRunState,
useDashboard,
} from "../../../../hooks/useDashboard";
import { registerComponent } from "../../index";
@@ -13,8 +13,6 @@ interface DashboardProps {
allowPanelExpand?: boolean;
definition: DashboardDefinition;
isRoot?: boolean;
- progress?: number;
- state?: DashboardRunState;
withPadding?: boolean;
}
@@ -26,13 +24,11 @@ interface DashboardWrapperProps {
const Dashboard = ({
allowPanelExpand = true,
definition,
- progress = 0,
isRoot = true,
- state = "ready",
withPadding = false,
}: DashboardProps) => (
<>
- {isRoot ?
: <>>}
+ {isRoot ?
: <>>}
{
- const {
- dashboard,
- dataMode,
- progress,
- search,
- selectedDashboard,
- selectedPanel,
- state,
- } = useDashboard();
+ const { dashboard, dataMode, search, selectedDashboard, selectedPanel } =
+ useDashboard();
if (
search.value ||
@@ -73,13 +62,14 @@ const DashboardWrapper = ({
}
return (
-
+ <>
+
+
+ >
);
};
diff --git a/ui/dashboard/src/components/snapshot/SnapshotRenderComplete/index.test.jsx b/ui/dashboard/src/components/snapshot/SnapshotRenderComplete/index.test.jsx
new file mode 100644
index 000000000..1fb71e8ed
--- /dev/null
+++ b/ui/dashboard/src/components/snapshot/SnapshotRenderComplete/index.test.jsx
@@ -0,0 +1,33 @@
+import React from "react";
+import SnapshotRenderComplete from "./index.tsx";
+import { DashboardContext } from "../../../hooks/useDashboard";
+import { render } from "@testing-library/react";
+import "@testing-library/jest-dom";
+
+test("return null when should not render snapshot complete div", async () => {
+ // ARRANGE
+ const { container } = render(
+
+
+
+ );
+
+ // ASSERT
+ expect(container).toBeEmptyDOMElement();
+});
+
+test("return null when should not render snapshot complete div", async () => {
+ // ARRANGE
+ render(
+
+
+
+ );
+
+ // ASSERT
+ expect(document.querySelector("#snapshot-complete")).toBeTruthy();
+});
diff --git a/ui/dashboard/src/components/snapshot/SnapshotRenderComplete/index.tsx b/ui/dashboard/src/components/snapshot/SnapshotRenderComplete/index.tsx
new file mode 100644
index 000000000..0678ac76e
--- /dev/null
+++ b/ui/dashboard/src/components/snapshot/SnapshotRenderComplete/index.tsx
@@ -0,0 +1,15 @@
+import { useDashboard } from "../../../hooks/useDashboard";
+
+const SnapshotRenderComplete = () => {
+ const {
+ render: { snapshotCompleteDiv },
+ } = useDashboard();
+
+ if (!snapshotCompleteDiv) {
+ return null;
+ }
+
+ return ;
+};
+
+export default SnapshotRenderComplete;
diff --git a/ui/dashboard/src/hooks/useDashboard.tsx b/ui/dashboard/src/hooks/useDashboard.tsx
index 83b06477d..5b6c9811b 100644
--- a/ui/dashboard/src/hooks/useDashboard.tsx
+++ b/ui/dashboard/src/hooks/useDashboard.tsx
@@ -2,15 +2,7 @@ import get from "lodash/get";
import has from "lodash/has";
import isEqual from "lodash/isEqual";
import paths from "deepdash/paths";
-import set from "lodash/set";
-import sortBy from "lodash/sortBy";
-import useDashboardWebSocket, {
- SocketActions,
- SocketURLFactory,
-} from "./useDashboardWebSocket";
-import usePrevious from "./usePrevious";
-import { buildComponentsMap } from "../components";
-import {
+import React, {
createContext,
Ref,
useCallback,
@@ -19,6 +11,14 @@ import {
useReducer,
useState,
} from "react";
+import set from "lodash/set";
+import sortBy from "lodash/sortBy";
+import useDashboardWebSocket, {
+ SocketActions,
+ SocketURLFactory,
+} from "./useDashboardWebSocket";
+import usePrevious from "./usePrevious";
+import { buildComponentsMap } from "../components";
import { GlobalHotKeys } from "react-hotkeys";
import { LeafNodeData, Width } from "../components/dashboards/common";
import { noop } from "../utils/func";
@@ -95,6 +95,10 @@ interface IDashboardContext {
progress: number;
state: DashboardRunState;
+ render: {
+ headless: boolean;
+ snapshotCompleteDiv: boolean;
+ };
}
export interface IActions {
@@ -314,6 +318,10 @@ export interface DashboardDataOptions {
snapshotId?: string;
}
+export interface DashboardRenderOptions {
+ headless?: boolean;
+}
+
interface DashboardProviderProps {
analyticsContext: any;
breakpointContext: any;
@@ -322,6 +330,7 @@ interface DashboardProviderProps {
dataOptions?: DashboardDataOptions;
eventHooks?: {};
featureFlags?: string[];
+ renderOptions?: DashboardRenderOptions;
socketUrlFactory?: SocketURLFactory;
stateDefaults?: {};
themeContext: any;
@@ -842,6 +851,9 @@ const DashboardProvider = ({
},
eventHooks,
featureFlags = [],
+ renderOptions = {
+ headless: false,
+ },
socketUrlFactory,
stateDefaults = {},
themeContext,
@@ -851,7 +863,11 @@ const DashboardProvider = ({
const [searchParams, setSearchParams] = useSearchParams();
const [state, dispatchInner] = useReducer(
reducer,
- getInitialState(searchParams, { ...stateDefaults, ...dataOptions })
+ getInitialState(searchParams, {
+ ...stateDefaults,
+ ...dataOptions,
+ ...renderOptions,
+ })
);
const dispatch = useCallback((action) => {
// console.log(action.type, action);
@@ -923,11 +939,6 @@ const DashboardProvider = ({
searchParams.get("tag") ||
get(stateDefaults, "search.groupBy.tag", "service");
const inputs = buildSelectedDashboardInputsFromSearchParams(searchParams);
- // console.log({
- // // @ts-ignore
- // previous: previousSelectedDashboardStates?.selectedDashboardInputs,
- // current: inputs,
- // });
dispatch({
type: DashboardActions.SET_DASHBOARD_SEARCH_VALUE,
value: goneFromDashboardToDashboard ? "" : search,
@@ -943,7 +954,6 @@ const DashboardProvider = ({
previousSelectedDashboardStates?.selectedDashboardInputs
) !== JSON.stringify(inputs)
) {
- // console.log("dispatching inputs", inputs);
dispatch({
type: DashboardActions.SET_DASHBOARD_INPUTS,
value: inputs,
@@ -1282,6 +1292,16 @@ const DashboardProvider = ({
});
}, [closePanelDetail]);
+ const [renderSnapshotCompleteDiv, setRenderSnapshotCompleteDiv] =
+ useState(false);
+
+ useEffect(() => {
+ if (dataOptions?.dataMode !== "snapshot" || state.state !== "complete") {
+ return;
+ }
+ setRenderSnapshotCompleteDiv(true);
+ }, [dataOptions?.dataMode, state.state]);
+
return (
diff --git a/ui/dashboard/yarn.lock b/ui/dashboard/yarn.lock
index 45e97023e..1aea6aeb1 100644
--- a/ui/dashboard/yarn.lock
+++ b/ui/dashboard/yarn.lock
@@ -2,6 +2,11 @@
# yarn lockfile v1
+"@adobe/css-tools@^4.0.1":
+ version "4.0.1"
+ resolved "https://registry.yarnpkg.com/@adobe/css-tools/-/css-tools-4.0.1.tgz#b38b444ad3aa5fedbb15f2f746dcd934226a12dd"
+ integrity sha512-+u76oB43nOHrF4DDWRLWDCtci7f3QJoEBigemIdIeTi1ODqjx6Tad9NCVnPRwewWlKkVab5PlK8DCtPTyX7S8g==
+
"@apideck/better-ajv-errors@^0.3.1":
version "0.3.1"
resolved "https://registry.npmjs.org/@apideck/better-ajv-errors/-/better-ajv-errors-0.3.1.tgz"
@@ -2547,6 +2552,13 @@
dependencies:
regenerator-runtime "^0.13.4"
+"@babel/runtime@^7.9.2":
+ version "7.19.0"
+ resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.19.0.tgz#22b11c037b094d27a8a2504ea4dcff00f50e2259"
+ integrity sha512-eR8Lo9hnDS7tqkO7NsV+mKvCmv5boaXFSZ70DnfhcgiEne8hv9oCEd36Klw74EtizEqLsy4YnW8UWwpBVolHZA==
+ dependencies:
+ regenerator-runtime "^0.13.4"
+
"@babel/template@^7.12.13", "@babel/template@^7.12.7", "@babel/template@^7.3.3":
version "7.12.13"
resolved "https://registry.npmjs.org/@babel/template/-/template-7.12.13.tgz"
@@ -4338,6 +4350,44 @@
lodash.merge "^4.6.2"
postcss-selector-parser "6.0.10"
+"@testing-library/dom@^8.5.0":
+ version "8.18.1"
+ resolved "https://registry.yarnpkg.com/@testing-library/dom/-/dom-8.18.1.tgz#80f91be02bc171fe5a3a7003f88207be31ac2cf3"
+ integrity sha512-oEvsm2B/WtcHKE+IcEeeCqNU/ltFGaVyGbpcm4g/2ytuT49jrlH9x5qRKL/H3A6yfM4YAbSbC0ceT5+9CEXnLg==
+ dependencies:
+ "@babel/code-frame" "^7.10.4"
+ "@babel/runtime" "^7.12.5"
+ "@types/aria-query" "^4.2.0"
+ aria-query "^5.0.0"
+ chalk "^4.1.0"
+ dom-accessibility-api "^0.5.9"
+ lz-string "^1.4.4"
+ pretty-format "^27.0.2"
+
+"@testing-library/jest-dom@5.16.5":
+ version "5.16.5"
+ resolved "https://registry.yarnpkg.com/@testing-library/jest-dom/-/jest-dom-5.16.5.tgz#3912846af19a29b2dbf32a6ae9c31ef52580074e"
+ integrity sha512-N5ixQ2qKpi5OLYfwQmUb/5mSV9LneAcaUfp32pn4yCnpb8r/Yz0pXFPck21dIicKmi+ta5WRAknkZCfA8refMA==
+ dependencies:
+ "@adobe/css-tools" "^4.0.1"
+ "@babel/runtime" "^7.9.2"
+ "@types/testing-library__jest-dom" "^5.9.1"
+ aria-query "^5.0.0"
+ chalk "^3.0.0"
+ css.escape "^1.5.1"
+ dom-accessibility-api "^0.5.6"
+ lodash "^4.17.15"
+ redent "^3.0.0"
+
+"@testing-library/react@13.4.0":
+ version "13.4.0"
+ resolved "https://registry.yarnpkg.com/@testing-library/react/-/react-13.4.0.tgz#6a31e3bf5951615593ad984e96b9e5e2d9380966"
+ integrity sha512-sXOGON+WNTh3MLE9rve97ftaZukN3oNf2KjDy7YTx6hcTO2uuLHuCGynMDhFwGw/jYf4OJ2Qk0i4i79qMNNkyw==
+ dependencies:
+ "@babel/runtime" "^7.12.5"
+ "@testing-library/dom" "^8.5.0"
+ "@types/react-dom" "^18.0.0"
+
"@tootallnate/once@1":
version "1.1.2"
resolved "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz"
@@ -4378,6 +4428,11 @@
resolved "https://registry.npmjs.org/@types/anymatch/-/anymatch-1.3.1.tgz"
integrity sha512-/+CRPXpBDpo2RK9C68N3b2cOvO0Cf5B9aPijHsoDQTHivnGSObdOF2BRQOYjojWTDy6nQvMjmqRXIxH55VjxxA==
+"@types/aria-query@^4.2.0":
+ version "4.2.2"
+ resolved "https://registry.yarnpkg.com/@types/aria-query/-/aria-query-4.2.2.tgz#ed4e0ad92306a704f9fb132a0cfcf77486dbe2bc"
+ integrity sha512-HnYpAE1Y6kRyKM/XkEuiRQhTHvkzMBurTHnpFLYLBGPIylZNPs9jJcuOOYWxPLJCSEtmZT0Y8rHDokKN7rRTig==
+
"@types/babel__core@^7.0.0", "@types/babel__core@^7.1.7":
version "7.1.12"
resolved "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.12.tgz"
@@ -4773,7 +4828,7 @@
dependencies:
"@types/istanbul-lib-report" "*"
-"@types/jest@29.0.3":
+"@types/jest@*", "@types/jest@29.0.3":
version "29.0.3"
resolved "https://registry.yarnpkg.com/@types/jest/-/jest-29.0.3.tgz#b61a5ed100850686b8d3c5e28e3a1926b2001b59"
integrity sha512-F6ukyCTwbfsEX5F2YmVYmM5TcTHy1q9P5rWlRbrk56KyMh3v9xRGUO3aa8+SkvMi0SHXtASJv1283enXimC0Og==
@@ -4848,10 +4903,10 @@
resolved "https://registry.yarnpkg.com/@types/node/-/node-17.0.25.tgz#527051f3c2f77aa52e5dc74e45a3da5fb2301448"
integrity sha512-wANk6fBrUwdpY4isjWrKTufkrXdu1D2YHCot2fD/DfWxF5sMrVSA+KN7ydckvaTCh0HiqX9IVl0L5/ZoXg5M7w==
-"@types/node@18.7.22":
- version "18.7.22"
- resolved "https://registry.yarnpkg.com/@types/node/-/node-18.7.22.tgz#76f7401362ad63d9d7eefa7dcdfa5fcd9baddff3"
- integrity sha512-TsmoXYd4zrkkKjJB0URF/mTIKPl+kVcbqClB2F/ykU7vil1BfWZVndOnpEIozPv4fURD28gyPFeIkW2G+KXOvw==
+"@types/node@18.7.23":
+ version "18.7.23"
+ resolved "https://registry.yarnpkg.com/@types/node/-/node-18.7.23.tgz#75c580983846181ebe5f4abc40fe9dfb2d65665f"
+ integrity sha512-DWNcCHolDq0ZKGizjx2DZjR/PqsYwAcYUJmfMWqtVU2MBMG5Mo+xFZrhGId5r/O5HOuMPyQEcM6KUBp5lBZZBg==
"@types/node@^14.0.10 || ^16.0.0", "@types/node@^14.14.20 || ^16.0.0":
version "16.11.15"
@@ -4910,7 +4965,7 @@
resolved "https://registry.npmjs.org/@types/qs/-/qs-6.9.6.tgz"
integrity sha512-0/HnwIfW4ki2D8L8c9GVcG5I72s9jP5GSLVF0VIXDW00kmIpA6O33G7a8n59Tmh7Nz0WUC3rSb7PTY/sdW2JzA==
-"@types/react-dom@18.0.6":
+"@types/react-dom@18.0.6", "@types/react-dom@^18.0.0":
version "18.0.6"
resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-18.0.6.tgz#36652900024842b74607a17786b6662dd1e103a1"
integrity sha512-/5OFZgfIPSwy+YuIBP/FgJnQnsxhZhjjrnxudMddeblOouIodEQ75X14Rr4wGSG/bknL+Omy9iWlLo1u/9GzAA==
@@ -4984,6 +5039,13 @@
resolved "https://registry.npmjs.org/@types/tapable/-/tapable-1.0.7.tgz"
integrity sha512-0VBprVqfgFD7Ehb2vd8Lh9TG3jP98gvr8rgehQqzztZNI7o8zS8Ad4jyZneKELphpuE212D8J70LnSNQSyO6bQ==
+"@types/testing-library__jest-dom@^5.9.1":
+ version "5.14.5"
+ resolved "https://registry.yarnpkg.com/@types/testing-library__jest-dom/-/testing-library__jest-dom-5.14.5.tgz#d113709c90b3c75fdb127ec338dad7d5f86c974f"
+ integrity sha512-SBwbxYoyPIvxHbeHxTZX2Pe/74F/tX2/D3mMvzabdeJ25bBojfW0TyB8BHrbq/9zaaKICJZjLP+8r6AeZMFCuQ==
+ dependencies:
+ "@types/jest" "*"
+
"@types/trusted-types@^2.0.2":
version "2.0.2"
resolved "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.2.tgz"
@@ -5755,6 +5817,11 @@ aria-query@^4.2.2:
"@babel/runtime" "^7.10.2"
"@babel/runtime-corejs3" "^7.10.2"
+aria-query@^5.0.0:
+ version "5.0.2"
+ resolved "https://registry.yarnpkg.com/aria-query/-/aria-query-5.0.2.tgz#0b8a744295271861e1d933f8feca13f9b70cfdc1"
+ integrity sha512-eigU3vhqSO+Z8BKDnVLN/ompjhf3pYzecKXz8+whRy+9gZu8n1TCGfwzQUUPnqdHl9ax1Hr9031orZ+UOEYr7Q==
+
arr-diff@^4.0.0:
version "4.0.0"
resolved "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz"
@@ -6756,6 +6823,14 @@ chalk@^2.0.0, chalk@^2.4.1, chalk@^2.4.2:
escape-string-regexp "^1.0.5"
supports-color "^5.3.0"
+chalk@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/chalk/-/chalk-3.0.0.tgz#3f73c2bf526591f574cc492c51e2456349f844e4"
+ integrity sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==
+ dependencies:
+ ansi-styles "^4.1.0"
+ supports-color "^7.1.0"
+
chalk@^4.0.0, chalk@^4.1.0:
version "4.1.0"
resolved "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz"
@@ -7625,6 +7700,11 @@ css-what@^5.1.0:
resolved "https://registry.npmjs.org/css-what/-/css-what-5.1.0.tgz"
integrity sha512-arSMRWIIFY0hV8pIxZMEfmMI47Wj3R/aWpZDDxWYCPEiOMv6tfOrnpDtgxBYPEQD4V0Y/958+1TdC3iWTFcUPw==
+css.escape@^1.5.1:
+ version "1.5.1"
+ resolved "https://registry.yarnpkg.com/css.escape/-/css.escape-1.5.1.tgz#42e27d4fa04ae32f931a4b4d4191fa9cddee97cb"
+ integrity sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==
+
cssdb@^5.0.0:
version "5.0.0"
resolved "https://registry.npmjs.org/cssdb/-/cssdb-5.0.0.tgz"
@@ -8146,6 +8226,11 @@ doctrine@^3.0.0:
dependencies:
esutils "^2.0.2"
+dom-accessibility-api@^0.5.6, dom-accessibility-api@^0.5.9:
+ version "0.5.14"
+ resolved "https://registry.yarnpkg.com/dom-accessibility-api/-/dom-accessibility-api-0.5.14.tgz#56082f71b1dc7aac69d83c4285eef39c15d93f56"
+ integrity sha512-NMt+m9zFMPZe0JcY9gN224Qvk6qLIdqex29clBvc/y75ZBX9YA9wNK3frsYvu2DI1xcCIwxwnX+TlsJ2DSOADg==
+
dom-converter@^0.2, dom-converter@^0.2.0:
version "0.2.0"
resolved "https://registry.npmjs.org/dom-converter/-/dom-converter-0.2.0.tgz"
@@ -12057,6 +12142,11 @@ lru-cache@^6.0.0:
dependencies:
yallist "^4.0.0"
+lz-string@^1.4.4:
+ version "1.4.4"
+ resolved "https://registry.yarnpkg.com/lz-string/-/lz-string-1.4.4.tgz#c0d8eaf36059f705796e1e344811cf4c498d3a26"
+ integrity sha512-0ckx7ZHRPqb0oUm8zNr+90mtf9DQB60H1wMCjBtfi62Kl3a7JbHob6gA2bC+xRvZoOL+1hzUK8jeuEIQE8svEQ==
+
magic-string@^0.25.0, magic-string@^0.25.7:
version "0.25.7"
resolved "https://registry.npmjs.org/magic-string/-/magic-string-0.25.7.tgz"
@@ -14608,7 +14698,7 @@ pretty-error@^4.0.0:
lodash "^4.17.20"
renderkid "^3.0.0"
-pretty-format@^27.4.2, pretty-format@^27.5.1:
+pretty-format@^27.0.2, pretty-format@^27.4.2, pretty-format@^27.5.1:
version "27.5.1"
resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-27.5.1.tgz#2181879fdea51a7a5851fb39d920faa63f01d88e"
integrity sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==
@@ -15317,6 +15407,14 @@ redent@^1.0.0:
indent-string "^2.1.0"
strip-indent "^1.0.1"
+redent@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/redent/-/redent-3.0.0.tgz#e557b7998316bb53c9f1f56fa626352c6963059f"
+ integrity sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==
+ dependencies:
+ indent-string "^4.0.0"
+ strip-indent "^3.0.0"
+
reduce-css-calc@1.3.0:
version "1.3.0"
resolved "https://registry.yarnpkg.com/reduce-css-calc/-/reduce-css-calc-1.3.0.tgz#747c914e049614a4c9cfbba629871ad1d2927716"