From a7cd1d6bf47e1e2966e3029bd28badf74c2af46b Mon Sep 17 00:00:00 2001 From: Revanshu Paliwal Date: Fri, 14 Jul 2023 16:50:17 +0200 Subject: SONAR-19906 Migrating github action tutorial page to MIUI --- .../sonar-web/config/jest/SetupTestEnvironment.ts | 13 +++ server/sonar-web/package.json | 2 + .../__tests__/AzurePipelinesTutorial-it.tsx | 2 +- .../__tests__/BitbucketPipelinesTutorial-it.tsx | 20 ++-- .../js/components/tutorials/components/AllSet.tsx | 100 +++++++++---------- .../tutorials/components/CreateYmlFile.tsx | 12 +-- .../tutorials/components/DefaultProjectKey.tsx | 11 +- .../GithubCFamilyExampleRepositories.tsx | 21 ++-- .../tutorials/components/GradleBuild.tsx | 22 ++-- .../tutorials/components/InlineSnippet.tsx | 13 ++- .../tutorials/components/RenderOptions.tsx | 11 +- .../tutorials/components/SentenceWithFilename.tsx | 3 +- .../tutorials/components/TokenStepGenerator.tsx | 16 +-- .../tutorials/components/YamlFileStep.tsx | 11 +- .../tutorials/github-action/AnalysisCommand.tsx | 6 -- .../github-action/GitHubActionTutorial.tsx | 86 ++++++---------- .../tutorials/github-action/SecretStep.tsx | 111 +++++++++++---------- .../__tests__/GithubActionTutorial-it.tsx | 44 ++++---- .../tutorials/github-action/commands/CFamily.tsx | 14 ++- .../tutorials/github-action/commands/DotNet.tsx | 23 ++--- .../tutorials/github-action/commands/Gradle.tsx | 3 - .../tutorials/github-action/commands/JavaMaven.tsx | 23 ++--- .../tutorials/github-action/commands/Others.tsx | 3 - .../jenkins/__tests__/JenkinsTutorial-it.tsx | 15 ++- .../src/main/js/components/tutorials/test-utils.ts | 2 +- .../src/main/js/hooks/useIntersectionObserver.ts | 53 ++++++++++ server/sonar-web/yarn.lock | 67 +++++++++++++ 27 files changed, 410 insertions(+), 297 deletions(-) create mode 100644 server/sonar-web/src/main/js/hooks/useIntersectionObserver.ts (limited to 'server') diff --git a/server/sonar-web/config/jest/SetupTestEnvironment.ts b/server/sonar-web/config/jest/SetupTestEnvironment.ts index 54350855106..a10eb5dd7e3 100644 --- a/server/sonar-web/config/jest/SetupTestEnvironment.ts +++ b/server/sonar-web/config/jest/SetupTestEnvironment.ts @@ -21,6 +21,12 @@ import React from 'react'; (window as any).React = React; +const MockObserver = { + observe: jest.fn(), + unobserve: jest.fn(), + disconnect: jest.fn(), +}; + const content = document.createElement('div'); content.id = 'content'; document.documentElement.appendChild(content); @@ -37,3 +43,10 @@ jest.mock('../../src/main/js/helpers/l10n', () => ({ translateWithParameters: (messageKey: string, ...parameters: Array) => [messageKey, ...parameters].join('.'), })); + +const MockIntersectionObserverEntries = [{ isIntersecting: true }]; + +(window as any).IntersectionObserver = jest.fn().mockImplementation((callback) => { + callback(MockIntersectionObserverEntries, MockObserver); + return MockObserver; +}); diff --git a/server/sonar-web/package.json b/server/sonar-web/package.json index 6d42f3de7bb..dfdd3cf7092 100644 --- a/server/sonar-web/package.json +++ b/server/sonar-web/package.json @@ -11,6 +11,8 @@ "@emotion/react": "11.11.1", "@emotion/styled": "11.11.0", "@primer/octicons-react": "19.3.0", + "@react-spring/rafz": "9.7.3", + "@react-spring/web": "9.7.3", "@tanstack/react-query": "4.29.14", "classnames": "2.3.2", "clipboard": "2.0.11", diff --git a/server/sonar-web/src/main/js/components/tutorials/azure-pipelines/__tests__/AzurePipelinesTutorial-it.tsx b/server/sonar-web/src/main/js/components/tutorials/azure-pipelines/__tests__/AzurePipelinesTutorial-it.tsx index 6080a73c7be..541a20afae0 100644 --- a/server/sonar-web/src/main/js/components/tutorials/azure-pipelines/__tests__/AzurePipelinesTutorial-it.tsx +++ b/server/sonar-web/src/main/js/components/tutorials/azure-pipelines/__tests__/AzurePipelinesTutorial-it.tsx @@ -216,7 +216,7 @@ function assertOtherStepIsCorrectlyRendered() { function assertFinishStepIsCorrectlyRendered() { expect( screen.getByRole('heading', { - name: 'onboarding.tutorial.ci_outro.all_set.title', + name: 'onboarding.tutorial.ci_outro.done', }) ).toBeInTheDocument(); } diff --git a/server/sonar-web/src/main/js/components/tutorials/bitbucket-pipelines/__tests__/BitbucketPipelinesTutorial-it.tsx b/server/sonar-web/src/main/js/components/tutorials/bitbucket-pipelines/__tests__/BitbucketPipelinesTutorial-it.tsx index 0a1285123a2..07691d586b8 100644 --- a/server/sonar-web/src/main/js/components/tutorials/bitbucket-pipelines/__tests__/BitbucketPipelinesTutorial-it.tsx +++ b/server/sonar-web/src/main/js/components/tutorials/bitbucket-pipelines/__tests__/BitbucketPipelinesTutorial-it.tsx @@ -28,7 +28,7 @@ import { } from '../../../../helpers/mocks/alm-settings'; import { mockComponent } from '../../../../helpers/mocks/component'; import { mockLanguage, mockLoggedInUser } from '../../../../helpers/testMocks'; -import { renderApp, RenderContext } from '../../../../helpers/testReactTestingUtils'; +import { RenderContext, renderApp } from '../../../../helpers/testReactTestingUtils'; import { AlmKeys } from '../../../../types/alm-settings'; import { Feature } from '../../../../types/features'; import { @@ -75,28 +75,28 @@ it('should follow and complete all steps', async () => { // Create/update configuration file step // Maven await user.click(ui.mavenBuildButton.get()); - expect(getCopyToClipboardValue(1)).toMatchSnapshot('Maven: bitbucket-pipelines.yml'); + expect(getCopyToClipboardValue(0, 'Copy')).toMatchSnapshot('Maven: bitbucket-pipelines.yml'); // Gradle await user.click(ui.gradleBuildButton.get()); - expect(getCopyToClipboardValue(2)).toMatchSnapshot('Groovy: build.gradle'); + expect(getCopyToClipboardValue(0, 'Copy')).toMatchSnapshot('Groovy: build.gradle'); await user.click(ui.gradleDSLButton(GradleBuildDSL.Kotlin).get()); - expect(getCopyToClipboardValue(2)).toMatchSnapshot('Kotlin: build.gradle.kts'); - expect(getCopyToClipboardValue(4)).toMatchSnapshot('Gradle: bitbucket-pipelines.yml'); + expect(getCopyToClipboardValue(0, 'Copy')).toMatchSnapshot('Kotlin: build.gradle.kts'); + expect(getCopyToClipboardValue(1, 'Copy')).toMatchSnapshot('Gradle: bitbucket-pipelines.yml'); // .NET await user.click(ui.dotnetBuildButton.get()); - expect(getCopyToClipboardValue(1)).toMatchSnapshot('.NET: bitbucket-pipelines.yml'); + expect(getCopyToClipboardValue(0, 'Copy')).toMatchSnapshot('.NET: bitbucket-pipelines.yml'); // CFamily await user.click(ui.cFamilyBuildButton.get()); - expect(getCopyToClipboardValue()).toMatchSnapshot('CFamily: sonar-project.properties'); - expect(getCopyToClipboardValue(2)).toMatchSnapshot('CFamily: bitbucket-pipelines.yml'); + expect(getCopyToClipboardValue(0, 'Copy')).toMatchSnapshot('CFamily: sonar-project.properties'); + expect(getCopyToClipboardValue(1, 'Copy')).toMatchSnapshot('CFamily: bitbucket-pipelines.yml'); // Other await user.click(ui.otherBuildButton.get()); - expect(getCopyToClipboardValue()).toMatchSnapshot('Other: sonar-project.properties'); - expect(getCopyToClipboardValue(2)).toMatchSnapshot('Other: .github/workflows/build.yml'); + expect(getCopyToClipboardValue(0, 'Copy')).toMatchSnapshot('Other: sonar-project.properties'); + expect(getCopyToClipboardValue(1, 'Copy')).toMatchSnapshot('Other: .github/workflows/build.yml'); await user.click(ui.finishTutorialButton.get()); expect(ui.allSetSentence.get()).toBeInTheDocument(); diff --git a/server/sonar-web/src/main/js/components/tutorials/components/AllSet.tsx b/server/sonar-web/src/main/js/components/tutorials/components/AllSet.tsx index afe2f20d884..b3a6b0e2ece 100644 --- a/server/sonar-web/src/main/js/components/tutorials/components/AllSet.tsx +++ b/server/sonar-web/src/main/js/components/tutorials/components/AllSet.tsx @@ -17,15 +17,17 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +import styled from '@emotion/styled'; +import { animated, config, useSpring } from '@react-spring/web'; +import { CheckIcon, FlagVisual, SubTitle } from 'design-system'; import * as React from 'react'; import withAvailableFeatures, { WithAvailableFeaturesProps, } from '../../../app/components/available-features/withAvailableFeatures'; import { translate } from '../../../helpers/l10n'; -import { getBaseUrl } from '../../../helpers/system'; +import useIntersectionObserver from '../../../hooks/useIntersectionObserver'; import { AlmKeys } from '../../../types/alm-settings'; import { Feature } from '../../../types/features'; -import SentenceWithHighlights from './SentenceWithHighlights'; export interface AllSetProps extends WithAvailableFeaturesProps { alm: AlmKeys; @@ -33,65 +35,53 @@ export interface AllSetProps extends WithAvailableFeaturesProps { } export function AllSet(props: AllSetProps) { + const outroRef = React.useRef(null); const { alm, willRefreshAutomatically } = props; const branchSupportEnabled = props.hasFeature(Feature.BranchSupport); + const intersectionEntry = useIntersectionObserver(outroRef, { freezeOnceVisible: true }); + + const outroAnimation = useSpring({ + from: { top: '200px' }, + to: intersectionEntry?.isIntersecting ? { top: '0px' } : { top: '200px' }, + config: config.wobbly, + }); + return ( - <> -
-

- + + + + {translate('onboarding.tutorial.ci_outro.done')} + + +

+ {translate('onboarding.tutorial.ci_outro.refresh_text')}

-
-
- -
-
-

- {translate('onboarding.tutorial.ci_outro.commit')} -

-

- {branchSupportEnabled - ? translate('onboarding.tutorial.ci_outro.commit.why', alm) - : translate('onboarding.tutorial.ci_outro.commit.why.no_branches')} -

-
-
- {willRefreshAutomatically && ( -
-
- -
-
-

- {translate('onboarding.tutorial.ci_outro.refresh')} -

-

{translate('onboarding.tutorial.ci_outro.refresh.why')}

-
-
- )} -
- {willRefreshAutomatically && ( -
- - {translate('onboarding.tutorial.ci_outro.waiting_for_fist_analysis')} -
- )} - +
    +
  • + + {branchSupportEnabled + ? translate('onboarding.tutorial.ci_outro.commit.why', alm) + : translate('onboarding.tutorial.ci_outro.commit.why.no_branches')} +
  • + {willRefreshAutomatically && ( +
  • + + {translate('onboarding.tutorial.ci_outro.refresh.why')} +
  • + )} +
+ + ); } +const MessageContainer = styled.div` + width: 840px; +`; + export default withAvailableFeatures(AllSet); diff --git a/server/sonar-web/src/main/js/components/tutorials/components/CreateYmlFile.tsx b/server/sonar-web/src/main/js/components/tutorials/components/CreateYmlFile.tsx index 26a1368bac7..cad8d25b570 100644 --- a/server/sonar-web/src/main/js/components/tutorials/components/CreateYmlFile.tsx +++ b/server/sonar-web/src/main/js/components/tutorials/components/CreateYmlFile.tsx @@ -17,11 +17,11 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +import { ClipboardIconButton, CodeSnippet, NumberedListItem } from 'design-system'; import * as React from 'react'; import { FormattedMessage } from 'react-intl'; -import { ClipboardIconButton } from '../../../components/controls/clipboard'; import { translate } from '../../../helpers/l10n'; -import CodeSnippet from '../../common/CodeSnippet'; +import { InlineSnippet } from './InlineSnippet'; export interface CreateYmlFileProps { yamlFileName: string; @@ -31,20 +31,20 @@ export interface CreateYmlFileProps { export default function CreateYmlFile(props: CreateYmlFileProps) { const { yamlTemplate, yamlFileName } = props; return ( -
  • + - {yamlFileName} + ), }} /> - -
  • + + ); } diff --git a/server/sonar-web/src/main/js/components/tutorials/components/DefaultProjectKey.tsx b/server/sonar-web/src/main/js/components/tutorials/components/DefaultProjectKey.tsx index 5112acc8ced..5833fdfd374 100644 --- a/server/sonar-web/src/main/js/components/tutorials/components/DefaultProjectKey.tsx +++ b/server/sonar-web/src/main/js/components/tutorials/components/DefaultProjectKey.tsx @@ -17,9 +17,9 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +import { CodeSnippet, NumberedListItem } from 'design-system'; import * as React from 'react'; import { Component } from '../../../types/types'; -import CodeSnippet from '../../common/CodeSnippet'; import SentenceWithFilename from './SentenceWithFilename'; export interface DefaultProjectKeyProps { @@ -31,12 +31,15 @@ const sonarProjectSnippet = (key: string) => `sonar.projectKey=${key}`; export default function DefaultProjectKey(props: DefaultProjectKeyProps) { const { component } = props; return ( -
  • + - -
  • + + ); } diff --git a/server/sonar-web/src/main/js/components/tutorials/components/GithubCFamilyExampleRepositories.tsx b/server/sonar-web/src/main/js/components/tutorials/components/GithubCFamilyExampleRepositories.tsx index 99d924eb5a3..332ba0ff603 100644 --- a/server/sonar-web/src/main/js/components/tutorials/components/GithubCFamilyExampleRepositories.tsx +++ b/server/sonar-web/src/main/js/components/tutorials/components/GithubCFamilyExampleRepositories.tsx @@ -18,10 +18,10 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import classNames from 'classnames'; +import { Card, LightLabel, StandoutLink } from 'design-system'; import React from 'react'; import { translate } from '../../../helpers/l10n'; import { getBaseUrl } from '../../../helpers/system'; -import Link from '../../common/Link'; import { OSs, TutorialModes } from '../types'; import './GithubCFamilyExampleRepositories.css'; @@ -57,26 +57,21 @@ export default function GithubCFamilyExampleRepositories( const link = `https://github.com/orgs/sonarsource-cfamily-examples/repositories?q=${queryParams}`; return ( -
    -
    + +
    - + sonarsource-cfamily-examples - +
    -

    + {translate('onboarding.tutorial.cfamily.examples_repositories_description')} -

    -
    + + ); } diff --git a/server/sonar-web/src/main/js/components/tutorials/components/GradleBuild.tsx b/server/sonar-web/src/main/js/components/tutorials/components/GradleBuild.tsx index 895aadb09b1..8087bdb5b04 100644 --- a/server/sonar-web/src/main/js/components/tutorials/components/GradleBuild.tsx +++ b/server/sonar-web/src/main/js/components/tutorials/components/GradleBuild.tsx @@ -17,15 +17,15 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +import { ClipboardIconButton, CodeSnippet, NumberedListItem } from 'design-system/lib'; import React from 'react'; import { FormattedMessage } from 'react-intl'; import { translate } from '../../../helpers/l10n'; import { Component } from '../../../types/types'; -import CodeSnippet from '../../common/CodeSnippet'; -import { ClipboardIconButton } from '../../controls/clipboard'; import { GradleBuildDSL } from '../types'; import { buildGradleSnippet } from '../utils'; import GradleBuildSelection from './GradleBuildSelection'; +import { InlineSnippet } from './InlineSnippet'; interface Props { component: Component; @@ -33,31 +33,35 @@ interface Props { export default function GradleBuild({ component }: Props) { return ( -
  • + - {GradleBuildDSL.Groovy} + ), kotlin: ( <> - {GradleBuildDSL.Kotlin} + ), - sq: org.sonarqube, + sq: , }} /> - + {(build) => ( - + )} -
  • + ); } diff --git a/server/sonar-web/src/main/js/components/tutorials/components/InlineSnippet.tsx b/server/sonar-web/src/main/js/components/tutorials/components/InlineSnippet.tsx index d84392f84cc..18f56f690d8 100644 --- a/server/sonar-web/src/main/js/components/tutorials/components/InlineSnippet.tsx +++ b/server/sonar-web/src/main/js/components/tutorials/components/InlineSnippet.tsx @@ -17,12 +17,21 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +import classNames from 'classnames'; import { CodeSnippet } from 'design-system'; import * as React from 'react'; import { FCProps } from '../../../types/misc'; -export function InlineSnippet({ snippet }: Pick, 'snippet'>) { +export function InlineSnippet({ + snippet, + className, +}: Pick, 'snippet'> & { className?: string }) { return ( - + ); } diff --git a/server/sonar-web/src/main/js/components/tutorials/components/RenderOptions.tsx b/server/sonar-web/src/main/js/components/tutorials/components/RenderOptions.tsx index 301008e4cbb..4a138015375 100644 --- a/server/sonar-web/src/main/js/components/tutorials/components/RenderOptions.tsx +++ b/server/sonar-web/src/main/js/components/tutorials/components/RenderOptions.tsx @@ -28,23 +28,32 @@ export interface RenderOptionsProps { optionLabelKey: string; options: string[]; titleLabelKey?: string; + setDone?: (doneStatus: boolean) => void; } export default function RenderOptions({ checked, label, onCheck, + setDone, optionLabelKey, options, titleLabelKey, }: RenderOptionsProps) { + const onChange = (checked: string) => { + if (setDone) { + setDone(true); + } + onCheck(checked); + }; + return (
    {titleLabelKey && } ({ label: translate(optionLabelKey, build), value: build, diff --git a/server/sonar-web/src/main/js/components/tutorials/components/SentenceWithFilename.tsx b/server/sonar-web/src/main/js/components/tutorials/components/SentenceWithFilename.tsx index eacfc511ef2..94aa6f25a07 100644 --- a/server/sonar-web/src/main/js/components/tutorials/components/SentenceWithFilename.tsx +++ b/server/sonar-web/src/main/js/components/tutorials/components/SentenceWithFilename.tsx @@ -20,6 +20,7 @@ import * as React from 'react'; import { FormattedMessage } from 'react-intl'; import { translate } from '../../../helpers/l10n'; +import { InlineSnippet } from './InlineSnippet'; export interface SentenceWithFilenameProps { filename: string; @@ -36,7 +37,7 @@ export default function SentenceWithFilename({ defaultMessage={translate(translationKey, 'sentence')} id={`${translationKey}.sentence`} values={{ - file: {filename}, + file: , }} /> diff --git a/server/sonar-web/src/main/js/components/tutorials/components/TokenStepGenerator.tsx b/server/sonar-web/src/main/js/components/tutorials/components/TokenStepGenerator.tsx index d44606562cd..e43cb39b1c9 100644 --- a/server/sonar-web/src/main/js/components/tutorials/components/TokenStepGenerator.tsx +++ b/server/sonar-web/src/main/js/components/tutorials/components/TokenStepGenerator.tsx @@ -17,9 +17,9 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +import { ButtonSecondary, NumberedListItem } from 'design-system'; import * as React from 'react'; import { FormattedMessage } from 'react-intl'; -import { Button } from '../../../components/controls/buttons'; import { translate } from '../../../helpers/l10n'; import { Component } from '../../../types/types'; import { LoggedInUser } from '../../../types/users'; @@ -39,21 +39,25 @@ export default function TokenStepGenerator(props: TokenStepGeneratorProps) { return ( <> -
  • + + {translate('onboarding.token.generate.long')} - + + ), + field: ( + + {translate('onboarding.tutorial.env_variables.field')} + ), - field: {translate('onboarding.tutorial.env_variables.field')}, value: translate('onboarding.tutorial.env_variables.token_generator.value'), }} /> -
  • + {isModalVisible && ( )} diff --git a/server/sonar-web/src/main/js/components/tutorials/components/YamlFileStep.tsx b/server/sonar-web/src/main/js/components/tutorials/components/YamlFileStep.tsx index e451af41491..b968dd2d3f6 100644 --- a/server/sonar-web/src/main/js/components/tutorials/components/YamlFileStep.tsx +++ b/server/sonar-web/src/main/js/components/tutorials/components/YamlFileStep.tsx @@ -17,6 +17,7 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +import { NumberedList, NumberedListItem } from 'design-system'; import * as React from 'react'; import { translate } from '../../../helpers/l10n'; import { withCLanguageFeature } from '../../hoc/withCLanguageFeature'; @@ -26,6 +27,7 @@ import { BuildTools } from '../types'; export interface YamlFileStepProps { children?: (buildTool: BuildTools) => React.ReactElement<{}>; hasCLanguageFeature: boolean; + setDone?: (doneStatus: boolean) => void; } export function YamlFileStep(props: YamlFileStepProps) { @@ -40,8 +42,8 @@ export function YamlFileStep(props: YamlFileStepProps) { const [buildToolSelected, setBuildToolSelected] = React.useState(); return ( -
      -
    1. + + {translate('onboarding.build')} setBuildToolSelected(value as BuildTools)} options={buildTools} optionLabelKey="onboarding.build" + setDone={props.setDone} /> -
    2. + {children && buildToolSelected && children(buildToolSelected)} -
    + ); } diff --git a/server/sonar-web/src/main/js/components/tutorials/github-action/AnalysisCommand.tsx b/server/sonar-web/src/main/js/components/tutorials/github-action/AnalysisCommand.tsx index 6204a994d68..89ac0a07d6c 100644 --- a/server/sonar-web/src/main/js/components/tutorials/github-action/AnalysisCommand.tsx +++ b/server/sonar-web/src/main/js/components/tutorials/github-action/AnalysisCommand.tsx @@ -34,7 +34,6 @@ export interface AnalysisCommandProps extends WithAvailableFeaturesProps { buildTool: BuildTools; mainBranchName: string; component: Component; - onDone: () => void; } export function AnalysisCommand(props: AnalysisCommandProps) { @@ -48,7 +47,6 @@ export function AnalysisCommand(props: AnalysisCommandProps) { branchesEnabled={branchSupportEnabled} mainBranchName={mainBranchName} component={component} - onDone={props.onDone} /> ); case BuildTools.Gradle: @@ -57,7 +55,6 @@ export function AnalysisCommand(props: AnalysisCommandProps) { branchesEnabled={branchSupportEnabled} mainBranchName={mainBranchName} component={component} - onDone={props.onDone} /> ); case BuildTools.DotNet: @@ -66,7 +63,6 @@ export function AnalysisCommand(props: AnalysisCommandProps) { branchesEnabled={branchSupportEnabled} mainBranchName={mainBranchName} component={component} - onDone={props.onDone} /> ); case BuildTools.CFamily: @@ -75,7 +71,6 @@ export function AnalysisCommand(props: AnalysisCommandProps) { branchesEnabled={branchSupportEnabled} mainBranchName={mainBranchName} component={component} - onDone={props.onDone} /> ); case BuildTools.Other: @@ -84,7 +79,6 @@ export function AnalysisCommand(props: AnalysisCommandProps) { branchesEnabled={branchSupportEnabled} mainBranchName={mainBranchName} component={component} - onDone={props.onDone} /> ); } diff --git a/server/sonar-web/src/main/js/components/tutorials/github-action/GitHubActionTutorial.tsx b/server/sonar-web/src/main/js/components/tutorials/github-action/GitHubActionTutorial.tsx index 1315f062729..0f0b134674e 100644 --- a/server/sonar-web/src/main/js/components/tutorials/github-action/GitHubActionTutorial.tsx +++ b/server/sonar-web/src/main/js/components/tutorials/github-action/GitHubActionTutorial.tsx @@ -17,6 +17,7 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +import { BasicSeparator, TutorialStep, TutorialStepList } from 'design-system'; import * as React from 'react'; import { translate } from '../../../helpers/l10n'; import { @@ -26,18 +27,11 @@ import { } from '../../../types/alm-settings'; import { Component } from '../../../types/types'; import { LoggedInUser } from '../../../types/users'; -import AllSetStep from '../components/AllSetStep'; -import Step from '../components/Step'; +import AllSet from '../components/AllSet'; import YamlFileStep from '../components/YamlFileStep'; import AnalysisCommand from './AnalysisCommand'; import SecretStep from './SecretStep'; -export enum Steps { - CREATE_SECRET = 1, - YAML = 2, - ALL_SET = 3, -} - export interface GitHubActionTutorialProps { almBinding?: AlmSettingsInstance; baseUrl: string; @@ -49,6 +43,7 @@ export interface GitHubActionTutorialProps { } export default function GitHubActionTutorial(props: GitHubActionTutorialProps) { + const [done, setDone] = React.useState(false); const { almBinding, baseUrl, @@ -58,52 +53,37 @@ export default function GitHubActionTutorial(props: GitHubActionTutorialProps) { mainBranchName, willRefreshAutomatically, } = props; - - const [step, setStep] = React.useState(Steps.CREATE_SECRET); return ( - <> - Steps.CREATE_SECRET} - onOpen={() => setStep(Steps.CREATE_SECRET)} - open={step === Steps.CREATE_SECRET} - renderForm={() => ( - setStep(Steps.YAML)} + + + + + + + {(buildTool) => ( + + )} + + + {done && ( + <> + + - )} - stepNumber={Steps.CREATE_SECRET} - stepTitle={translate('onboarding.tutorial.with.github_action.create_secret.title')} - /> - Steps.YAML} - onOpen={() => setStep(Steps.YAML)} - open={step === Steps.YAML} - renderForm={() => ( - - {(buildTool) => ( - setStep(Steps.ALL_SET)} - /> - )} - - )} - stepNumber={Steps.YAML} - stepTitle={translate('onboarding.tutorial.with.github_action.yaml.title')} - /> - - + + )} + ); } diff --git a/server/sonar-web/src/main/js/components/tutorials/github-action/SecretStep.tsx b/server/sonar-web/src/main/js/components/tutorials/github-action/SecretStep.tsx index 2e137e2353b..a86d562f45e 100644 --- a/server/sonar-web/src/main/js/components/tutorials/github-action/SecretStep.tsx +++ b/server/sonar-web/src/main/js/components/tutorials/github-action/SecretStep.tsx @@ -17,14 +17,20 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +import { + BasicSeparator, + ClipboardIconButton, + NumberedList, + NumberedListItem, + StandoutLink, +} from 'design-system'; import * as React from 'react'; import { FormattedMessage } from 'react-intl'; -import { Button } from '../../../components/controls/buttons'; -import { ClipboardIconButton } from '../../../components/controls/clipboard'; import { translate } from '../../../helpers/l10n'; import { AlmSettingsInstance, ProjectAlmBindingResponse } from '../../../types/alm-settings'; import { Component } from '../../../types/types'; import { LoggedInUser } from '../../../types/users'; +import { InlineSnippet } from '../components/InlineSnippet'; import SentenceWithHighlights from '../components/SentenceWithHighlights'; import TokenStepGenerator from '../components/TokenStepGenerator'; import { buildGithubLink } from '../utils'; @@ -35,97 +41,94 @@ export interface SecretStepProps { component: Component; currentUser: LoggedInUser; projectBinding?: ProjectAlmBindingResponse; - onDone: () => void; } export default function SecretStep(props: SecretStepProps) { const { almBinding, baseUrl, component, currentUser, projectBinding } = props; return ( -
    -

    - - {translate('onboarding.tutorial.with.github_action.secret.intro.link')} - - ) : ( - - {translate('onboarding.tutorial.with.github_action.secret.intro.link')} - - ), - }} - /> -

    -
      -
    1. + <> + + {translate('onboarding.tutorial.with.github_action.secret.intro.link')} + + ) : ( + + {translate('onboarding.tutorial.with.github_action.secret.intro.link')} + + ), + }} + /> + + -
    2. -
    3. + + - SONAR_TOKEN + -
    4. + -
    5. + -
    6. -
    - -
    - -
      -
    1. + + + + + -
    2. -
    3. + + - - SONAR_HOST_URL + -
    4. -
    5. + + , - field: {translate('onboarding.tutorial.env_variables.field')}, - value: {baseUrl}, + field: ( + + {translate('onboarding.tutorial.env_variables.field')} + + ), + value: , }} /> -
    6. -
    7. + + -
    8. -
    - -
    + + + ); } diff --git a/server/sonar-web/src/main/js/components/tutorials/github-action/__tests__/GithubActionTutorial-it.tsx b/server/sonar-web/src/main/js/components/tutorials/github-action/__tests__/GithubActionTutorial-it.tsx index 06ab3f48ec5..16c3e2e9c87 100644 --- a/server/sonar-web/src/main/js/components/tutorials/github-action/__tests__/GithubActionTutorial-it.tsx +++ b/server/sonar-web/src/main/js/components/tutorials/github-action/__tests__/GithubActionTutorial-it.tsx @@ -28,7 +28,7 @@ import { } from '../../../../helpers/mocks/alm-settings'; import { mockComponent } from '../../../../helpers/mocks/component'; import { mockLanguage, mockLoggedInUser } from '../../../../helpers/testMocks'; -import { renderApp, RenderContext } from '../../../../helpers/testReactTestingUtils'; +import { RenderContext, renderApp } from '../../../../helpers/testReactTestingUtils'; import { AlmKeys } from '../../../../types/alm-settings'; import { Feature } from '../../../../types/features'; import { @@ -65,45 +65,49 @@ it('should follow and complete all steps', async () => { expect(await ui.secretsStepTitle.find()).toBeInTheDocument(); // Env variables step - expect(getCopyToClipboardValue()).toMatchSnapshot('sonar token key'); - expect(getCopyToClipboardValue(1)).toMatchSnapshot('sonarqube host url key'); - expect(getCopyToClipboardValue(2)).toMatchSnapshot('sonarqube host url value'); - await user.click(ui.continueButton.get()); + expect(getCopyToClipboardValue(0, 'Copy to clipboard')).toMatchSnapshot('sonar token key'); + expect(getCopyToClipboardValue(1, 'Copy to clipboard')).toMatchSnapshot('sonarqube host url key'); + expect(getCopyToClipboardValue(2, 'Copy to clipboard')).toMatchSnapshot( + 'sonarqube host url value' + ); // Create/update configuration file step // Maven await user.click(ui.mavenBuildButton.get()); - expect(getCopyToClipboardValue(1)).toMatchSnapshot('Maven: .github/workflows/build.yml'); + expect(getCopyToClipboardValue(0, 'Copy')).toMatchSnapshot('Maven: .github/workflows/build.yml'); // Gradle await user.click(ui.gradleBuildButton.get()); - expect(getCopyToClipboardValue(2)).toMatchSnapshot('Groovy: build.gradle'); + expect(getCopyToClipboardValue(0, 'Copy')).toMatchSnapshot('Groovy: build.gradle'); await user.click(ui.gradleDSLButton(GradleBuildDSL.Kotlin).get()); - expect(getCopyToClipboardValue(2)).toMatchSnapshot('Kotlin: build.gradle.kts'); - expect(getCopyToClipboardValue(4)).toMatchSnapshot('Gradle: .github/workflows/build.yml'); + expect(getCopyToClipboardValue(0, 'Copy')).toMatchSnapshot('Kotlin: build.gradle.kts'); + expect(getCopyToClipboardValue(1, 'Copy')).toMatchSnapshot('Gradle: .github/workflows/build.yml'); // .NET await user.click(ui.dotnetBuildButton.get()); - expect(getCopyToClipboardValue(1)).toMatchSnapshot('.NET: .github/workflows/build.yml'); + expect(getCopyToClipboardValue(0, 'Copy')).toMatchSnapshot('.NET: .github/workflows/build.yml'); // CFamily await user.click(ui.cFamilyBuildButton.get()); - expect(getCopyToClipboardValue()).toMatchSnapshot('CFamily: sonar-project.properties'); + expect(getCopyToClipboardValue(0, 'Copy')).toMatchSnapshot('CFamily: sonar-project.properties'); await user.click(ui.linuxButton.get()); - expect(getCopyToClipboardValue(2)).toMatchSnapshot('CFamily Linux: .github/workflows/build.yml'); + expect(getCopyToClipboardValue(1, 'Copy')).toMatchSnapshot( + 'CFamily Linux: .github/workflows/build.yml' + ); await user.click(ui.windowsButton.get()); - expect(getCopyToClipboardValue(2)).toMatchSnapshot( + expect(getCopyToClipboardValue(1, 'Copy')).toMatchSnapshot( 'CFamily Windows: .github/workflows/build.yml' ); await user.click(ui.macosButton.get()); - expect(getCopyToClipboardValue(2)).toMatchSnapshot('CFamily MacOS: .github/workflows/build.yml'); + expect(getCopyToClipboardValue(1, 'Copy')).toMatchSnapshot( + 'CFamily MacOS: .github/workflows/build.yml' + ); // Other await user.click(ui.otherBuildButton.get()); - expect(getCopyToClipboardValue()).toMatchSnapshot('Other: sonar-project.properties'); - expect(getCopyToClipboardValue(2)).toMatchSnapshot('Other: .github/workflows/build.yml'); + expect(getCopyToClipboardValue(0, 'Copy')).toMatchSnapshot('Other: sonar-project.properties'); + expect(getCopyToClipboardValue(1, 'Copy')).toMatchSnapshot('Other: .github/workflows/build.yml'); - await user.click(ui.finishTutorialButton.get()); expect(ui.allSetSentence.get()).toBeInTheDocument(); }); @@ -116,7 +120,7 @@ it('should generate/delete a new token or use existing one', async () => { // Generate token await user.click(ui.genTokenDialogButton.get()); await user.click(ui.generateTokenButton.get()); - expect(getCopyToClipboardValue(3)).toEqual('generatedtoken2'); + expect(getCopyToClipboardValue()).toEqual('generatedtoken2'); // Revoke current token and create new one await user.click(ui.deleteTokenButton.get()); @@ -124,7 +128,7 @@ it('should generate/delete a new token or use existing one', async () => { await selectEvent.select(ui.expiresInSelect.get(), 'users.tokens.expiration.365'); await user.click(ui.generateTokenButton.get()); expect(ui.tokenValue.get()).toBeInTheDocument(); - await user.click(ui.continueButton.getAll()[1]); + await user.click(ui.continueButton.getAll()[0]); expect(ui.tokenValue.query()).not.toBeInTheDocument(); }); @@ -141,9 +145,7 @@ it('navigates between steps', async () => { // If project is bound, link to repo is visible expect(await ui.linkToRepo.find()).toBeInTheDocument(); - await user.click(await ui.continueButton.find()); await user.click(ui.mavenBuildButton.get()); - await user.click(ui.finishTutorialButton.get()); expect(ui.allSetSentence.get()).toBeInTheDocument(); await user.click(ui.ymlFileStepTitle.get()); diff --git a/server/sonar-web/src/main/js/components/tutorials/github-action/commands/CFamily.tsx b/server/sonar-web/src/main/js/components/tutorials/github-action/commands/CFamily.tsx index 34c99c81052..7aeaf12410a 100644 --- a/server/sonar-web/src/main/js/components/tutorials/github-action/commands/CFamily.tsx +++ b/server/sonar-web/src/main/js/components/tutorials/github-action/commands/CFamily.tsx @@ -17,13 +17,13 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +import { NumberedListItem } from 'design-system'; import * as React from 'react'; import { translate } from '../../../../helpers/l10n'; import { Component } from '../../../../types/types'; import { CompilationInfo } from '../../components/CompilationInfo'; import CreateYmlFile from '../../components/CreateYmlFile'; import DefaultProjectKey from '../../components/DefaultProjectKey'; -import FinishButton from '../../components/FinishButton'; import GithubCFamilyExampleRepositories from '../../components/GithubCFamilyExampleRepositories'; import RenderOptions from '../../components/RenderOptions'; import { OSs, TutorialModes } from '../../types'; @@ -33,7 +33,6 @@ export interface CFamilyProps { branchesEnabled?: boolean; mainBranchName: string; component: Component; - onDone: () => void; } const STEPS = { @@ -118,12 +117,12 @@ const STEPS = { export default function CFamily(props: CFamilyProps) { const { component, branchesEnabled, mainBranchName } = props; - const [os, setOs] = React.useState(); + const [os, setOs] = React.useState(OSs.Linux); return ( <> -
  • + {translate('onboarding.build.other.os')} {os && ( )} -
  • + {os && ( <> - - + )} diff --git a/server/sonar-web/src/main/js/components/tutorials/github-action/commands/DotNet.tsx b/server/sonar-web/src/main/js/components/tutorials/github-action/commands/DotNet.tsx index 26580bbfc34..ad306727393 100644 --- a/server/sonar-web/src/main/js/components/tutorials/github-action/commands/DotNet.tsx +++ b/server/sonar-web/src/main/js/components/tutorials/github-action/commands/DotNet.tsx @@ -20,7 +20,6 @@ import * as React from 'react'; import { Component } from '../../../../types/types'; import CreateYmlFile from '../../components/CreateYmlFile'; -import FinishButton from '../../components/FinishButton'; import { GITHUB_ACTIONS_RUNS_ON_WINDOWS } from '../constants'; import { generateGitHubActionsYaml } from '../utils'; @@ -28,7 +27,6 @@ export interface DotNetProps { branchesEnabled?: boolean; mainBranchName: string; component: Component; - onDone: () => void; } function dotnetYamlSteps(projectKey: string) { @@ -69,17 +67,14 @@ function dotnetYamlSteps(projectKey: string) { export default function DotNet(props: DotNetProps) { const { component, branchesEnabled, mainBranchName } = props; return ( - <> - - - + ); } diff --git a/server/sonar-web/src/main/js/components/tutorials/github-action/commands/Gradle.tsx b/server/sonar-web/src/main/js/components/tutorials/github-action/commands/Gradle.tsx index c0e84c06cd8..f5ab48b5b1d 100644 --- a/server/sonar-web/src/main/js/components/tutorials/github-action/commands/Gradle.tsx +++ b/server/sonar-web/src/main/js/components/tutorials/github-action/commands/Gradle.tsx @@ -20,7 +20,6 @@ import * as React from 'react'; import { Component } from '../../../../types/types'; import CreateYmlFile from '../../components/CreateYmlFile'; -import FinishButton from '../../components/FinishButton'; import GradleBuild from '../../components/GradleBuild'; import { GITHUB_ACTIONS_RUNS_ON_LINUX } from '../constants'; import { generateGitHubActionsYaml } from '../utils'; @@ -29,7 +28,6 @@ export interface GradleProps { branchesEnabled?: boolean; mainBranchName: string; component: Component; - onDone: () => void; } const GRADLE_YAML_STEPS = ` @@ -71,7 +69,6 @@ export default function Gradle(props: GradleProps) { GRADLE_YAML_STEPS )} /> - ); } diff --git a/server/sonar-web/src/main/js/components/tutorials/github-action/commands/JavaMaven.tsx b/server/sonar-web/src/main/js/components/tutorials/github-action/commands/JavaMaven.tsx index c9b253dd3e8..a619a5eeb87 100644 --- a/server/sonar-web/src/main/js/components/tutorials/github-action/commands/JavaMaven.tsx +++ b/server/sonar-web/src/main/js/components/tutorials/github-action/commands/JavaMaven.tsx @@ -20,7 +20,6 @@ import * as React from 'react'; import { Component } from '../../../../types/types'; import CreateYmlFile from '../../components/CreateYmlFile'; -import FinishButton from '../../components/FinishButton'; import { GITHUB_ACTIONS_RUNS_ON_LINUX } from '../constants'; import { generateGitHubActionsYaml } from '../utils'; @@ -28,7 +27,6 @@ export interface JavaMavenProps { branchesEnabled?: boolean; mainBranchName: string; component: Component; - onDone: () => void; } function mavenYamlSteps(projectKey: string, projectName: string) { @@ -60,17 +58,14 @@ function mavenYamlSteps(projectKey: string, projectName: string) { export default function JavaMaven(props: JavaMavenProps) { const { component, branchesEnabled, mainBranchName } = props; return ( - <> - - - + ); } diff --git a/server/sonar-web/src/main/js/components/tutorials/github-action/commands/Others.tsx b/server/sonar-web/src/main/js/components/tutorials/github-action/commands/Others.tsx index b2a067f0b7c..d3633fce33c 100644 --- a/server/sonar-web/src/main/js/components/tutorials/github-action/commands/Others.tsx +++ b/server/sonar-web/src/main/js/components/tutorials/github-action/commands/Others.tsx @@ -21,7 +21,6 @@ import * as React from 'react'; import { Component } from '../../../../types/types'; import CreateYmlFile from '../../components/CreateYmlFile'; import DefaultProjectKey from '../../components/DefaultProjectKey'; -import FinishButton from '../../components/FinishButton'; import { GITHUB_ACTIONS_RUNS_ON_LINUX } from '../constants'; import { generateGitHubActionsYaml } from '../utils'; @@ -29,7 +28,6 @@ export interface OthersProps { branchesEnabled?: boolean; mainBranchName: string; component: Component; - onDone: () => void; } function otherYamlSteps(branchesEnabled: boolean) { @@ -70,7 +68,6 @@ export default function Others(props: OthersProps) { otherYamlSteps(!!branchesEnabled) )} /> - ); } diff --git a/server/sonar-web/src/main/js/components/tutorials/jenkins/__tests__/JenkinsTutorial-it.tsx b/server/sonar-web/src/main/js/components/tutorials/jenkins/__tests__/JenkinsTutorial-it.tsx index 0031a56f1dd..fd1378270cf 100644 --- a/server/sonar-web/src/main/js/components/tutorials/jenkins/__tests__/JenkinsTutorial-it.tsx +++ b/server/sonar-web/src/main/js/components/tutorials/jenkins/__tests__/JenkinsTutorial-it.tsx @@ -17,7 +17,6 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ - import userEvent from '@testing-library/user-event'; import React from 'react'; import UserTokensMock from '../../../../api/mocks/UserTokensMock'; @@ -80,7 +79,7 @@ const ui = { jenkinsStepTitle: byRole('heading', { name: 'onboarding.tutorial.with.jenkins.jenkinsfile.title', }), - allSetSentence: byText('onboarding.tutorial.ci_outro.all_set.sentence'), + allSetSentence: byText('onboarding.tutorial.ci_outro.done'), ...getTutorialActionButtons(), ...getTutorialBuildButtons(), }; @@ -139,23 +138,23 @@ it.each([AlmKeys.BitbucketCloud, AlmKeys.BitbucketServer, AlmKeys.GitHub, AlmKey // CFamilly await user.click(ui.cFamilyBuildButton.get()); - expect(getCopyToClipboardValue()).toMatchSnapshot(`sonar-project.properties code`); + expect(getCopyToClipboardValue(0, 'Copy')).toMatchSnapshot(`sonar-project.properties code`); await user.click(ui.linuxButton.get()); - expect(getCopyToClipboardValue(1)).toMatchSnapshot(`cfamily linux jenkinsfile`); + expect(getCopyToClipboardValue()).toMatchSnapshot(`cfamily linux jenkinsfile`); await user.click(ui.windowsButton.get()); - expect(getCopyToClipboardValue(1)).toMatchSnapshot(`cfamily windows jenkinsfile`); + expect(getCopyToClipboardValue()).toMatchSnapshot(`cfamily windows jenkinsfile`); await user.click(ui.macosButton.get()); - expect(getCopyToClipboardValue(1)).toMatchSnapshot(`cfamily macos jenkinsfile`); + expect(getCopyToClipboardValue()).toMatchSnapshot(`cfamily macos jenkinsfile`); // Other await user.click(ui.otherBuildButton.get()); - expect(getCopyToClipboardValue()).toMatchSnapshot( + expect(getCopyToClipboardValue(0, 'Copy')).toMatchSnapshot( `other build tools sonar-project.properties code` ); - expect(getCopyToClipboardValue(1)).toMatchSnapshot(`other build tools jenkinsfile`); + expect(getCopyToClipboardValue()).toMatchSnapshot(`other build tools jenkinsfile`); await user.click(ui.finishTutorialButton.get()); expect(ui.allSetSentence.get()).toBeInTheDocument(); diff --git a/server/sonar-web/src/main/js/components/tutorials/test-utils.ts b/server/sonar-web/src/main/js/components/tutorials/test-utils.ts index 760ee7d2d95..58be0817983 100644 --- a/server/sonar-web/src/main/js/components/tutorials/test-utils.ts +++ b/server/sonar-web/src/main/js/components/tutorials/test-utils.ts @@ -52,7 +52,7 @@ export function getCommonNodes(ci: TutorialModes) { ci === TutorialModes.GitHubActions ? 'secret' : 'variables' }.intro.link`, }), - allSetSentence: byText('onboarding.tutorial.ci_outro.all_set.sentence'), + allSetSentence: byText('onboarding.tutorial.ci_outro.done'), }; } diff --git a/server/sonar-web/src/main/js/hooks/useIntersectionObserver.ts b/server/sonar-web/src/main/js/hooks/useIntersectionObserver.ts new file mode 100644 index 00000000000..6da44bf380f --- /dev/null +++ b/server/sonar-web/src/main/js/hooks/useIntersectionObserver.ts @@ -0,0 +1,53 @@ +/* + * SonarQube + * Copyright (C) 2009-2023 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +import { RefObject, useEffect, useState } from 'react'; +import { isDefined } from '../helpers/types'; + +interface Options extends IntersectionObserverInit { + freezeOnceVisible?: boolean; +} + +export default function useIntersectionObserver( + ref: RefObject, + options: Options = {} +) { + const { root = null, rootMargin = '0px', threshold = 0, freezeOnceVisible = false } = options; + const [entry, setEntry] = useState(); + + const frozen = (entry?.isIntersecting || false) && freezeOnceVisible; + + useEffect(() => { + if (!isDefined(IntersectionObserver) || !isDefined(ref.current) || frozen) { + return; + } + + const observer = new IntersectionObserver( + ([entry]) => setEntry(entry), + + { root, rootMargin, threshold } + ); + + observer.observe(ref.current); + return () => observer.disconnect(); + }, [ref, frozen, root, rootMargin, threshold]); + + return entry; +} diff --git a/server/sonar-web/yarn.lock b/server/sonar-web/yarn.lock index 17a4c72095d..909ed7d284f 100644 --- a/server/sonar-web/yarn.lock +++ b/server/sonar-web/yarn.lock @@ -3606,6 +3606,71 @@ __metadata: languageName: node linkType: hard +"@react-spring/animated@npm:~9.7.3": + version: 9.7.3 + resolution: "@react-spring/animated@npm:9.7.3" + dependencies: + "@react-spring/shared": ~9.7.3 + "@react-spring/types": ~9.7.3 + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + checksum: 468942ca3a11c02c3e56def26b2da9dd10ddbed548004245c4ac309cce00b58d971e781abed67db0d652f72737eaa73766ea9a43b8ef3b08a7ed2eddc04d4c39 + languageName: node + linkType: hard + +"@react-spring/core@npm:~9.7.3": + version: 9.7.3 + resolution: "@react-spring/core@npm:9.7.3" + dependencies: + "@react-spring/animated": ~9.7.3 + "@react-spring/shared": ~9.7.3 + "@react-spring/types": ~9.7.3 + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + checksum: 8a80a07276458fd14099320eda824e58a11ce3a9b03a5c9cd3f4252adb4d26da04ee5caf5cbc961199f55c2d58a99638d5ea292cdb6aa029208dbab741b5c531 + languageName: node + linkType: hard + +"@react-spring/rafz@npm:9.7.3": + version: 9.7.3 + resolution: "@react-spring/rafz@npm:9.7.3" + checksum: c88b3c6306eab4a93a9f4ea38ad2e0ea3287a8b79a9a092cb1fd934a97ce092e41c32373b2b2e3de4b63027de7ae7414e0d442936dd8d94b51c2ff7d51e8f643 + languageName: node + linkType: hard + +"@react-spring/shared@npm:~9.7.3": + version: 9.7.3 + resolution: "@react-spring/shared@npm:9.7.3" + dependencies: + "@react-spring/types": ~9.7.3 + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + checksum: 912b5e567eb5345c9a6c8e8c0c2d69b1f411af72a0685b95831809c267c89846a31341ca071f284ace98b3cb5de647054dc76f6ace81d6379513eaf96b52f195 + languageName: node + linkType: hard + +"@react-spring/types@npm:~9.7.3": + version: 9.7.3 + resolution: "@react-spring/types@npm:9.7.3" + checksum: f47b81fe556464aa54a78603311cb584d6a0f03088522229afb058265bbe2ade2095a55ec7f4e960c3b9cceaa5d47865bc41fc6643c0f5f4bd3d8650203d8389 + languageName: node + linkType: hard + +"@react-spring/web@npm:9.7.3": + version: 9.7.3 + resolution: "@react-spring/web@npm:9.7.3" + dependencies: + "@react-spring/animated": ~9.7.3 + "@react-spring/core": ~9.7.3 + "@react-spring/shared": ~9.7.3 + "@react-spring/types": ~9.7.3 + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 + checksum: 7f5cd05b2314b7f2f715e1926abcf9aa0a539399b222ab34e989144f48350adfcd2edab65d41425570f72c57f602fc6994d6730fbeed902171ac527b630a8a9b + languageName: node + linkType: hard + "@remix-run/router@npm:1.6.2": version: 1.6.2 resolution: "@remix-run/router@npm:1.6.2" @@ -4844,6 +4909,8 @@ __metadata: "@emotion/react": 11.11.1 "@emotion/styled": 11.11.0 "@primer/octicons-react": 19.3.0 + "@react-spring/rafz": 9.7.3 + "@react-spring/web": 9.7.3 "@swc/core": 1.3.65 "@swc/jest": 0.2.26 "@tanstack/react-query": 4.29.14 -- cgit v1.2.3