diff --git a/package.json b/package.json index 362e872..052d076 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@togglecorp/toggle-ui", - "version": "0.18.3", + "version": "0.18.4", "description": "React component library by togglecorp", "files": [ "/build" diff --git a/src/components/GenericOption/index.tsx b/src/components/GenericOption/index.tsx index a6abe76..4634c3f 100644 --- a/src/components/GenericOption/index.tsx +++ b/src/components/GenericOption/index.tsx @@ -1,5 +1,8 @@ import React, { useCallback, useRef, useEffect } from 'react'; -import { _cs } from '@togglecorp/fujs'; +import { + _cs, + isNotDefined, +} from '@togglecorp/fujs'; import RawButton from '../RawButton'; @@ -15,6 +18,7 @@ export interface GenericOptionParams

>) => React.ReactNode; contentRendererParam: (key: OK, opt: O) => P; + actionsSelector?: (props: O) => React.ReactNode; option: O; optionKey: OK; onClick: (optionKey: OK, option: O) => void; @@ -25,6 +29,7 @@ function GenericOption

({ optionContainerClassName, contentRenderer, contentRendererParam, + actionsSelector, option, onClick, onFocus, @@ -81,23 +86,41 @@ function GenericOption

({ [], ); + if (isNotDefined(actionsSelector)) { + return ( + + {contentRenderer(props)} + + ); + } + return ( - - {contentRenderer(props)} - + + {contentRenderer(props)} + + {actionsSelector(option)} + ); } export default GenericOption; diff --git a/src/components/GenericOption/styles.css b/src/components/GenericOption/styles.css index c6b28ef..3d72c4b 100644 --- a/src/components/GenericOption/styles.css +++ b/src/components/GenericOption/styles.css @@ -2,3 +2,12 @@ padding: calc(var(--tui-spacing-large) - var(--tui-spacing-small)); text-align: left; } +.option-container { + display: flex; + align-items: center; + padding-right: var(--tui-spacing-small); + + .option-renderer { + flex-grow: 1; + } +} diff --git a/src/components/MultiSelectInput/SearchMultiSelectInput.tsx b/src/components/MultiSelectInput/SearchMultiSelectInput.tsx index 59f8f69..787ecdc 100644 --- a/src/components/MultiSelectInput/SearchMultiSelectInput.tsx +++ b/src/components/MultiSelectInput/SearchMultiSelectInput.tsx @@ -12,13 +12,12 @@ import { rankedSearchOnList } from '../../utils'; import styles from './styles.css'; interface OptionProps { - actions?: React.ReactNode; children: React.ReactNode; isActive: boolean; } + function Option(props: OptionProps) { const { - actions, children, isActive, } = props; @@ -31,9 +30,6 @@ function Option(props: OptionProps) {

{ children }
-
- {actions} -
); } @@ -244,11 +240,10 @@ function SearchMultiSelectInput< children: labelSelector(option), containerClassName: _cs(styles.option, isActive && styles.active), title: labelSelector(option), - actions: actionsSelector?.(option), isActive, }; }, - [labelSelector, value, actionsSelector], + [labelSelector, value], ); // FIXME: value should not be on dependency list, also try to pass options like in SelectInput @@ -296,6 +291,7 @@ function SearchMultiSelectInput< optionKeySelector={keySelector} optionRenderer={Option} optionRendererParams={optionRendererParams} + actionsSelector={actionsSelector} optionContainerClassName={styles.optionContainer} onOptionClick={handleOptionClick} valueDisplay={valueDisplay} diff --git a/src/components/SelectInput/SearchSelectInput.tsx b/src/components/SelectInput/SearchSelectInput.tsx index 7d00378..f9b3813 100644 --- a/src/components/SelectInput/SearchSelectInput.tsx +++ b/src/components/SelectInput/SearchSelectInput.tsx @@ -12,12 +12,10 @@ import { rankedSearchOnList } from '../../utils'; import styles from './styles.css'; interface OptionProps { - actions?: React.ReactNode; children: React.ReactNode; } function Option(props: OptionProps) { const { - actions, children, } = props; @@ -29,9 +27,6 @@ function Option(props: OptionProps) {
{ children }
-
- {actions} -
); } @@ -237,12 +232,11 @@ function SearchSelectInput< return { children: labelSelector(option), - actions: actionsSelector?.(option), containerClassName: _cs(styles.option, isActive && styles.active), title: labelSelector(option), }; }, - [value, labelSelector, actionsSelector], + [value, labelSelector], ); const handleOptionClick = useCallback( @@ -283,6 +277,7 @@ function SearchSelectInput< optionKeySelector={keySelector} optionRenderer={Option} optionRendererParams={optionRendererParams} + actionsSelector={actionsSelector} optionContainerClassName={styles.optionContainer} onOptionClick={handleOptionClick} valueDisplay={valueDisplay} diff --git a/src/components/SelectInputContainer/index.tsx b/src/components/SelectInputContainer/index.tsx index 34335b0..370d1d4 100644 --- a/src/components/SelectInputContainer/index.tsx +++ b/src/components/SelectInputContainer/index.tsx @@ -61,6 +61,7 @@ export type SelectInputContainerProps< optionKeySelector: (datum: O, index: number) => OK; optionRenderer: (props: Pick>) => React.ReactNode; optionRendererParams: (optionKey: OK, option: O) => P; + actionsSelector?: (option: O) => React.ReactNode; totalOptionsCount?: number; optionsPopupContentClassName?: string; options: O[] | undefined | null; @@ -112,6 +113,7 @@ function SelectInputContainer) { - // NOTE: This intentionally breaks HTML semantics (link inside a button). - // This workaround is to allow clickable links inside SelectInput options - // where a button wrapper is required. - e.stopPropagation(); - window.open('https://www.google.com/search?q=story', '_blank'); -} - export const WithActions = Template.bind({}); WithActions.args = { actionsSelector: () => ( - - + ), }; diff --git a/src/stories/SearchMultiSelectInput.stories.tsx b/src/stories/SearchMultiSelectInput.stories.tsx index e943fb0..e26f546 100644 --- a/src/stories/SearchMultiSelectInput.stories.tsx +++ b/src/stories/SearchMultiSelectInput.stories.tsx @@ -3,7 +3,6 @@ import { Story } from '@storybook/react/types-6-0'; import { useArgs } from '@storybook/client-api'; import { IoOpenOutline } from 'react-icons/io5'; import SearchMultiSelectInput, { SearchMultiSelectInputProps } from '#components/MultiSelectInput/SearchMultiSelectInput'; -import QuickActionButton from '#components/QuickActionButton'; import useQuery, { entityListTransformer } from '../utils/useQuery'; export default { @@ -190,24 +189,16 @@ const Template: Story) { - // NOTE: This intentionally breaks HTML semantics (link inside a button). - // This workaround is to allow clickable links inside SelectInput options - // where a button wrapper is required. - e.stopPropagation(); - window.open('https://www.google.com/search?q=story', '_blank'); -} - export const WithActions = Template.bind({}); WithActions.args = { actionsSelector: () => ( - - + ), }; diff --git a/src/stories/SearchSelectInput.stories.tsx b/src/stories/SearchSelectInput.stories.tsx index 2f7555e..762a6d4 100644 --- a/src/stories/SearchSelectInput.stories.tsx +++ b/src/stories/SearchSelectInput.stories.tsx @@ -3,7 +3,6 @@ import { Story } from '@storybook/react/types-6-0'; import { useArgs } from '@storybook/client-api'; import { IoOpenOutline } from 'react-icons/io5'; import SearchSelectInput, { SearchSelectInputProps } from '#components/SelectInput/SearchSelectInput'; -import QuickActionButton from '#components/QuickActionButton'; import useQuery, { entityListTransformer } from '../utils/useQuery'; export default { @@ -195,24 +194,16 @@ Default.args = { value: '1', }; -function handleClick(_:string | undefined, e: React.MouseEvent) { - // NOTE: This intentionally breaks HTML semantics (link inside a button). - // This workaround is to allow clickable links inside SelectInput options - // where a button wrapper is required. - e.stopPropagation(); - window.open('https://www.google.com/search?q=story', '_blank'); -} - export const WithActions = Template.bind({}); WithActions.args = { actionsSelector: () => ( - - + ), }; diff --git a/src/stories/SelectInput.stories.tsx b/src/stories/SelectInput.stories.tsx index 4bc649a..e60d460 100644 --- a/src/stories/SelectInput.stories.tsx +++ b/src/stories/SelectInput.stories.tsx @@ -3,7 +3,6 @@ import { Story } from '@storybook/react/types-6-0'; import { useArgs } from '@storybook/client-api'; import { IoOpenOutline } from 'react-icons/io5'; import SelectInput, { SelectInputProps } from '#components/SelectInput'; -import QuickActionButton from '#components/QuickActionButton'; export default { title: 'Input/SelectInput', @@ -47,24 +46,16 @@ const Template: Story) { - // NOTE: This intentionally breaks HTML semantics (link inside a button). - // This workaround is to allow clickable links inside SelectInput options - // where a button wrapper is required. - e.stopPropagation(); - window.open('https://www.google.com/search?q=story', '_blank'); -} - export const WithActions = Template.bind({}); WithActions.args = { actionsSelector: () => ( - - + ), };