Skip to content

Commit

Permalink
feat(Dropdown): add E2E tests (#1962)
Browse files Browse the repository at this point in the history
* feat: add initial dropdown test

* feat: move major interaction tests to e2e

* feat: add controleld dropdown e2e tests

* Create lovely-chairs-sneeze.md

* fix: console log

* Update lovely-chairs-sneeze.md

* Update lovely-chairs-sneeze.md

* fix: BaseInput 2 clicks on dropdown

* fix: test for safari

* fix: handle double clicks with stopPropogation

* fix: eslint error

* fix: lint
  • Loading branch information
saurabhdaware authored Feb 7, 2024
1 parent 0718591 commit a9c779b
Show file tree
Hide file tree
Showing 5 changed files with 376 additions and 422 deletions.
12 changes: 12 additions & 0 deletions .changeset/lovely-chairs-sneeze.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
---
"@razorpay/blade": patch
---

- feat(Dropdown): add E2E tests
- fix(Dropdown): dropdown getting closed without explicit isOpen={false} in controlled dropdown

> [!Note]
>
> if you have used ControlledDropdown in a similar way how it was documented, things should work fine.
>
> If you have used `isOpen` from Controlled Dropdown but you're not handling it inside `onOpenChange`, you will have to handle that state as well. E.g. `isOpen={isDropdownOpen} onOpenChange={(isOpen) => setIsDropdownOpen(isOpen)}`
42 changes: 26 additions & 16 deletions packages/blade/src/components/Dropdown/Dropdown.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ import { getComponentId, isValidAllowedChildren } from '~utils/isValidAllowedChi
import { isReactNative } from '~utils';
import { MetaConstants, metaAttribute } from '~utils/metaAttribute';
import { throwBladeError } from '~utils/logger';
import { useDidUpdate } from '~utils/useDidUpdate';
import type { ContainerElementType } from '~utils/types';
import { useControllableState } from '~utils/useControllable';

const validDropdownChildren = [
dropdownComponentIds.triggers.SelectInput,
Expand Down Expand Up @@ -59,7 +59,6 @@ const _Dropdown = ({
testID,
...styledProps
}: DropdownProps): React.ReactElement => {
const [isOpen, setIsOpen] = React.useState<boolean>(isOpenControlled ?? false);
const [options, setOptions] = React.useState<DropdownContextType['options']>([]);
const [filteredValues, setFilteredValues] = React.useState<string[]>([]);
const [selectedIndices, setSelectedIndices] = React.useState<
Expand Down Expand Up @@ -97,21 +96,28 @@ const _Dropdown = ({
const dropdownContainerRef = React.useRef<HTMLDivElement>(null);

const dropdownBaseId = useId('dropdown');
const isDropdownOpenRef = React.useRef(isOpenControlled);

useDidUpdate(() => {
onOpenChange?.(isOpen);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [isOpen]);
const [isDropdownOpen, setIsDropdownOpen] = useControllableState({
value: isOpenControlled,
defaultValue: false,
onChange: (isOpenControlledValue) => {
isDropdownOpenRef.current = isOpenControlledValue;
onOpenChange?.(isOpenControlledValue);
},
});

React.useEffect(() => {
if (isOpenControlled !== undefined) {
setIsOpen(isOpenControlled);
}
}, [isOpenControlled]);
isDropdownOpenRef.current = isDropdownOpen;

const setIsOpen = (isOpenValue: boolean): void => {
isDropdownOpenRef.current = isOpenValue;
setIsDropdownOpen(() => isOpenValue);
};

const close = React.useCallback(() => {
setActiveTagIndex(-1);
setIsOpen(false);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);

React.Children.map(children, (child) => {
Expand Down Expand Up @@ -143,7 +149,7 @@ const _Dropdown = ({

const contextValue = React.useMemo<DropdownContextType>(
() => ({
isOpen,
isOpen: isDropdownOpen,
setIsOpen,
close,
selectedIndices,
Expand Down Expand Up @@ -181,7 +187,8 @@ const _Dropdown = ({
}),
// eslint-disable-next-line react-hooks/exhaustive-deps
[
isOpen,
isDropdownOpen,
isOpenControlled,
selectedIndices,
controlledValueIndices,
options,
Expand All @@ -199,15 +206,15 @@ const _Dropdown = ({

const BottomSheetAndDropdownGlueContextValue = React.useMemo((): BottomSheetAndDropdownGlueContext => {
return {
isOpen,
isOpen: isDropdownOpen,
dropdownHasBottomSheet,
hasAutoCompleteInBottomSheetHeader,
setDropdownHasBottomSheet,
// This is the dismiss function which will be injected into the BottomSheet
// Basically <BottomSheet onDismiss={onBottomSheetDismiss} />
onBottomSheetDismiss: close,
};
}, [dropdownHasBottomSheet, hasAutoCompleteInBottomSheetHeader, isOpen, close]);
}, [dropdownHasBottomSheet, hasAutoCompleteInBottomSheetHeader, isDropdownOpen, close]);

React.useEffect((): (() => void) | undefined => {
if (!isReactNative()) {
Expand All @@ -220,7 +227,10 @@ const _Dropdown = ({
return;
}

if (!dropdown.contains(target) && !isTagDismissedRef.current?.value) {
const isOutsideClick = !dropdown.contains(target) && !isTagDismissedRef.current?.value;

const isDropdownOpenState = isDropdownOpenRef.current;
if (isOutsideClick && isDropdownOpenState) {
close();
}

Expand Down
Loading

0 comments on commit a9c779b

Please sign in to comment.