## Description Grouping the widgets into categories to make it easier for people to find widgets. This will be behind the feature flag `release_widgetdiscovery_enabled` <img src="https://github.com/appsmithorg/appsmith/assets/22471214/4932a091-1831-4d95-b722-3301580fb6be" height="300px" /> Project home [here on Notion](https://www.notion.so/appsmith/Widget-Discoverability-755cf059a1904950888304b90b74106f?d=8bc3059134984787900a69963dd13d90#27967092cfa74505bab55bd163d28c18). #### PR fixes following issue(s) #24865 #24867 #24868 #24869 #### Media > A video or a GIF is preferred. when using Loom, don’t embed because it looks like it’s a GIF. instead, just link to the video > > #### Type of change > Please delete options that are not relevant. - Bug fix (non-breaking change which fixes an issue) - New feature (non-breaking change which adds functionality) - Breaking change (fix or feature that would cause existing functionality to not work as expected) - Chore (housekeeping or task changes that don't impact user perception) - This change requires a documentation update > > > ## Testing > #### How Has This Been Tested? > Please describe the tests that you ran to verify your changes. Also list any relevant details for your test configuration. > Delete anything that is not relevant - [x] Manual - [ ] Jest - [x] Cypress > > #### Test Plan > (https://github.com/appsmithorg/TestSmith/issues/2440) > > #### Issues raised during DP testing > Link issues raised during DP testing for better visiblity and tracking (copy link from comments dropped on this PR) > > > ## Checklist: #### Dev activity - [ ] My code follows the style guidelines of this project - [ ] I have performed a self-review of my own code - [ ] I have commented my code, particularly in hard-to-understand areas - [ ] I have made corresponding changes to the documentation - [ ] My changes generate no new warnings - [ ] I have added tests that prove my fix is effective or that my feature works - [ ] New and existing unit tests pass locally with my changes - [ ] PR is being merged under a feature flag #### QA activity: - [ ] [Speedbreak features](https://github.com/appsmithorg/TestSmith/wiki/Guidelines-for-test-plans#speedbreakers-) have been covered - [x] Test plan covers all impacted features and [areas of interest](https://github.com/appsmithorg/TestSmith/wiki/Guidelines-for-test-plans#areas-of-interest-) - [x] Test plan has been peer reviewed by project stakeholders and other QA members - [x] Manually tested functionality on DP - [ ] We had an implementation alignment call with stakeholders post QA Round 2 - [x] Cypress test cases have been added and approved by SDET/manual QA - [ ] Added `Test Plan Approved` label after Cypress tests were reviewed - [ ] Added `Test Plan Approved` label after JUnit tests were reviewed
100 lines
2.7 KiB
TypeScript
100 lines
2.7 KiB
TypeScript
import React, { useEffect, useMemo, useRef, useState } from "react";
|
|
import { useSelector } from "react-redux";
|
|
import WidgetCard from "./WidgetCard";
|
|
import { getWidgetCards } from "selectors/editorSelectors";
|
|
import { SearchInput } from "design-system";
|
|
import { ENTITY_EXPLORER_SEARCH_ID } from "constants/Explorer";
|
|
import { debounce } from "lodash";
|
|
import {
|
|
createMessage,
|
|
WIDGET_SIDEBAR_CAPTION,
|
|
} from "@appsmith/constants/messages";
|
|
import Fuse from "fuse.js";
|
|
import type { WidgetCardProps } from "widgets/BaseWidget";
|
|
import AnalyticsUtil from "utils/AnalyticsUtil";
|
|
|
|
function WidgetSidebar({ isActive }: { isActive: boolean }) {
|
|
const cards = useSelector(getWidgetCards);
|
|
const [filteredCards, setFilteredCards] = useState(cards);
|
|
const searchInputRef = useRef<HTMLInputElement | null>(null);
|
|
|
|
const fuse = useMemo(() => {
|
|
const options = {
|
|
keys: [
|
|
{
|
|
name: "displayName",
|
|
weight: 0.9,
|
|
},
|
|
{
|
|
name: "searchTags",
|
|
weight: 0.1,
|
|
},
|
|
],
|
|
threshold: 0.2,
|
|
distance: 100,
|
|
};
|
|
|
|
return new Fuse(cards, options);
|
|
}, [cards]);
|
|
|
|
const sendWidgetSearchAnalytics = debounce((value: string) => {
|
|
if (value !== "") {
|
|
AnalyticsUtil.logEvent("WIDGET_SEARCH", { value });
|
|
}
|
|
}, 1000);
|
|
|
|
const filterCards = (keyword: string) => {
|
|
sendWidgetSearchAnalytics(keyword);
|
|
|
|
if (keyword.trim().length > 0) {
|
|
const searchResult = fuse.search(keyword);
|
|
setFilteredCards(searchResult as WidgetCardProps[]);
|
|
} else {
|
|
setFilteredCards(cards);
|
|
}
|
|
};
|
|
|
|
useEffect(() => {
|
|
if (isActive) searchInputRef.current?.focus();
|
|
}, [isActive]);
|
|
|
|
const search = debounce((value: string) => {
|
|
filterCards(value.toLowerCase());
|
|
}, 300);
|
|
|
|
return (
|
|
<div
|
|
className={`flex flex-col overflow-hidden ${isActive ? "" : "hidden"}`}
|
|
>
|
|
<div className="sticky top-0 px-3 mt-0.5">
|
|
<SearchInput
|
|
autoComplete="off"
|
|
autoFocus
|
|
id={ENTITY_EXPLORER_SEARCH_ID}
|
|
onChange={search}
|
|
placeholder="Search widgets"
|
|
ref={searchInputRef}
|
|
type="text"
|
|
/>
|
|
</div>
|
|
<div
|
|
className="flex-grow px-3 mt-3 overflow-y-scroll"
|
|
data-testid="widget-sidebar-scrollable-wrapper"
|
|
>
|
|
<p className="px-3 py-3 text-sm leading-relaxed t--widget-sidebar">
|
|
{createMessage(WIDGET_SIDEBAR_CAPTION)}
|
|
</p>
|
|
<div className="grid items-stretch grid-cols-3 gap-3 justify-items-stretch">
|
|
{filteredCards.map((card) => (
|
|
<WidgetCard details={card} key={card.key} />
|
|
))}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
WidgetSidebar.displayName = "WidgetSidebar";
|
|
|
|
export default WidgetSidebar;
|