diff --git a/frontend/package.json b/frontend/package.json
index 48a5fe3673..503a47fb47 100644
--- a/frontend/package.json
+++ b/frontend/package.json
@@ -66,8 +66,10 @@
"chartjs-adapter-date-fns": "3.0.0",
"classnames": "2.3.2",
"copy-to-clipboard": "3.3.3",
+ "countries-and-timezones": "^3.4.0",
"cypress": "9.7.0",
"date-fns": "2.29.3",
+ "date-fns-tz": "^2.0.0",
"debounce": "1.2.1",
"deep-diff": "1.0.2",
"dequal": "2.0.3",
@@ -92,7 +94,9 @@
"react-dropzone": "14.2.3",
"react-error-boundary": "3.1.4",
"react-hooks-global-state": "2.1.0",
+ "react-joyride": "^2.5.3",
"react-markdown": "^8.0.4",
+ "react-linkify": "^1.0.0-alpha",
"react-router-dom": "6.8.1",
"react-table": "7.8.0",
"react-test-renderer": "17.0.2",
@@ -107,10 +111,7 @@
"vite-plugin-svgr": "2.4.0",
"vite-tsconfig-paths": "4.0.5",
"vitest": "0.28.5",
- "whatwg-fetch": "3.6.2",
- "countries-and-timezones": "^3.4.0",
- "date-fns-tz": "^2.0.0",
- "react-linkify": "^1.0.0-alpha"
+ "whatwg-fetch": "3.6.2"
},
"optionalDependencies": {
"orval": "^6.10.3"
diff --git a/frontend/src/component/App.tsx b/frontend/src/component/App.tsx
index 65a50202c2..ad70203d09 100644
--- a/frontend/src/component/App.tsx
+++ b/frontend/src/component/App.tsx
@@ -20,6 +20,7 @@ import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
import MaintenanceBanner from './maintenance/MaintenanceBanner';
import { styled } from '@mui/material';
import { InitialRedirect } from './InitialRedirect';
+import { Demo } from './demo/Demo';
const StyledContainer = styled('div')(() => ({
'& ul': {
@@ -100,6 +101,13 @@ export const App = () => {
+ }
+ />
+
>
diff --git a/frontend/src/component/demo/Demo.tsx b/frontend/src/component/demo/Demo.tsx
new file mode 100644
index 0000000000..31ceb5396d
--- /dev/null
+++ b/frontend/src/component/demo/Demo.tsx
@@ -0,0 +1,236 @@
+import { Typography } from '@mui/material';
+import InfoOutlinedIcon from '@mui/icons-material/InfoOutlined';
+import { Badge } from 'component/common/Badge/Badge';
+import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
+import { useEffect, useState } from 'react';
+import { Step } from 'react-joyride';
+import { DemoTopics } from './DemoTopics/DemoTopics';
+import { DemoSteps } from './DemoSteps/DemoSteps';
+import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
+import { createLocalStorage } from 'utils/createLocalStorage';
+
+export interface ITutorialTopic {
+ title: string;
+ steps: Step[];
+}
+
+const defaultProgress = {
+ expanded: true,
+ run: false,
+ topic: 0,
+ steps: [0],
+};
+
+const { value: storedProgress, setValue: setStoredProgress } =
+ createLocalStorage('Tutorial:v1', defaultProgress);
+
+const TOPICS: ITutorialTopic[] = [
+ {
+ title: 'Import',
+ steps: [
+ {
+ target: 'button[data-testid="IMPORT_BUTTON"]',
+ title: (
+
+ Import toggle configuration
+
+ ),
+ content: (
+ <>
+
+ This is a cool feature that enables you to import
+ your toggle configuration. This is just an example
+ and not part of the final guide.
+
+ >
+ ),
+ disableBeacon: true,
+ },
+ ],
+ },
+ {
+ title: 'New feature toggle',
+ steps: [
+ {
+ target: 'button[data-testid="NAVIGATE_TO_CREATE_FEATURE"]',
+ title: (
+
+ Add a new feature toggle
+
+ ),
+ content: (
+ <>
+
+ You can use this button to add a new feature toggle.
+ This is just an example and not part of the final
+ guide.
+
+ >
+ ),
+ disableBeacon: true,
+ },
+ ],
+ },
+ {
+ title: 'Enable/disable a feature toggle',
+ steps: [
+ {
+ target: '.MuiSwitch-sizeMedium',
+ title: (
+
+ Enable/disable a feature toggle
+
+ ),
+ content: (
+ <>
+
+ The simplest way to use a feature toggle is to
+ enable or disable it for everyone (on/off).
+
+ }
+ >
+ Look at the demo page when toggling!
+
+ >
+ ),
+ disableBeacon: true,
+ },
+ ],
+ },
+ {
+ title: 'Community',
+ steps: [
+ {
+ target: 'a[href="https://twitter.com/getunleash"]',
+ title: (
+
+ Follow us on Twitter!
+
+ ),
+ content: (
+ <>
+
+ Following us on Twitter is one of the easiest ways
+ of keeping up with us. This is just an example and
+ not part of the final guide.
+
+ >
+ ),
+ disableBeacon: true,
+ },
+ {
+ target: 'a[href="https://www.linkedin.com/company/getunleash"]',
+ title: (
+
+ Follow us on LinkedIn!
+
+ ),
+ content: (
+ <>
+
+ You can also follow us LinkedIn. This is just an
+ example and not part of the final guide.
+
+ >
+ ),
+ disableBeacon: true,
+ },
+ {
+ target: 'a[href="https://github.com/Unleash/unleash"]',
+ title: (
+
+ Check out Unleash on GitHub!
+
+ ),
+ content: (
+ <>
+
+ Unleash is open-source, check out the project on
+ GitHub. This is just an example and not part of the
+ final guide.
+
+ >
+ ),
+ disableBeacon: true,
+ },
+ {
+ target: 'a[href="https://slack.unleash.run"]',
+ title: (
+ Join us on Slack!
+ ),
+ content: (
+ <>
+
+ Join our community in Slack. This is just an example
+ and not part of the final guide.
+
+ >
+ ),
+ disableBeacon: true,
+ },
+ ],
+ },
+];
+
+export const Demo = () => {
+ const { uiConfig } = useUiConfig();
+ const [loaded, setLoaded] = useState(false);
+ const [expanded, setExpanded] = useState(storedProgress.expanded ?? true);
+ const [run, setRun] = useState(storedProgress.run ?? false);
+ const [topic, setTopic] = useState(storedProgress.topic ?? 0);
+ const [steps, setSteps] = useState(storedProgress.steps ?? [0]);
+
+ useEffect(() => {
+ setTimeout(() => {
+ setLoaded(true);
+ }, 1000);
+ }, []);
+
+ useEffect(() => {
+ setStoredProgress({
+ expanded,
+ run,
+ topic,
+ steps,
+ });
+ }, [expanded, run, topic, steps]);
+
+ if (!uiConfig.flags.demo) return null;
+
+ return (
+ <>
+ {
+ setTopic(topic);
+ setSteps(steps => {
+ const newSteps = [...steps];
+ newSteps[topic] = 0;
+ return newSteps;
+ });
+ }}
+ topics={TOPICS}
+ />
+
+ }
+ />
+ >
+ );
+};
diff --git a/frontend/src/component/demo/DemoSteps/DemoSteps.tsx b/frontend/src/component/demo/DemoSteps/DemoSteps.tsx
new file mode 100644
index 0000000000..7bef1012e6
--- /dev/null
+++ b/frontend/src/component/demo/DemoSteps/DemoSteps.tsx
@@ -0,0 +1,232 @@
+import Joyride, {
+ ACTIONS,
+ CallBackProps,
+ EVENTS,
+ STATUS,
+ TooltipRenderProps,
+} from 'react-joyride';
+import { Button, Typography, styled, useTheme } from '@mui/material';
+import { ITutorialTopic } from '../Demo';
+import { useEffect } from 'react';
+import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
+
+const StyledTooltip = styled('div')(({ theme }) => ({
+ backgroundColor: theme.palette.background.paper,
+ color: theme.palette.text.primary,
+ borderRadius: theme.shape.borderRadiusMedium,
+ width: '100%',
+ maxWidth: theme.spacing(45),
+ padding: theme.spacing(3),
+}));
+
+const StyledTooltipTitle = styled('div')(({ theme }) => ({
+ display: 'flex',
+ alignItems: 'center',
+ gap: theme.spacing(1),
+ marginBottom: theme.spacing(1),
+ flexWrap: 'wrap',
+}));
+
+const StyledTooltipActions = styled('div')(({ theme }) => ({
+ display: 'flex',
+ justifyContent: 'space-between',
+ marginTop: theme.spacing(3),
+ '&&& button': {
+ '&:first-of-type': {
+ marginLeft: theme.spacing(-2),
+ },
+ fontSize: theme.fontSizes.smallBody,
+ },
+}));
+
+const StyledTooltipPrimaryActions = styled('div')(({ theme }) => ({
+ display: 'flex',
+ gap: theme.spacing(1),
+}));
+
+interface IDemoStepsProps {
+ run: boolean;
+ setRun: React.Dispatch>;
+ setExpanded: React.Dispatch>;
+ steps: number[];
+ setSteps: React.Dispatch>;
+ topic: number;
+ setTopic: React.Dispatch>;
+ topics: ITutorialTopic[];
+}
+
+export const DemoSteps = ({
+ run,
+ setRun,
+ setExpanded,
+ steps,
+ setSteps,
+ topic,
+ setTopic,
+ topics,
+}: IDemoStepsProps) => {
+ const theme = useTheme();
+
+ const skip = () => {
+ setRun(false);
+ setTopic(-1);
+ setExpanded(false);
+ };
+
+ const back = () => {
+ if (steps[topic] === 0) {
+ setRun(false);
+ const newTopic = topic - 1;
+ setTopic(newTopic);
+ setSteps(steps => {
+ const newSteps = [...steps];
+ newSteps[newTopic] = topics[newTopic].steps.length - 1;
+ return newSteps;
+ });
+ } else {
+ setSteps(steps => {
+ const newSteps = [...steps];
+ newSteps[topic] = steps[topic] - 1;
+ return newSteps;
+ });
+ }
+ };
+
+ const joyrideCallback = (data: CallBackProps) => {
+ const { action, index, status, type, step } = data;
+
+ if (action === ACTIONS.UPDATE) {
+ const el = document.querySelector(step.target as string);
+ if (el) {
+ el.scrollIntoView({
+ block: 'center',
+ });
+ }
+ }
+
+ if (
+ ([EVENTS.STEP_AFTER, EVENTS.TARGET_NOT_FOUND] as string[]).includes(
+ type
+ )
+ ) {
+ const newStep = index + (action === ACTIONS.PREV ? -1 : 1);
+ setSteps(steps => {
+ const newSteps = [...steps];
+ newSteps[topic] = newStep;
+ return newSteps;
+ });
+ } else if (
+ ([STATUS.FINISHED, STATUS.SKIPPED] as string[]).includes(status)
+ ) {
+ setRun(false);
+ if (topic === topics.length - 1) {
+ setTopic(-1);
+ setExpanded(false);
+ } else {
+ const newTopic = topic + 1;
+ setTopic(newTopic);
+ setSteps(steps => {
+ const newSteps = [...steps];
+ newSteps[newTopic] = 0;
+ return newSteps;
+ });
+ }
+ }
+ };
+
+ useEffect(() => {
+ setRun(true);
+ }, [topic, steps]);
+
+ if (topic === -1) return null;
+
+ return (
+ {
+ const { onClick } = primaryProps;
+
+ return (
+
+
+ {step.title}
+ 1}
+ show={
+
+ (step {steps[topic] + 1} of{' '}
+ {topics[topic].steps.length})
+
+ }
+ />
+
+ {step.content}
+
+
+
+ 0 || steps[topic] > 0}
+ show={
+
+ }
+ />
+
+
+
+
+ );
+ }}
+ />
+ );
+};
diff --git a/frontend/src/component/demo/DemoTopics/DemoTopics.tsx b/frontend/src/component/demo/DemoTopics/DemoTopics.tsx
new file mode 100644
index 0000000000..2105e08d71
--- /dev/null
+++ b/frontend/src/component/demo/DemoTopics/DemoTopics.tsx
@@ -0,0 +1,210 @@
+import {
+ Accordion,
+ AccordionDetails,
+ AccordionSummary,
+ Button,
+ LinearProgress,
+ Typography,
+ linearProgressClasses,
+ styled,
+} from '@mui/material';
+import { CheckCircle, CircleOutlined, ExpandMore } from '@mui/icons-material';
+import { ITutorialTopic } from '../Demo';
+import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
+
+const StyledAccordion = styled(Accordion)(({ theme }) => ({
+ position: 'fixed',
+ bottom: 0,
+ left: 0,
+ width: '100%',
+ maxWidth: theme.spacing(30),
+ zIndex: theme.zIndex.fab,
+ '&&&': {
+ borderRadius: 0,
+ borderTopLeftRadius: theme.shape.borderRadiusLarge,
+ borderTopRightRadius: theme.shape.borderRadiusLarge,
+ },
+ '&:before': {
+ display: 'none',
+ },
+ '& .expand-icon': {
+ position: 'absolute',
+ right: theme.spacing(2),
+ fontSize: theme.fontSizes.mainHeader,
+ transition: 'transform 150ms cubic-bezier(0.4, 0, 0.2, 1) 0ms',
+ },
+ '&.Mui-expanded .expand-icon': {
+ transform: 'rotate(180deg)',
+ },
+}));
+
+const StyledAccordionSummary = styled(AccordionSummary)(({ theme }) => ({
+ '& .MuiAccordionSummary-content': {
+ flexDirection: 'column',
+ alignItems: 'center',
+ },
+ backgroundColor: theme.palette.primary.main,
+ color: theme.palette.primary.contrastText,
+ borderTopLeftRadius: theme.shape.borderRadiusLarge,
+ borderTopRightRadius: theme.shape.borderRadiusLarge,
+}));
+
+const StyledExpandMoreIcon = styled(ExpandMore)(({ theme }) => ({
+ color: theme.palette.primary.contrastText,
+}));
+
+const StyledTitle = styled('div')({
+ display: 'flex',
+ alignItems: 'center',
+});
+
+const StyledSubtitle = styled(Typography)(({ theme }) => ({
+ fontSize: theme.fontSizes.smallerBody,
+ marginTop: theme.spacing(0.5),
+ marginBottom: theme.spacing(0.5),
+}));
+
+const StyledProgress = styled('div')(({ theme }) => ({
+ width: '100%',
+ display: 'flex',
+ alignItems: 'center',
+ gap: theme.spacing(1.5),
+}));
+
+const StyledLinearProgress = styled(LinearProgress)(({ theme }) => ({
+ width: '100%',
+ height: theme.spacing(1),
+ borderRadius: theme.shape.borderRadius,
+ [`&.${linearProgressClasses.colorPrimary}`]: {
+ backgroundColor: theme.palette.primary.dark,
+ },
+ [`& .${linearProgressClasses.bar}`]: {
+ borderRadius: theme.shape.borderRadius,
+ backgroundColor: theme.palette.primary.contrastText,
+ },
+}));
+
+const StyledStep = styled('li', {
+ shouldForwardProp: prop => prop !== 'selected' && prop !== 'completed',
+})<{ selected?: boolean; completed?: boolean }>(
+ ({ theme, selected, completed }) => ({
+ padding: theme.spacing(1),
+ cursor: 'pointer',
+ display: 'flex',
+ alignItems: 'center',
+ marginTop: theme.spacing(1),
+ borderRadius: theme.shape.borderRadius,
+ gap: theme.spacing(1),
+ backgroundColor: theme.palette.background.elevation2,
+ ...(selected && {
+ backgroundColor: theme.palette.secondary.light,
+ fontWeight: theme.typography.fontWeightBold,
+ border: `1px solid ${theme.palette.primary.main}`,
+ }),
+ ...(completed && {
+ backgroundColor: theme.palette.background.elevation1,
+ textDecoration: 'line-through',
+ }),
+ })
+);
+
+const StyledCheckCircle = styled(CheckCircle)(({ theme }) => ({
+ color: theme.palette.primary.main,
+ fontSize: theme.fontSizes.bodySize,
+}));
+
+const StyledCircleOutlined = styled(CircleOutlined)(({ theme }) => ({
+ color: theme.palette.neutral.main,
+ fontSize: theme.fontSizes.bodySize,
+}));
+
+const StyledStepIcon = styled(ExpandMore)(({ theme }) => ({
+ transform: 'rotate(-90deg)',
+ fontSize: theme.fontSizes.bodySize,
+}));
+
+const StyledButton = styled(Button)(({ theme }) => ({
+ width: '100%',
+ marginTop: theme.spacing(2),
+ '&&&': {
+ fontSize: theme.fontSizes.smallBody,
+ },
+}));
+
+interface IDemoTopicsProps {
+ expanded: boolean;
+ setExpanded: React.Dispatch>;
+ steps: number[];
+ currentTopic: number;
+ setCurrentTopic: (topic: number) => void;
+ topics: ITutorialTopic[];
+}
+
+export const DemoTopics = ({
+ expanded,
+ setExpanded,
+ steps,
+ currentTopic,
+ setCurrentTopic,
+ topics,
+}: IDemoTopicsProps) => {
+ const completedSteps = steps.reduce((acc, step) => acc + (step || 0), 0);
+ const totalSteps = topics.flatMap(({ steps }) => steps).length;
+ const percentage = (completedSteps / totalSteps) * 100;
+
+ return (
+ setExpanded(expanded => !expanded)}
+ >
+
+
+ Unleash tutorial
+
+
+
+ Complete all steps to finish tutorial
+
+
+
+ {percentage.toFixed()}%
+
+
+
+
+
+
+ The steps will guide you
+
+ {topics.map((topic, index) => {
+ const selected = currentTopic === index;
+ const completed = steps[index] === topic.steps.length;
+ return (
+ setCurrentTopic(index)}
+ selected={selected}
+ completed={completed}
+ >
+ }
+ elseShow={}
+ />
+
+ {topic.title}
+
+
+
+ );
+ })}
+
+ View demo link again
+
+
+
+ );
+};
diff --git a/frontend/src/index.tsx b/frontend/src/index.tsx
index 0b800d72b0..4354f6a52e 100644
--- a/frontend/src/index.tsx
+++ b/frontend/src/index.tsx
@@ -15,6 +15,8 @@ import { InstanceStatus } from 'component/common/InstanceStatus/InstanceStatus';
import { UIProviderContainer } from 'component/providers/UIProvider/UIProviderContainer';
import { MessageBanner } from 'component/common/MessageBanner/MessageBanner';
+window.global ||= window;
+
ReactDOM.render(
diff --git a/frontend/yarn.lock b/frontend/yarn.lock
index a98b40eaca..906a93ecad 100644
--- a/frontend/yarn.lock
+++ b/frontend/yarn.lock
@@ -1469,6 +1469,11 @@
resolved "https://registry.yarnpkg.com/@exodus/schemasafe/-/schemasafe-1.0.0-rc.9.tgz#56b9c6df627190f2dcda15f81f25d68826d9be4d"
integrity sha512-dGGHpb61hLwifAu7sotuHFDBw6GTdpG8aKC0fsK17EuTzMRvUrH7lEAr6LTJ+sx3AZYed9yZ77rltVDHyg2hRg==
+"@gilbarbara/deep-equal@^0.1.1":
+ version "0.1.2"
+ resolved "https://registry.yarnpkg.com/@gilbarbara/deep-equal/-/deep-equal-0.1.2.tgz#1a106721368dba5e7e9fb7e9a3a6f9efbd8df36d"
+ integrity sha512-jk+qzItoEb0D0xSSmrKDDzf9sheQj/BAPxlgNxgmOaA3mxpUa6ndJLYGZKsJnIVEQSD8zcTbyILz7I0HcnBCRA==
+
"@humanwhocodes/config-array@^0.11.8":
version "0.11.8"
resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.11.8.tgz#03595ac2075a4dc0f191cc2131de14fbd7d410b9"
@@ -4142,6 +4147,11 @@ deepmerge@^2.2.1:
resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-2.2.1.tgz#5d3ff22a01c00f645405a2fbc17d0778a1801170"
integrity sha512-R9hc1Xa/NOBi9WRVUWg19rl1UB7Tt4kuPd+thNJgFZoxXsTz7ncaPaeIm+40oSGuP33DfMb4sZt1QIGiJzC4EA==
+deepmerge@^4.2.2:
+ version "4.3.1"
+ resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.3.1.tgz#44b5f2147cd3b00d4b56137685966f26fd25dd4a"
+ integrity sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==
+
defaults@^1.0.3:
version "1.0.4"
resolved "https://registry.yarnpkg.com/defaults/-/defaults-1.0.4.tgz#b0b02062c1e2aa62ff5d9528f0f98baa90978d7a"
@@ -4921,6 +4931,11 @@ executable@^4.1.1:
dependencies:
pify "^2.2.0"
+exenv@^1.2.2:
+ version "1.2.2"
+ resolved "https://registry.yarnpkg.com/exenv/-/exenv-1.2.2.tgz#2ae78e85d9894158670b03d47bec1f03bd91bb9d"
+ integrity sha512-Z+ktTxTwv9ILfgKCk32OX3n/doe+OcLTRtqK9pcL+JsP3J1/VW8Uvl4ZjLlKqeW4rzK4oesDOGMEMRIZqtP4Iw==
+
expect@^29.0.0:
version "29.3.1"
resolved "https://registry.yarnpkg.com/expect/-/expect-29.3.1.tgz#92877aad3f7deefc2e3f6430dd195b92295554a6"
@@ -5793,6 +5808,16 @@ is-interactive@^1.0.0:
resolved "https://registry.yarnpkg.com/is-interactive/-/is-interactive-1.0.0.tgz#cea6e6ae5c870a7b0a0004070b7b587e0252912e"
integrity sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==
+is-lite@^0.8.2:
+ version "0.8.2"
+ resolved "https://registry.yarnpkg.com/is-lite/-/is-lite-0.8.2.tgz#26ab98b32aae8cc8b226593b9a641d2bf4bd3b6a"
+ integrity sha512-JZfH47qTsslwaAsqbMI3Q6HNNjUuq6Cmzzww50TdP5Esb6e1y2sK2UAaZZuzfAzpoI2AkxoPQapZdlDuP6Vlsw==
+
+is-lite@^0.9.2:
+ version "0.9.2"
+ resolved "https://registry.yarnpkg.com/is-lite/-/is-lite-0.9.2.tgz#4b19e9a26b7c99ed50f748bcf088db57893d0730"
+ integrity sha512-qZuxbaEiKLOKhX4sbHLfhFN9iA3YciuZLb37/DfXCpWnz8p7qNL2lwkpxYMXfjlS8eEEjpULPZxAUI8N6FYvYQ==
+
is-map@^2.0.1, is-map@^2.0.2:
version "2.0.2"
resolved "https://registry.yarnpkg.com/is-map/-/is-map-2.0.2.tgz#00922db8c9bf73e81b7a335827bc2a43f2b91127"
@@ -7313,6 +7338,11 @@ pony-cause@1.1.1, pony-cause@^1.0.0:
resolved "https://registry.yarnpkg.com/pony-cause/-/pony-cause-1.1.1.tgz#f795524f83bebbf1878bd3587b45f69143cbf3f9"
integrity sha512-PxkIc/2ZpLiEzQXu5YRDOUgBlfGYBY8156HY5ZcRAwwonMk5W/MrJP2LLkG/hF7GEQzaHo2aS7ho6ZLCOvf+6g==
+popper.js@^1.16.0:
+ version "1.16.1"
+ resolved "https://registry.yarnpkg.com/popper.js/-/popper.js-1.16.1.tgz#2a223cb3dc7b6213d740e40372be40de43e65b1b"
+ integrity sha512-Wb4p1J4zyFTbM+u6WuO4XstYx4Ky9Cewe4DWrel7B0w6VVICvPwdOpotjzcf6eD8TsckVnIMNONQyPIUFOUbCQ==
+
postcss@^8.4.20, postcss@^8.4.21:
version "8.4.21"
resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.21.tgz#c639b719a57efc3187b13a1d765675485f4134f4"
@@ -7493,6 +7523,19 @@ react-error-boundary@3.1.4, react-error-boundary@^3.1.0:
dependencies:
"@babel/runtime" "^7.12.5"
+react-floater@^0.7.6:
+ version "0.7.6"
+ resolved "https://registry.yarnpkg.com/react-floater/-/react-floater-0.7.6.tgz#a98ee90e3d51200c6e6a564ff33496f3c0d7cfee"
+ integrity sha512-tt/15k/HpaShbtvWCwsQYLR+ebfUuYbl+oAUJ3DcEDkgYKeUcSkDey2PdAIERdVwzdFZANz47HbwoET2/Rduxg==
+ dependencies:
+ deepmerge "^4.2.2"
+ exenv "^1.2.2"
+ is-lite "^0.8.2"
+ popper.js "^1.16.0"
+ prop-types "^15.8.1"
+ react-proptype-conditional-require "^1.0.4"
+ tree-changes "^0.9.1"
+
react-hooks-global-state@2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/react-hooks-global-state/-/react-hooks-global-state-2.1.0.tgz#91d1d85c6c920f2e8681579d71d552207c5fe4ac"
@@ -7522,6 +7565,20 @@ react-linkify@^1.0.0-alpha:
dependencies:
linkify-it "^2.0.3"
tlds "^1.199.0"
+react-joyride@^2.5.3:
+ version "2.5.3"
+ resolved "https://registry.yarnpkg.com/react-joyride/-/react-joyride-2.5.3.tgz#3e753f80502a74abcc956babec4873d204345911"
+ integrity sha512-DKKvb/JAAsHm0x/RWO3WI6NOtTMHDso5v8MTauxTSz2dFs7Tu1rWg1BDBWmEMj6pUCvem7hblFbCiDAcvhs8tQ==
+ dependencies:
+ deepmerge "^4.2.2"
+ exenv "^1.2.2"
+ is-lite "^0.9.2"
+ prop-types "^15.8.1"
+ react-floater "^0.7.6"
+ react-is "^16.13.1"
+ scroll "^3.0.1"
+ scrollparent "^2.0.1"
+ tree-changes "^0.9.2"
react-markdown@^8.0.4:
version "8.0.4"
@@ -7544,6 +7601,11 @@ react-markdown@^8.0.4:
unist-util-visit "^4.0.0"
vfile "^5.0.0"
+react-proptype-conditional-require@^1.0.4:
+ version "1.0.4"
+ resolved "https://registry.yarnpkg.com/react-proptype-conditional-require/-/react-proptype-conditional-require-1.0.4.tgz#69c2d5741e6df5e08f230f36bbc2944ee1222555"
+ integrity sha512-nopsRn7KnGgazBe2c3H2+Kf+Csp6PGDRLiBkYEDMKY8o/EIgft/WnIm/OnAKTawZiLnJXHAqhpFBddvs6NiXlw==
+
react-refresh@^0.14.0:
version "0.14.0"
resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.14.0.tgz#4e02825378a5f227079554d4284889354e5f553e"
@@ -7920,6 +7982,16 @@ scheduler@^0.20.2:
loose-envify "^1.1.0"
object-assign "^4.1.1"
+scroll@^3.0.1:
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/scroll/-/scroll-3.0.1.tgz#d5afb59fb3592ee3df31c89743e78b39e4cd8a26"
+ integrity sha512-pz7y517OVls1maEzlirKO5nPYle9AXsFzTMNJrRGmT951mzpIBy7sNHOg5o/0MQd/NqliCiWnAi0kZneMPFLcg==
+
+scrollparent@^2.0.1:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/scrollparent/-/scrollparent-2.0.1.tgz#715d5b9cc57760fb22bdccc3befb5bfe06b1a317"
+ integrity sha512-HSdN78VMvFCSGCkh0oYX/tY4R3P1DW61f8+TeZZ4j2VLgfwvw0bpRSOv4PCVKisktIwbzHCfZsx+rLbbDBqIBA==
+
semver@7.3.8, semver@^7.3.2, semver@^7.3.7:
version "7.3.8"
resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.8.tgz#07a78feafb3f7b32347d725e33de7e2a2df67798"
@@ -8477,6 +8549,14 @@ tr46@~0.0.3:
resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a"
integrity sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==
+tree-changes@^0.9.1, tree-changes@^0.9.2:
+ version "0.9.3"
+ resolved "https://registry.yarnpkg.com/tree-changes/-/tree-changes-0.9.3.tgz#89433ab3b4250c2910d386be1f83912b7144efcc"
+ integrity sha512-vvvS+O6kEeGRzMglTKbc19ltLWNtmNt1cpBoSYLj/iEcPVvpJasemKOlxBrmZaCtDJoF+4bwv3m01UKYi8mukQ==
+ dependencies:
+ "@gilbarbara/deep-equal" "^0.1.1"
+ is-lite "^0.8.2"
+
trim-lines@^3.0.0:
version "3.0.1"
resolved "https://registry.yarnpkg.com/trim-lines/-/trim-lines-3.0.1.tgz#d802e332a07df861c48802c04321017b1bd87338"