diff options
author | Viktor Vorona <viktor.vorona@sonarsource.com> | 2023-08-03 15:59:01 +0200 |
---|---|---|
committer | sonartech <sonartech@sonarsource.com> | 2023-08-18 20:02:48 +0000 |
commit | 3c949c01785f9edcc81097484ef92ec54922dcd9 (patch) | |
tree | 826bc4d0e9b09c7044cc95ac21dfef73732e6f51 /server | |
parent | a621ae4781fb3692f53f6f14315f142f8d8b705b (diff) | |
download | sonarqube-3c949c01785f9edcc81097484ef92ec54922dcd9.tar.gz sonarqube-3c949c01785f9edcc81097484ef92ec54922dcd9.zip |
SONAR-20023 CCT guide for issues list page
Diffstat (limited to 'server')
14 files changed, 433 insertions, 17 deletions
diff --git a/server/sonar-web/design-system/package.json b/server/sonar-web/design-system/package.json index 52f053c905b..5eced492114 100644 --- a/server/sonar-web/design-system/package.json +++ b/server/sonar-web/design-system/package.json @@ -73,6 +73,7 @@ "react-helmet-async": "1.3.0", "react-highlight-words": "0.20.0", "react-intl": "6.4.4", + "react-joyride": "2.5.5", "react-modal": "3.16.1", "react-router-dom": "6.11.2", "react-select": "5.7.3", diff --git a/server/sonar-web/design-system/src/components/Guide.tsx b/server/sonar-web/design-system/src/components/Guide.tsx new file mode 100644 index 00000000000..6aaaf1596b2 --- /dev/null +++ b/server/sonar-web/design-system/src/components/Guide.tsx @@ -0,0 +1,100 @@ +/* + * 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 styled from '@emotion/styled'; +import { Suspense } from 'react'; +import ReactJoyride, { Props as JoyrideProps, TooltipRenderProps } from 'react-joyride'; +import tw from 'twin.macro'; +import { PopupZLevel } from '../helpers'; +import { Spinner } from './DeferredSpinner'; +import { ButtonPrimary, ButtonSecondary, WrapperButton } from './buttons'; +import { CloseIcon } from './icons'; +import { PopupWrapper } from './popups'; + +type Props = JoyrideProps; + +// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access +(window as any).global = (window as any).global ?? {}; + +const Popup = styled(PopupWrapper)` + position: relative; + width: 300px; + border: none; + ${tw`sw-body-sm`} + ${tw`sw-p-3`} +`; + +function TooltipComponent({ + continuous, + index, + step, + size, + isLastStep, + backProps, + skipProps, + closeProps, + primaryProps, + tooltipProps, +}: TooltipRenderProps) { + return ( + <Popup zLevel={PopupZLevel.Absolute} {...tooltipProps}> + <div className="sw-flex sw-justify-between"> + <b className="sw-mb-2">{step.title}</b> + <WrapperButton + className="sw-w-[30px] sw-h-[30px] sw--mt-2 sw--mr-2 sw-flex sw-justify-center" + {...skipProps} + > + <CloseIcon className="sw-mr-0" /> + </WrapperButton> + </div> + <div>{step.content}</div> + <div className="sw-flex sw-justify-between sw-items-center sw-mt-3"> + <b> + {index + 1} of {size} + </b> + <div> + {index > 0 && ( + <ButtonSecondary className="sw-mr-2" {...backProps}> + {backProps.title} + </ButtonSecondary> + )} + {continuous && !isLastStep && ( + <ButtonPrimary {...primaryProps}>{primaryProps.title}</ButtonPrimary> + )} + {(!continuous || isLastStep) && ( + <ButtonPrimary {...closeProps}>{closeProps.title}</ButtonPrimary> + )} + </div> + </div> + </Popup> + ); +} + +export function Guide(props: Props) { + return ( + <Suspense fallback={<Spinner />}> + <ReactJoyride + scrollDuration={0} + scrollOffset={250} + tooltipComponent={TooltipComponent} + {...props} + /> + </Suspense> + ); +} diff --git a/server/sonar-web/design-system/src/components/buttons/Button.tsx b/server/sonar-web/design-system/src/components/buttons/Button.tsx index 2d2ee37f744..bfcf1788f66 100644 --- a/server/sonar-web/design-system/src/components/buttons/Button.tsx +++ b/server/sonar-web/design-system/src/components/buttons/Button.tsx @@ -39,7 +39,7 @@ export interface ButtonProps extends AllowedButtonAttributes { icon?: React.ReactNode; innerRef?: React.Ref<HTMLButtonElement>; isExternal?: LinkProps['isExternal']; - onClick?: (event?: React.MouseEvent<HTMLButtonElement | HTMLAnchorElement>) => unknown; + onClick?: (event: React.MouseEvent<HTMLButtonElement | HTMLAnchorElement>) => unknown; preventDefault?: boolean; reloadDocument?: LinkProps['reloadDocument']; diff --git a/server/sonar-web/design-system/src/components/index.ts b/server/sonar-web/design-system/src/components/index.ts index 92aa59aca20..b839c227262 100644 --- a/server/sonar-web/design-system/src/components/index.ts +++ b/server/sonar-web/design-system/src/components/index.ts @@ -43,6 +43,7 @@ export * from './FavoriteButton'; export { FlagMessage } from './FlagMessage'; export * from './FlowStep'; export * from './GenericAvatar'; +export * from './Guide'; export * from './HighlightedSection'; export { Histogram } from './Histogram'; export { HotspotRating } from './HotspotRating'; diff --git a/server/sonar-web/design-system/src/components/popups.tsx b/server/sonar-web/design-system/src/components/popups.tsx index a80d9b949ea..d24f691777b 100644 --- a/server/sonar-web/design-system/src/components/popups.tsx +++ b/server/sonar-web/design-system/src/components/popups.tsx @@ -200,7 +200,7 @@ export class Popup extends React.PureComponent<PopupProps, State> { } } -const PopupWrapper = styled.div<{ zLevel: PopupZLevel }>` +export const PopupWrapper = styled.div<{ zLevel: PopupZLevel }>` position: ${({ zLevel }) => (zLevel === PopupZLevel.Global ? 'fixed' : 'absolute')}; background-color: ${themeColor('popup')}; color: ${themeContrast('popup')}; diff --git a/server/sonar-web/package.json b/server/sonar-web/package.json index c7808145a30..2540b442595 100644 --- a/server/sonar-web/package.json +++ b/server/sonar-web/package.json @@ -36,6 +36,7 @@ "react-helmet-async": "1.3.0", "react-highlight-words": "0.20.0", "react-intl": "6.4.4", + "react-joyride": "2.5.5", "react-modal": "3.16.1", "react-router-dom": "6.11.2", "react-select": "5.7.3", diff --git a/server/sonar-web/src/main/js/api/mocks/LocalStorageMock.ts b/server/sonar-web/src/main/js/api/mocks/LocalStorageMock.ts new file mode 100644 index 00000000000..b5d42008f66 --- /dev/null +++ b/server/sonar-web/src/main/js/api/mocks/LocalStorageMock.ts @@ -0,0 +1,51 @@ +/* + * 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. + */ + +export default class LocalStorageMock { + store: Record<string, string>; + + constructor() { + this.store = {}; + } + + clear() { + this.store = {}; + } + + getItem(key: string) { + return this.store[key] || null; + } + + setItem(key: string, value: string) { + this.store[key] = String(value); + } + + removeItem(key: string) { + delete this.store[key]; + } + + get length() { + return Object.keys(this.store).length; + } + + key(index: number) { + return Object.keys(this.store)[index] || null; + } +} diff --git a/server/sonar-web/src/main/js/apps/issues/components/IssueListGuide.tsx b/server/sonar-web/src/main/js/apps/issues/components/IssueListGuide.tsx new file mode 100644 index 00000000000..da88c12e22d --- /dev/null +++ b/server/sonar-web/src/main/js/apps/issues/components/IssueListGuide.tsx @@ -0,0 +1,145 @@ +/* + * 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 { Guide } from 'design-system'; +import React, { useEffect } from 'react'; +import { FormattedMessage } from 'react-intl'; +import { dismissNotice } from '../../../api/users'; +import { CurrentUserContext } from '../../../app/components/current-user/CurrentUserContext'; +import DocLink from '../../../components/common/DocLink'; +import { translate } from '../../../helpers/l10n'; +import { NoticeType } from '../../../types/users'; + +interface Props { + run?: boolean; +} + +export default function IssueListGuide({ run }: Props) { + const { currentUser } = React.useContext(CurrentUserContext); + const hasLocalStorageValue = localStorage.getItem(NoticeType.ISSUE_GUIDE) === 'true'; + + useEffect(() => { + if (!currentUser.dismissedNotices.IssueListGuiding && hasLocalStorageValue) { + dismissNotice(NoticeType.ISSUE_GUIDE); + } + }, [currentUser, hasLocalStorageValue]); + + if (hasLocalStorageValue || currentUser.dismissedNotices.IssueListGuiding) { + return null; + } + + const onToggle = (props: { action: string }) => { + if (props.action === 'reset') { + if (currentUser.isLoggedIn) { + dismissNotice(NoticeType.ISSUE_GUIDE); + } else { + localStorage.setItem(NoticeType.ISSUE_GUIDE, 'true'); + } + } + }; + + const constructContent = ( + first: string, + second: string, + extraContent?: string | React.ReactNode + ) => ( + <> + <span>{translate(first)}</span> + <br /> + <br /> + <span>{translate(second)}</span> + {extraContent ?? null} + </> + ); + + const steps = [ + { + target: '[data-guiding-id="issuelist-1"]', + content: constructContent('guiding.issue_list.1.content.1', 'guiding.issue_list.1.content.2'), + title: translate('guiding.issue_list.1.title'), + placement: 'right' as const, + disableBeacon: true, + floaterProps: { + disableAnimation: true, + }, + }, + { + target: '[data-guiding-id="issuelist-2"]', + content: constructContent('guiding.issue_list.2.content.1', 'guiding.issue_list.2.content.2'), + title: translate('guiding.issue_list.2.title'), + placement: 'right' as const, + disableBeacon: true, + floaterProps: { + disableAnimation: true, + }, + }, + { + target: '[data-guiding-id="issuelist-3"]', + content: constructContent('guiding.issue_list.2.content.1', 'guiding.issue_list.2.content.2'), + title: translate('guiding.issue_list.3.title'), + placement: 'right' as const, + disableBeacon: true, + floaterProps: { + disableAnimation: true, + }, + }, + { + target: '[data-guiding-id="issuelist-4"]', + content: constructContent( + 'guiding.issue_list.4.content.1', + 'guiding.issue_list.4.content.2', + <ul className="sw-mt-2 sw-pl-5 sw-list-disc"> + <li>{translate('guiding.issue_list.4.content.list.1')}</li> + <li>{translate('guiding.issue_list.4.content.list.2')}</li> + <li>{translate('guiding.issue_list.4.content.list.3')}</li> + </ul> + ), + title: translate('guiding.issue_list.4.title'), + disableScrolling: true, + disableBeacon: true, + floaterProps: { + disableAnimation: true, + }, + }, + { + target: 'body', + content: ( + <FormattedMessage + id="guiding.issue_list.5.content" + defaultMessage={translate('guiding.issue_list.5.content')} + values={{ + link: ( + <DocLink to="/user-guide/clean-code" className="sw-capitalize"> + {translate('documentation')} + </DocLink> + ), + }} + /> + ), + title: translate('guiding.issue_list.5.title'), + placement: 'center' as const, + disableBeacon: true, + floaterProps: { + disableAnimation: true, + }, + }, + ]; + + return <Guide callback={onToggle} steps={steps} run={run} continuous />; +} diff --git a/server/sonar-web/src/main/js/apps/issues/components/IssuesApp.tsx b/server/sonar-web/src/main/js/apps/issues/components/IssuesApp.tsx index f9bf57cbff1..1975ae716da 100644 --- a/server/sonar-web/src/main/js/apps/issues/components/IssuesApp.tsx +++ b/server/sonar-web/src/main/js/apps/issues/components/IssuesApp.tsx @@ -100,6 +100,7 @@ import { shouldOpenStandardsFacet, } from '../utils'; import BulkChangeModal, { MAX_PAGE_SIZE } from './BulkChangeModal'; +import IssueListGuide from './IssueListGuide'; import IssueReviewHistoryAndComments from './IssueReviewHistoryAndComments'; import IssuesList from './IssuesList'; import IssuesSourceViewer from './IssuesSourceViewer'; @@ -1303,6 +1304,8 @@ export class App extends React.PureComponent<Props, State> { render() { const { openIssue } = this.state; + const { component, location } = this.props; + const open = getOpen(location.query); return ( <PageWrapperStyle id="issues-page"> @@ -1310,6 +1313,7 @@ export class App extends React.PureComponent<Props, State> { <PageContentFontWrapper className="sw-body-sm"> <div className="sw-w-full sw-flex" id="issues-page"> <Suggestions suggestions="issues" /> + <IssueListGuide run={!open && !component?.needIssueSync} /> {openIssue ? ( <Helmet diff --git a/server/sonar-web/src/main/js/apps/issues/sidebar/AttributeCategoryFacet.tsx b/server/sonar-web/src/main/js/apps/issues/sidebar/AttributeCategoryFacet.tsx index 4797ecb36ad..48ed584f7c6 100644 --- a/server/sonar-web/src/main/js/apps/issues/sidebar/AttributeCategoryFacet.tsx +++ b/server/sonar-web/src/main/js/apps/issues/sidebar/AttributeCategoryFacet.tsx @@ -32,12 +32,14 @@ export function AttributeCategoryFacet(props: Props) { const { categories = [], ...rest } = props; return ( - <SimpleListStyleFacet - property="cleanCodeAttributeCategory" - itemNamePrefix="issue.clean_code_attribute_category" - listItems={CATEGORIES} - selectedItems={categories} - {...rest} - /> + <div data-guiding-id="issuelist-1"> + <SimpleListStyleFacet + property="cleanCodeAttributeCategory" + itemNamePrefix="issue.clean_code_attribute_category" + listItems={CATEGORIES} + selectedItems={categories} + {...rest} + /> + </div> ); } diff --git a/server/sonar-web/src/main/js/apps/issues/sidebar/SoftwareQualityFacet.tsx b/server/sonar-web/src/main/js/apps/issues/sidebar/SoftwareQualityFacet.tsx index 2851cc06a60..d0dec615114 100644 --- a/server/sonar-web/src/main/js/apps/issues/sidebar/SoftwareQualityFacet.tsx +++ b/server/sonar-web/src/main/js/apps/issues/sidebar/SoftwareQualityFacet.tsx @@ -32,12 +32,14 @@ export function SoftwareQualityFacet(props: Props) { const { qualities = [], ...rest } = props; return ( - <SimpleListStyleFacet - property="impactSoftwareQuality" - itemNamePrefix="issue.software_quality" - listItems={QUALITIES} - selectedItems={qualities} - {...rest} - /> + <div data-guiding-id="issuelist-2"> + <SimpleListStyleFacet + property="impactSoftwareQuality" + itemNamePrefix="issue.software_quality" + listItems={QUALITIES} + selectedItems={qualities} + {...rest} + /> + </div> ); } diff --git a/server/sonar-web/src/main/js/apps/issues/test-utils.tsx b/server/sonar-web/src/main/js/apps/issues/test-utils.tsx index fc243e293f1..42af36dee89 100644 --- a/server/sonar-web/src/main/js/apps/issues/test-utils.tsx +++ b/server/sonar-web/src/main/js/apps/issues/test-utils.tsx @@ -127,6 +127,8 @@ export const ui = { ruleFacetList: byRole('list', { name: 'issues.facet.rules' }), ruleFacetSearch: byPlaceholderText('search.search_for_rules'), tagFacetSearch: byPlaceholderText('search.search_for_tags'), + + guidePopup: byRole('alertdialog'), }; export async function waitOnDataLoaded() { diff --git a/server/sonar-web/src/main/js/types/users.ts b/server/sonar-web/src/main/js/types/users.ts index ab2421f700e..d7acd57f448 100644 --- a/server/sonar-web/src/main/js/types/users.ts +++ b/server/sonar-web/src/main/js/types/users.ts @@ -32,6 +32,7 @@ export interface Notice { export enum NoticeType { EDUCATION_PRINCIPLES = 'educationPrinciples', SONARLINT_AD = 'sonarlintAd', + ISSUE_GUIDE = 'issueCleanCodeGuide', } export interface LoggedInUser extends CurrentUser, UserActive { diff --git a/server/sonar-web/yarn.lock b/server/sonar-web/yarn.lock index 50ecf6de2b4..bd24a6320eb 100644 --- a/server/sonar-web/yarn.lock +++ b/server/sonar-web/yarn.lock @@ -3074,6 +3074,13 @@ __metadata: languageName: node linkType: hard +"@gilbarbara/deep-equal@npm:^0.1.1": + version: 0.1.2 + resolution: "@gilbarbara/deep-equal@npm:0.1.2" + checksum: 78d4e76d36cbee639c008a63be52c1ac803212ff2560e55f68d2b8b2a6ac5e746c1976854cf101483ca18a9911aed2349da147b7756be43e75efb95e3f24468b + languageName: node + linkType: hard + "@humanwhocodes/config-array@npm:^0.11.10": version: 0.11.10 resolution: "@humanwhocodes/config-array@npm:0.11.10" @@ -4995,6 +5002,7 @@ __metadata: react-helmet-async: 1.3.0 react-highlight-words: 0.20.0 react-intl: 6.4.4 + react-joyride: 2.5.5 react-modal: 3.16.1 react-router-dom: 6.11.2 react-select: 5.7.3 @@ -6499,6 +6507,13 @@ __metadata: languageName: node linkType: hard +"deepmerge@npm:^4.3.1": + version: 4.3.1 + resolution: "deepmerge@npm:4.3.1" + checksum: 2024c6a980a1b7128084170c4cf56b0fd58a63f2da1660dcfe977415f27b17dbe5888668b59d0b063753f3220719d5e400b7f113609489c90160bb9a5518d052 + languageName: node + linkType: hard + "define-properties@npm:^1.1.2, define-properties@npm:^1.1.3": version: 1.1.3 resolution: "define-properties@npm:1.1.3" @@ -6608,6 +6623,7 @@ __metadata: react-helmet-async: 1.3.0 react-highlight-words: 0.20.0 react-intl: 6.4.4 + react-joyride: 2.5.5 react-modal: 3.16.1 react-router-dom: 6.11.2 react-select: 5.7.3 @@ -7707,7 +7723,7 @@ __metadata: languageName: node linkType: hard -"exenv@npm:^1.2.0": +"exenv@npm:^1.2.0, exenv@npm:^1.2.2": version: 1.2.2 resolution: "exenv@npm:1.2.2" checksum: a894f3b60ab8419e0b6eec99c690a009c8276b4c90655ccaf7d28faba2de3a6b93b3d92210f9dc5efd36058d44f04098f6bbccef99859151104bfd49939904dc @@ -8875,6 +8891,20 @@ __metadata: languageName: node linkType: hard +"is-lite@npm:^0.8.2": + version: 0.8.2 + resolution: "is-lite@npm:0.8.2" + checksum: 0ee62cb238c2a044f58d1cd139fb0b48026c407ec8625ee6572b417f164e17ec937f0a0785f466e320749a796c316a3b78dcb4b520f7ddd4b9de38ad5a23d70f + languageName: node + linkType: hard + +"is-lite@npm:^0.9.2": + version: 0.9.2 + resolution: "is-lite@npm:0.9.2" + checksum: 8c4d2c58cf99a8289715925c0c3175dadf63e5ad293ad395ce650430ce90afe533a84ad0ffdeed0ce277dabdae63acdd3dac5d9b629bd61c3c0c620e2376f26e + languageName: node + linkType: hard + "is-map@npm:^2.0.1, is-map@npm:^2.0.2": version: 2.0.2 resolution: "is-map@npm:2.0.2" @@ -11050,6 +11080,13 @@ __metadata: languageName: node linkType: hard +"popper.js@npm:^1.16.0": + version: 1.16.1 + resolution: "popper.js@npm:1.16.1" + checksum: c56ae5001ec50a77ee297a8061a0221d99d25c7348d2e6bcd3e45a0d0f32a1fd81bca29d46cb0d4bdf13efb77685bd6a0ce93f9eb3c608311a461f945fffedbe + languageName: node + linkType: hard + "postcss-calc@npm:9.0.1": version: 9.0.1 resolution: "postcss-calc@npm:9.0.1" @@ -11388,6 +11425,24 @@ __metadata: languageName: node linkType: hard +"react-floater@npm:^0.7.6": + version: 0.7.6 + resolution: "react-floater@npm:0.7.6" + 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 + peerDependencies: + react: 15 - 18 + react-dom: 15 - 18 + checksum: 8268e14fbdf9393b39300f3c90ea2de382782f1d959176579e30841095a73f9240ec05fce3ec89e8b4e58cbc46c9b043b2ad0b338f5c5d3b91168b16a4282ac1 + languageName: node + linkType: hard + "react-helmet-async@npm:1.3.0": version: 1.3.0 resolution: "react-helmet-async@npm:1.3.0" @@ -11476,6 +11531,26 @@ __metadata: languageName: node linkType: hard +"react-joyride@npm:2.5.5": + version: 2.5.5 + resolution: "react-joyride@npm:2.5.5" + dependencies: + deepmerge: ^4.3.1 + 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.1.0 + tree-changes: ^0.9.2 + peerDependencies: + react: 15 - 18 + react-dom: 15 - 18 + checksum: ca4a18631e12638e10f7ebb063880f66a984a4edc0fe81f87649ac1ec32c3790a03c6cbe10f189c029d64af52eef4173042c676024987accb0aeaa4e1216bb04 + languageName: node + linkType: hard + "react-lifecycles-compat@npm:^3.0.0, react-lifecycles-compat@npm:^3.0.4": version: 3.0.4 resolution: "react-lifecycles-compat@npm:3.0.4" @@ -11498,6 +11573,13 @@ __metadata: languageName: node linkType: hard +"react-proptype-conditional-require@npm:^1.0.4": + version: 1.0.4 + resolution: "react-proptype-conditional-require@npm:1.0.4" + checksum: 78f82d15b2c77c14fd8fbcbbed279850df3a856984aacd519ee6c2162e034b114b8ac47c00157b84ef7c98c0711d933a0177d9d54555629cf381f54341bb0e8f + languageName: node + linkType: hard + "react-refresh@npm:^0.14.0": version: 0.14.0 resolution: "react-refresh@npm:0.14.0" @@ -12051,6 +12133,20 @@ __metadata: languageName: node linkType: hard +"scroll@npm:^3.0.1": + version: 3.0.1 + resolution: "scroll@npm:3.0.1" + checksum: e6b045347adace30035073882e6ef2af7e1c81dd611faf3a578ca8cd0d1a3a9da54932dd97ed6fd99c9573351c758fa50e6d8ed4afb5bd3a33794b6c48d25922 + languageName: node + linkType: hard + +"scrollparent@npm:^2.1.0": + version: 2.1.0 + resolution: "scrollparent@npm:2.1.0" + checksum: 646cfdaf981f94b52f1020cc26b18ff1a751c2fa4e40777f6dfac314500d365b4db3fec39fd06be7b683f0a1dbd29df8ce1629db50381e9072433465b2f446fa + languageName: node + linkType: hard + "select@npm:^1.1.2": version: 1.1.2 resolution: "select@npm:1.1.2" @@ -12798,6 +12894,16 @@ __metadata: languageName: node linkType: hard +"tree-changes@npm:^0.9.1, tree-changes@npm:^0.9.2": + version: 0.9.3 + resolution: "tree-changes@npm:0.9.3" + dependencies: + "@gilbarbara/deep-equal": ^0.1.1 + is-lite: ^0.8.2 + checksum: 86d890b18e83f2a20e7257982aec62efa186abbb08de4cead1c8062c50793f5b3c5fd09f98d9f4b8784b921e830687c0ea9bdb42ef4abb2eb9d6782d8c56a673 + languageName: node + linkType: hard + "ts-interface-checker@npm:^0.1.9": version: 0.1.13 resolution: "ts-interface-checker@npm:0.1.13" |