Merge pull request #4147 from kamre/multi-select-fix
Improvements for multi-select property
This commit is contained in:
commit
ca15e12ceb
3 changed files with 75 additions and 5 deletions
|
@ -57,6 +57,12 @@ describe('properties/multiSelect', () => {
|
||||||
const board = createBoard()
|
const board = createBoard()
|
||||||
const card = createCard()
|
const card = createCard()
|
||||||
|
|
||||||
|
const expectOptionsMenuToBeVisible = (template: IPropertyTemplate) => {
|
||||||
|
for (const option of template.options) {
|
||||||
|
expect(screen.getByRole('menuitem', {name: option.value})).toBeInTheDocument()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
jest.resetAllMocks()
|
jest.resetAllMocks()
|
||||||
})
|
})
|
||||||
|
@ -128,6 +134,7 @@ describe('properties/multiSelect', () => {
|
||||||
userEvent.type(screen.getByRole('combobox', {name: /value selector/i}), 'b{enter}')
|
userEvent.type(screen.getByRole('combobox', {name: /value selector/i}), 'b{enter}')
|
||||||
|
|
||||||
expect(mockedMutator.changePropertyValue).toHaveBeenCalledWith(board.id, card, propertyTemplate.id, ['multi-option-1', 'multi-option-2'])
|
expect(mockedMutator.changePropertyValue).toHaveBeenCalledWith(board.id, card, propertyTemplate.id, ['multi-option-1', 'multi-option-2'])
|
||||||
|
expectOptionsMenuToBeVisible(propertyTemplate)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('can unselect a option', async () => {
|
it('can unselect a option', async () => {
|
||||||
|
@ -152,6 +159,58 @@ describe('properties/multiSelect', () => {
|
||||||
userEvent.click(screen.getAllByRole('button', {name: /clear/i})[0])
|
userEvent.click(screen.getAllByRole('button', {name: /clear/i})[0])
|
||||||
|
|
||||||
expect(mockedMutator.changePropertyValue).toHaveBeenCalledWith(board.id, card, propertyTemplate.id, ['multi-option-2'])
|
expect(mockedMutator.changePropertyValue).toHaveBeenCalledWith(board.id, card, propertyTemplate.id, ['multi-option-2'])
|
||||||
|
expectOptionsMenuToBeVisible(propertyTemplate)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('can unselect a option via backspace', async () => {
|
||||||
|
const propertyTemplate = buildMultiSelectPropertyTemplate()
|
||||||
|
const propertyValue = ['multi-option-1', 'multi-option-2']
|
||||||
|
|
||||||
|
render(
|
||||||
|
<MultiSelect
|
||||||
|
property={new MultiSelectProperty()}
|
||||||
|
readOnly={false}
|
||||||
|
showEmptyPlaceholder={false}
|
||||||
|
propertyTemplate={propertyTemplate}
|
||||||
|
propertyValue={propertyValue}
|
||||||
|
board={{...board}}
|
||||||
|
card={{...card}}
|
||||||
|
/>,
|
||||||
|
{wrapper: Wrapper},
|
||||||
|
)
|
||||||
|
|
||||||
|
userEvent.click(screen.getByTestId(nonEditableMultiSelectTestId))
|
||||||
|
|
||||||
|
userEvent.type(screen.getByRole('combobox', {name: /value selector/i}), '{backspace}')
|
||||||
|
|
||||||
|
expect(mockedMutator.changePropertyValue).toHaveBeenCalledWith(board.id, card, propertyTemplate.id, ['multi-option-1'])
|
||||||
|
expectOptionsMenuToBeVisible(propertyTemplate)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('can close menu on escape', async () => {
|
||||||
|
const propertyTemplate = buildMultiSelectPropertyTemplate()
|
||||||
|
const propertyValue = ['multi-option-1', 'multi-option-2']
|
||||||
|
|
||||||
|
render(
|
||||||
|
<MultiSelect
|
||||||
|
property={new MultiSelectProperty()}
|
||||||
|
readOnly={false}
|
||||||
|
showEmptyPlaceholder={false}
|
||||||
|
propertyTemplate={propertyTemplate}
|
||||||
|
propertyValue={propertyValue}
|
||||||
|
board={{...board}}
|
||||||
|
card={{...card}}
|
||||||
|
/>,
|
||||||
|
{wrapper: Wrapper},
|
||||||
|
)
|
||||||
|
|
||||||
|
userEvent.click(screen.getByTestId(nonEditableMultiSelectTestId))
|
||||||
|
|
||||||
|
userEvent.type(screen.getByRole('combobox', {name: /value selector/i}), '{escape}')
|
||||||
|
|
||||||
|
for (const option of propertyTemplate.options) {
|
||||||
|
expect(screen.queryByRole('menuitem', {name: option.value})).toBeNull()
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
it('can create a new option', async () => {
|
it('can create a new option', async () => {
|
||||||
|
@ -177,6 +236,7 @@ describe('properties/multiSelect', () => {
|
||||||
userEvent.type(screen.getByRole('combobox', {name: /value selector/i}), 'new-value{enter}')
|
userEvent.type(screen.getByRole('combobox', {name: /value selector/i}), 'new-value{enter}')
|
||||||
|
|
||||||
expect(mockedMutator.insertPropertyOption).toHaveBeenCalledWith(board.id, board.cardProperties, propertyTemplate, expect.objectContaining({value: 'new-value'}), 'add property option')
|
expect(mockedMutator.insertPropertyOption).toHaveBeenCalledWith(board.id, board.cardProperties, propertyTemplate, expect.objectContaining({value: 'new-value'}), 'add property option')
|
||||||
|
expectOptionsMenuToBeVisible(propertyTemplate)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('can delete a option', () => {
|
it('can delete a option', () => {
|
||||||
|
|
|
@ -30,8 +30,8 @@
|
||||||
.IconButton.delete-value {
|
.IconButton.delete-value {
|
||||||
@include z-index(value-selector-delete);
|
@include z-index(value-selector-delete);
|
||||||
width: 16px;
|
width: 16px;
|
||||||
min-width: 16px;
|
|
||||||
height: 16px;
|
height: 16px;
|
||||||
|
flex: 0 0 auto;
|
||||||
|
|
||||||
i {
|
i {
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
|
|
|
@ -71,7 +71,10 @@ const ValueSelectorLabel = (props: LabelProps): JSX.Element => {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<div className='value-menu-option'>
|
<div
|
||||||
|
className='value-menu-option'
|
||||||
|
role='menuitem'
|
||||||
|
>
|
||||||
<div className='label-container'>
|
<div className='label-container'>
|
||||||
<Label color={option.color}>{option.value}</Label>
|
<Label color={option.color}>{option.value}</Label>
|
||||||
</div>
|
</div>
|
||||||
|
@ -162,7 +165,6 @@ function ValueSelector(props: Props): JSX.Element {
|
||||||
captureMenuScroll={true}
|
captureMenuScroll={true}
|
||||||
maxMenuHeight={1200}
|
maxMenuHeight={1200}
|
||||||
isMulti={props.isMulti}
|
isMulti={props.isMulti}
|
||||||
menuIsOpen={true}
|
|
||||||
isClearable={true}
|
isClearable={true}
|
||||||
styles={valueSelectorStyle}
|
styles={valueSelectorStyle}
|
||||||
formatOptionLabel={(option: IPropertyOption, meta: FormatOptionLabelMeta<IPropertyOption>) => (
|
formatOptionLabel={(option: IPropertyOption, meta: FormatOptionLabelMeta<IPropertyOption>) => (
|
||||||
|
@ -181,24 +183,32 @@ function ValueSelector(props: Props): JSX.Element {
|
||||||
getOptionLabel={(o: IPropertyOption) => o.value}
|
getOptionLabel={(o: IPropertyOption) => o.value}
|
||||||
getOptionValue={(o: IPropertyOption) => o.id}
|
getOptionValue={(o: IPropertyOption) => o.id}
|
||||||
onChange={(value: OnChangeValue<IPropertyOption, true | false>, action: ActionMeta<IPropertyOption>): void => {
|
onChange={(value: OnChangeValue<IPropertyOption, true | false>, action: ActionMeta<IPropertyOption>): void => {
|
||||||
if (action.action === 'select-option') {
|
if (action.action === 'select-option' || action.action === 'pop-value') {
|
||||||
if (Array.isArray(value)) {
|
if (Array.isArray(value)) {
|
||||||
props.onChange((value as IPropertyOption[]).map((option) => option.id))
|
props.onChange((value as IPropertyOption[]).map((option) => option.id))
|
||||||
} else {
|
} else {
|
||||||
props.onChange((value as IPropertyOption).id)
|
props.onChange((value as IPropertyOption).id)
|
||||||
|
props.onBlur?.()
|
||||||
}
|
}
|
||||||
} else if (action.action === 'clear') {
|
} else if (action.action === 'clear') {
|
||||||
props.onChange('')
|
props.onChange('')
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
|
onKeyDown={(event) => {
|
||||||
|
if (event.key === 'Escape') {
|
||||||
|
props.onBlur?.()
|
||||||
|
}
|
||||||
|
}}
|
||||||
onBlur={props.onBlur}
|
onBlur={props.onBlur}
|
||||||
onCreateOption={props.onCreate}
|
onCreateOption={props.onCreate}
|
||||||
autoFocus={true}
|
autoFocus={true}
|
||||||
value={props.value || null}
|
value={props.value || null}
|
||||||
closeMenuOnSelect={true}
|
closeMenuOnSelect={!props.isMulti}
|
||||||
placeholder={props.emptyValue}
|
placeholder={props.emptyValue}
|
||||||
hideSelectedOptions={false}
|
hideSelectedOptions={false}
|
||||||
defaultMenuIsOpen={true}
|
defaultMenuIsOpen={true}
|
||||||
|
menuIsOpen={props.isMulti}
|
||||||
|
blurInputOnSelect={!props.isMulti}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue