Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: Popover in Multi select widget inside modals breaks away from the widget #13129

Merged
merged 36 commits into from
Jun 22, 2022
Merged
Show file tree
Hide file tree
Changes from 30 commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
9301bf9
fix: Popover in Multi select widget inside modals breaks away from th…
wmdev0808 Apr 20, 2022
43ca423
Merge branch 'release' into fix/12161-multiselects-broken-in-modal
wmdev0808 Apr 20, 2022
b2c1ebf
fix: Popover in Multi select widget inside modals breaks away from th…
wmdev0808 Apr 20, 2022
ef56d73
Merge branch 'release' into fix/12161-multiselects-broken-in-modal
wmdev0808 Apr 20, 2022
0771f23
fix: Popover in Multi select widget inside modals breaks away from th…
wmdev0808 Apr 20, 2022
58dfb48
Merge branch 'release' into fix/12161-multiselects-broken-in-modal
wmdev0808 May 4, 2022
806b212
Merge branch 'release' into fix/12161-multiselects-broken-in-modal
Tooluloope May 24, 2022
9a8bb35
fix: popup issues
Tooluloope May 24, 2022
2d694ca
remove unused code
Tooluloope May 24, 2022
d07d8f8
fix: PR review
Tooluloope May 25, 2022
17ec737
Revert "fix: popup issues"
Tooluloope May 30, 2022
88a1daa
Revert "remove unused code"
Tooluloope May 30, 2022
e1301dc
fix: remove autofocus
Tooluloope May 31, 2022
af17e7b
Merge branch 'release' into fix/12161-multiselects-broken-in-modal
Tooluloope May 31, 2022
b22975f
fix: PR review
Tooluloope May 31, 2022
6108d6f
Fix: Disable scrolling when dropdown opens
Tooluloope Jun 1, 2022
1680df9
fix: for MultiSelect
Tooluloope Jun 8, 2022
0d4f8a0
Merge branch 'release' into fix/12161-multiselects-broken-in-modal
Tooluloope Jun 8, 2022
22db2ed
fix: TreeSelect
Tooluloope Jun 8, 2022
3a33155
fix: remove dead code
Tooluloope Jun 8, 2022
665aec5
Merge branch 'release' into fix/12161-multiselects-broken-in-modal
Tooluloope Jun 8, 2022
cef5bed
fix: empty commit
Tooluloope Jun 9, 2022
4d5fc3b
Revert "fix: empty commit"
Tooluloope Jun 9, 2022
bd4632a
fix: refactor code
Tooluloope Jun 9, 2022
b77870a
Merge branch 'release' into fix/12161-multiselects-broken-in-modal
Tooluloope Jun 9, 2022
1f0c38b
fix: remove template literals
Tooluloope Jun 9, 2022
2c46d42
fix: CANVAS_SELECTOR
Tooluloope Jun 9, 2022
909bbf0
fix: remove console statements
Tooluloope Jun 9, 2022
3d7d891
fix: more refactoring
Tooluloope Jun 9, 2022
ddaf283
fix: add code comments
Tooluloope Jun 9, 2022
90532f1
merged latest release
ashit-rath Jun 17, 2022
2c6054f
Merge branch 'release' into fix/12161-multiselects-broken-in-modal
Tooluloope Jun 20, 2022
7ab05c4
fix: failing tests
Tooluloope Jun 20, 2022
5af2839
fix: popup issue
Tooluloope Jun 21, 2022
31f91f6
fix: use ESC to close dropdown
Tooluloope Jun 22, 2022
43ecd36
fix: failing tests
Tooluloope Jun 22, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion app/client/src/constants/WidgetConstants.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ export const WIDGET_CLASSNAME_PREFIX = "WIDGET_";
export const MAIN_CONTAINER_WIDGET_ID = "0";
export const MAIN_CONTAINER_WIDGET_NAME = "MainContainer";
export const MODAL_PORTAL_CLASSNAME = "bp3-modal-widget";
export const CANVAS_CLASSNAME = "appsmith_widget_0";
export const CANVAS_SELECTOR = "canvas";

export const DEFAULT_CENTER = { lat: -34.397, lng: 150.644 };

Expand Down
2 changes: 2 additions & 0 deletions app/client/src/pages/AppViewer/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ import {
} from "actions/controlActions";
import { setAppViewHeaderHeight } from "actions/appViewActions";
import { showPostCompletionMessage } from "selectors/onboardingSelectors";
import { CANVAS_SELECTOR } from "constants/WidgetConstants";
import { getShowBrandingBadge } from "@appsmith/selectors/organizationSelectors";

const AppViewerBody = styled.section<{
Expand Down Expand Up @@ -222,6 +223,7 @@ function AppViewer(props: Props) {
backgroundColor={selectedTheme.properties.colors.backgroundColor}
>
<AppViewerBody
className={CANVAS_SELECTOR}
hasPages={pages.length > 1}
headerHeight={headerHeight}
showGuidedTourMessage={showGuidedTourMessage}
Expand Down
37 changes: 14 additions & 23 deletions app/client/src/widgets/MultiSelectTreeWidget/component/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,17 +18,14 @@ import "rc-tree-select/assets/index.less";
import { DefaultValueType } from "rc-tree-select/lib/interface";
import { TreeNodeProps } from "rc-tree-select/lib/TreeNode";
import { CheckedStrategy } from "rc-tree-select/lib/utils/strategyUtil";
import {
CANVAS_CLASSNAME,
MODAL_PORTAL_CLASSNAME,
TextSize,
} from "constants/WidgetConstants";
import { RenderMode, TextSize } from "constants/WidgetConstants";
import { Alignment, Button, Classes, InputGroup } from "@blueprintjs/core";
import { labelMargin, WidgetContainerDiff } from "widgets/WidgetUtils";
import Icon from "components/ads/Icon";
import { Colors } from "constants/Colors";
import { LabelPosition } from "components/constants";
import LabelWithTooltip from "components/ads/LabelWithTooltip";
import useDropdown from "widgets/useDropdown";

export interface TreeSelectProps
extends Required<
Expand Down Expand Up @@ -63,6 +60,7 @@ export interface TreeSelectProps
widgetId: string;
filterText?: string;
isFilterable: boolean;
renderMode?: RenderMode;
}

const getSvg = (expanded: boolean) => (
Expand Down Expand Up @@ -99,7 +97,6 @@ const switcherIcon = (treeNode: TreeNodeProps) => {
}
return getSvg(treeNode.expanded);
};
const FOCUS_TIMEOUT = 500;

function MultiTreeSelectComponent({
accentColor,
Expand All @@ -126,6 +123,7 @@ function MultiTreeSelectComponent({
onChange,
options,
placeholder,
renderMode,
value,
widgetId,
width,
Expand All @@ -134,10 +132,17 @@ function MultiTreeSelectComponent({
const [filter, setFilter] = useState(filterText ?? "");

const _menu = useRef<HTMLElement | null>(null);

const labelRef = useRef<HTMLDivElement>(null);
const inputRef = useRef<HTMLInputElement>(null);

const [memoDropDownWidth, setMemoDropDownWidth] = useState(0);

const { BackDrop, getPopupContainer, onOpen, selectRef } = useDropdown({
inputRef,
renderMode,
});

// treeDefaultExpandAll is uncontrolled after first render,
// using this to force render to respond to changes in expandAll
useEffect(() => {
Expand All @@ -152,15 +157,6 @@ function MultiTreeSelectComponent({
[filter],
);

const getDropdownPosition = useCallback(() => {
const node = _menu.current;
if (Boolean(node?.closest(`.${MODAL_PORTAL_CLASSNAME}`))) {
return document.querySelector(
`.${MODAL_PORTAL_CLASSNAME}`,
) as HTMLElement;
}
return document.querySelector(`.${CANVAS_CLASSNAME}`) as HTMLElement;
}, []);
const onQueryChange = useCallback((event: ChangeEvent<HTMLInputElement>) => {
event.stopPropagation();
setFilter(event.target.value);
Expand All @@ -185,9 +181,9 @@ function MultiTreeSelectComponent({
menu: React.ReactElement<any, string | React.JSXElementConstructor<any>>,
) => (
<>
<BackDrop />
{isFilterable ? (
<InputGroup
autoFocus
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ashit-rath this was the major reason for the jumps/scrolling when the dropdown is rendered

inputRef={inputRef}
leftIcon="search"
onChange={onQueryChange}
Expand All @@ -205,12 +201,6 @@ function MultiTreeSelectComponent({
[loading, isFilterable, filter, onQueryChange],
);

const onOpen = useCallback((open: boolean) => {
if (open) {
setTimeout(() => inputRef.current?.focus(), FOCUS_TIMEOUT);
}
}, []);

const onClear = useCallback(() => onChange([], []), []);

return (
Expand Down Expand Up @@ -265,7 +255,7 @@ function MultiTreeSelectComponent({
dropdownRender={dropdownRender}
dropdownStyle={dropdownStyle}
filterTreeNode
getPopupContainer={getDropdownPosition}
getPopupContainer={getPopupContainer}
inputIcon={
<Icon
className="dropdown-icon"
Expand All @@ -283,6 +273,7 @@ function MultiTreeSelectComponent({
onClear={onClear}
onDropdownVisibleChange={onOpen}
placeholder={placeholder}
ref={selectRef}
removeIcon={
<Icon
className="remove-icon"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -532,6 +532,7 @@ class MultiSelectTreeWidget extends BaseWidget<
onChange={this.onOptionChange}
options={options}
placeholder={this.props.placeholderText as string}
renderMode={this.props.renderMode}
value={filteredValue}
widgetId={this.props.widgetId}
width={componentWidth}
Expand Down
4 changes: 2 additions & 2 deletions app/client/src/widgets/MultiSelectWidget/component/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {
StyledCheckbox,
} from "./index.styled";
import {
CANVAS_CLASSNAME,
CANVAS_SELECTOR,
MODAL_PORTAL_CLASSNAME,
TextSize,
} from "constants/WidgetConstants";
Expand Down Expand Up @@ -109,7 +109,7 @@ function MultiSelectComponent({
`.${MODAL_PORTAL_CLASSNAME}`,
) as HTMLElement;
}
return document.querySelector(`.${CANVAS_CLASSNAME}`) as HTMLElement;
return document.querySelector(`.${CANVAS_SELECTOR}`) as HTMLElement;
Tooluloope marked this conversation as resolved.
Show resolved Hide resolved
}, []);

const handleSelectAll = () => {
Expand Down
81 changes: 14 additions & 67 deletions app/client/src/widgets/MultiSelectWidgetV2/component/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,22 +18,20 @@ import MenuItemCheckBox, {
StyledCheckbox,
InputContainer,
} from "./index.styled";
import {
CANVAS_CLASSNAME,
MODAL_PORTAL_CLASSNAME,
TextSize,
} from "constants/WidgetConstants";
import { RenderMode, TextSize } from "constants/WidgetConstants";
import Icon from "components/ads/Icon";
import { Alignment, Button, Classes, InputGroup } from "@blueprintjs/core";
import { labelMargin, WidgetContainerDiff } from "widgets/WidgetUtils";
import { Colors } from "constants/Colors";
import { LabelPosition } from "components/constants";
import { uniqBy } from "lodash";
import LabelWithTooltip from "components/ads/LabelWithTooltip";
import useDropdown from "widgets/useDropdown";

const menuItemSelectedIcon = (props: { isSelected: boolean }) => {
return <MenuItemCheckBox checked={props.isSelected} />;
};

export interface MultiSelectProps
extends Required<
Pick<
Expand Down Expand Up @@ -66,10 +64,10 @@ export interface MultiSelectProps
accentColor?: string;
onFocus?: (e: React.FocusEvent) => void;
onBlur?: (e: React.FocusEvent) => void;
renderMode?: RenderMode;
}

const DEBOUNCE_TIMEOUT = 1000;
const FOCUS_TIMEOUT = 500;

function MultiSelectComponent({
accentColor,
Expand Down Expand Up @@ -97,6 +95,7 @@ function MultiSelectComponent({
onFocus,
options,
placeholder,
renderMode,
serverSideFiltering,
value,
widgetId,
Expand All @@ -111,6 +110,11 @@ function MultiSelectComponent({
const labelRef = useRef<HTMLDivElement>(null);
const inputRef = useRef<HTMLInputElement>(null);

const { BackDrop, getPopupContainer, onOpen, selectRef } = useDropdown({
inputRef,
renderMode,
});

// SelectAll if all options are in Value
useEffect(() => {
if (
Expand Down Expand Up @@ -163,15 +167,6 @@ function MultiSelectComponent({
) : null,
[filter],
);
const getDropdownPosition = useCallback(() => {
const node = _menu.current;
if (Boolean(node?.closest(`.${MODAL_PORTAL_CLASSNAME}`))) {
return document.querySelector(
`.${MODAL_PORTAL_CLASSNAME}`,
) as HTMLElement;
}
return document.querySelector(`.${CANVAS_CLASSNAME}`) as HTMLElement;
}, []);

const handleSelectAll = () => {
if (!isSelectAll) {
Expand All @@ -195,63 +190,13 @@ function MultiSelectComponent({
return onChange([]);
};

const onOpen = useCallback((open: boolean) => {
if (open) {
setTimeout(() => inputRef.current?.focus(), FOCUS_TIMEOUT);
}
}, []);

const checkOptionsAndValue = () => {
const emptyFalseArr = [false];
if (value.length === 0 || filteredOptions.length === 0)
return emptyFalseArr;
return filteredOptions.map((x) => value.some((y) => y.value === x.value));
};

// SelectAll if all options are in Value
useEffect(() => {
Tooluloope marked this conversation as resolved.
Show resolved Hide resolved
if (
!isSelectAll &&
filteredOptions.length &&
value.length &&
!checkOptionsAndValue().includes(false)
) {
setIsSelectAll(true);
}
if (isSelectAll && filteredOptions.length !== value.length) {
setIsSelectAll(false);
}
}, [filteredOptions, value]);

// Trigger onFilterChange once filter is Updated
useEffect(() => {
const timeOutId = setTimeout(
() => onFilterChange(filter),
DEBOUNCE_TIMEOUT,
);
return () => clearTimeout(timeOutId);
}, [filter]);

// Filter options based on serverSideFiltering
useEffect(
() => {
if (serverSideFiltering) {
return setFilteredOptions(options);
}
const filtered = options.filter((option) => {
return (
String(option.label)
.toLowerCase()
.indexOf(filter.toLowerCase()) >= 0 ||
String(option.value)
.toLowerCase()
.indexOf(filter.toLowerCase()) >= 0
);
});
setFilteredOptions(filtered);
},
serverSideFiltering ? [options] : [filter, options],
);
useEffect(() => {
const parentWidth = width - WidgetContainerDiff;
if (compactMode && labelRef.current) {
Expand All @@ -277,6 +222,7 @@ function MultiSelectComponent({
menu: React.ReactElement<any, string | React.JSXElementConstructor<any>>,
) => (
<>
<BackDrop />
{isFilterable ? (
<InputGroup
inputRef={inputRef}
Expand Down Expand Up @@ -354,15 +300,15 @@ function MultiSelectComponent({
<Select
animation="slide-up"
choiceTransitionName="rc-select-selection__choice-zoom"
className="rc-select"
// TODO: Make Autofocus a variable in the property pane
// autoFocus
className="rc-select"
defaultActiveFirstOption={false}
disabled={disabled}
dropdownClassName={`multi-select-dropdown multiselect-popover-width-${widgetId}`}
dropdownRender={dropdownRender}
dropdownStyle={dropdownStyle}
getPopupContainer={getDropdownPosition}
getPopupContainer={getPopupContainer}
inputIcon={
<Icon
className="dropdown-icon"
Expand All @@ -384,6 +330,7 @@ function MultiSelectComponent({
onFocus={onFocus}
options={filteredOptions}
placeholder={placeholder || "select option(s)"}
ref={selectRef}
removeIcon={
<Icon
className="remove-icon"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -615,6 +615,7 @@ class MultiSelectWidget extends BaseWidget<
onFilterChange={this.onFilterChange}
options={options}
placeholder={this.props.placeholderText as string}
renderMode={this.props.renderMode}
serverSideFiltering={this.props.serverSideFiltering}
value={values}
widgetId={this.props.widgetId}
Expand Down
Loading