From c19772656bdb959f2e0cc862b6f5bb80e7632976 Mon Sep 17 00:00:00 2001 From: Rounak Shrestha <66557682+Rounak-stha@users.noreply.github.com> Date: Tue, 17 Oct 2023 09:11:51 +0545 Subject: [PATCH] Component/language drop down (#5982) * feat: Added LanguageDropDown Component * chore: Removed Leftover Files * Resolve Issues - Proper Component Name - Use Predefined Tailwind Class - Pass Handler as Prop * Resolve Issues - Replace Tabs with Spaces - Use Tailwind Default Class for Dropdown Content --- .../Common/LanguageDropDown/index.module.css | 39 +++++++++++ .../Common/LanguageDropDown/index.stories.tsx | 10 +++ components/Common/LanguageDropDown/index.tsx | 56 +++++++++++++++ hooks/__tests__/useClickOutside.test.mjs | 39 ----------- hooks/useClickOutside.ts | 21 ------ i18n/locales/en.json | 3 +- package-lock.json | 70 +++++++++++++++++++ package.json | 1 + 8 files changed, 178 insertions(+), 61 deletions(-) create mode 100644 components/Common/LanguageDropDown/index.module.css create mode 100644 components/Common/LanguageDropDown/index.stories.tsx create mode 100644 components/Common/LanguageDropDown/index.tsx delete mode 100644 hooks/__tests__/useClickOutside.test.mjs delete mode 100644 hooks/useClickOutside.ts diff --git a/components/Common/LanguageDropDown/index.module.css b/components/Common/LanguageDropDown/index.module.css new file mode 100644 index 0000000000000..9f8f4b3800be9 --- /dev/null +++ b/components/Common/LanguageDropDown/index.module.css @@ -0,0 +1,39 @@ +.iconWrapper { + @apply h-9 + w-9 + rounded-md + bg-neutral-100 + p-2 + text-neutral-700 + dark:bg-neutral-900 + dark:text-neutral-300; +} + +.dropDownContent { + @apply max-h-80 + w-48 + overflow-y-scroll + rounded + border + border-neutral-200 + py-[1px] + shadow-lg + dark:border-neutral-900; +} + +.dropDownItem { + @apply cursor-default + px-2.5 + py-1.5 + text-sm + font-medium + text-neutral-800 + outline-none + data-[highlighted]:bg-green-600 + data-[highlighted]:text-white + dark:text-white; +} + +.currentDropDown { + @apply bg-green-600 text-white; +} diff --git a/components/Common/LanguageDropDown/index.stories.tsx b/components/Common/LanguageDropDown/index.stories.tsx new file mode 100644 index 0000000000000..49ca0c78c5510 --- /dev/null +++ b/components/Common/LanguageDropDown/index.stories.tsx @@ -0,0 +1,10 @@ +import type { Meta as MetaObj, StoryObj } from '@storybook/react'; + +import LanguageDropDown from './index'; + +type Story = StoryObj; +type Meta = MetaObj; + +export const Default: Story = {}; + +export default { component: LanguageDropDown } as Meta; diff --git a/components/Common/LanguageDropDown/index.tsx b/components/Common/LanguageDropDown/index.tsx new file mode 100644 index 0000000000000..69964dad7b094 --- /dev/null +++ b/components/Common/LanguageDropDown/index.tsx @@ -0,0 +1,56 @@ +import { LanguageIcon } from '@heroicons/react/24/outline'; +import * as DropdownMenu from '@radix-ui/react-dropdown-menu'; +import classNames from 'classnames'; +import type { FC } from 'react'; +import { useIntl } from 'react-intl'; + +import { useLocale } from '@/hooks/useLocale'; + +import styles from './index.module.css'; + +export type LanguageDropDownProps = { + onClick?: () => void; +}; + +const LanguageDropdown: FC = ({ + onClick = () => {}, +}) => { + const { availableLocales, currentLocale } = useLocale(); + const intl = useIntl(); + + const ariaLabel = intl.formatMessage({ + id: 'components.common.languageDropdown.label', + }); + + return ( + + + + + + + + {availableLocales.map(({ name, code }) => ( + + {name} + + ))} + + + + ); +}; + +export default LanguageDropdown; diff --git a/hooks/__tests__/useClickOutside.test.mjs b/hooks/__tests__/useClickOutside.test.mjs deleted file mode 100644 index ba62438042b2d..0000000000000 --- a/hooks/__tests__/useClickOutside.test.mjs +++ /dev/null @@ -1,39 +0,0 @@ -import { render, fireEvent, screen } from '@testing-library/react'; -import { useState } from 'react'; - -import { useClickOutside } from '../useClickOutside'; - -describe('useClickOutside', () => { - const Component = () => { - const [state, setState] = useState(false); - const ref = useClickOutside(() => setState(false)); - - return ( -
-

Page

-
- - {state && ( -
-

Modal

-
- )} -
-
- ); - }; - - it('should call handler when click outside', () => { - render(); - fireEvent.click(screen.getByText('Open')); - fireEvent.click(screen.getByText('Page')); - expect(screen.queryByText('Modal')).not.toBeInTheDocument(); - }); - - it('should not call handler when click inside', () => { - render(); - fireEvent.click(screen.getByText('Open')); - fireEvent.click(screen.getByText('Modal')); - expect(screen.getByText('Modal')).toBeInTheDocument(); - }); -}); diff --git a/hooks/useClickOutside.ts b/hooks/useClickOutside.ts deleted file mode 100644 index 1268c3a31c3b3..0000000000000 --- a/hooks/useClickOutside.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { useEffect, useRef } from 'react'; - -type Handler = (_event: MouseEvent | TouchEvent) => void; - -export const useClickOutside = (handler: Handler) => { - const ref = useRef(null); - - useEffect(() => { - const listener = (event: MouseEvent | TouchEvent) => { - if (ref.current && !ref.current.contains(event.target as Node)) { - handler(event); - } - }; - - document.addEventListener('click', listener); - - return () => document.removeEventListener('click', listener); - }, [ref, handler]); - - return ref; -}; diff --git a/i18n/locales/en.json b/i18n/locales/en.json index b6136e52ddbbc..53860db66301e 100644 --- a/i18n/locales/en.json +++ b/i18n/locales/en.json @@ -54,5 +54,6 @@ "components.common.pagination.next": "Next", "components.common.pagination.nextAriaLabel": "Next page", "components.common.pagination.defaultLabel": "Pagination", - "components.common.pagination.pageLabel": "Go to page {pageNumber}" + "components.common.pagination.pageLabel": "Go to page {pageNumber}", + "components.common.languageDropdown.label": "Choose Language" } diff --git a/package-lock.json b/package-lock.json index 46b61d91ec38e..048992cbf9ad0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,6 +13,7 @@ "@mdx-js/react": "^2.3.0", "@nodevu/core": "~0.1.0", "@radix-ui/react-avatar": "^1.0.4", + "@radix-ui/react-dropdown-menu": "^2.0.6", "@radix-ui/react-select": "^2.0.0", "@radix-ui/react-tabs": "^1.0.4", "@radix-ui/react-toast": "^1.1.5", @@ -4632,6 +4633,35 @@ } } }, + "node_modules/@radix-ui/react-dropdown-menu": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dropdown-menu/-/react-dropdown-menu-2.0.6.tgz", + "integrity": "sha512-i6TuFOoWmLWq+M/eCLGd/bQ2HfAX1RJgvrBQ6AQLmzfvsLdefxbWu8G9zczcPFfcSPehz9GcpF6K9QYreFV8hA==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/primitive": "1.0.1", + "@radix-ui/react-compose-refs": "1.0.1", + "@radix-ui/react-context": "1.0.1", + "@radix-ui/react-id": "1.0.1", + "@radix-ui/react-menu": "2.0.6", + "@radix-ui/react-primitive": "1.0.3", + "@radix-ui/react-use-controllable-state": "1.0.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-focus-guards": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-guards/-/react-focus-guards-1.0.1.tgz", @@ -4692,6 +4722,46 @@ } } }, + "node_modules/@radix-ui/react-menu": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@radix-ui/react-menu/-/react-menu-2.0.6.tgz", + "integrity": "sha512-BVkFLS+bUC8HcImkRKPSiVumA1VPOOEC5WBMiT+QAVsPzW1FJzI9KnqgGxVDPBcql5xXrHkD3JOVoXWEXD8SYA==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/primitive": "1.0.1", + "@radix-ui/react-collection": "1.0.3", + "@radix-ui/react-compose-refs": "1.0.1", + "@radix-ui/react-context": "1.0.1", + "@radix-ui/react-direction": "1.0.1", + "@radix-ui/react-dismissable-layer": "1.0.5", + "@radix-ui/react-focus-guards": "1.0.1", + "@radix-ui/react-focus-scope": "1.0.4", + "@radix-ui/react-id": "1.0.1", + "@radix-ui/react-popper": "1.1.3", + "@radix-ui/react-portal": "1.0.4", + "@radix-ui/react-presence": "1.0.1", + "@radix-ui/react-primitive": "1.0.3", + "@radix-ui/react-roving-focus": "1.0.4", + "@radix-ui/react-slot": "1.0.2", + "@radix-ui/react-use-callback-ref": "1.0.1", + "aria-hidden": "^1.1.1", + "react-remove-scroll": "2.5.5" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-popper": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.1.3.tgz", diff --git a/package.json b/package.json index a1ea77af72787..52e29279a2b44 100644 --- a/package.json +++ b/package.json @@ -44,6 +44,7 @@ "@mdx-js/react": "^2.3.0", "@nodevu/core": "~0.1.0", "@radix-ui/react-avatar": "^1.0.4", + "@radix-ui/react-dropdown-menu": "^2.0.6", "@radix-ui/react-select": "^2.0.0", "@radix-ui/react-tabs": "^1.0.4", "@radix-ui/react-toast": "^1.1.5",