Skip to content

Commit 87dc52d

Browse files
committed
feat(ui-select,ui-simple-select): add support for rendering selected option's before and after content in Select and SimpleSelect input
Closes: INSTUI-4225
1 parent db5c708 commit 87dc52d

File tree

8 files changed

+823
-7
lines changed

8 files changed

+823
-7
lines changed

cypress/component/Select.cy.tsx

Lines changed: 184 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,8 @@
2222
* SOFTWARE.
2323
*/
2424
import React from 'react'
25-
import { Select } from '../../packages/ui'
26-
import { IconCheckSolid } from '../../packages/ui-icons'
25+
import { Select } from '@instructure/ui'
26+
import { IconCheckSolid, IconEyeSolid } from '@instructure/ui-icons'
2727
import 'cypress-real-events'
2828
import '../support/component'
2929

@@ -45,6 +45,44 @@ const getOptions = (
4545
</Select.Option>
4646
))
4747

48+
type ExampleOptionWithContent = 'opt1' | 'op2' | 'opt3'
49+
50+
const optionsWithBeforeContent = [
51+
{
52+
id: 'opt1',
53+
label: 'Text',
54+
renderBeforeLabel: 'XY'
55+
},
56+
{
57+
id: 'opt2',
58+
label: 'Icon',
59+
renderBeforeLabel: 'YY'
60+
},
61+
{
62+
id: 'opt3',
63+
label: 'Colored Icon',
64+
renderBeforeLabel: <IconEyeSolid />
65+
}
66+
]
67+
68+
const optionsWithAfterContent = [
69+
{
70+
id: 'opt1',
71+
label: 'Text',
72+
renderAfterLabel: 'XY'
73+
},
74+
{
75+
id: 'opt2',
76+
label: 'Icon',
77+
renderAfterLabel: 'YY'
78+
},
79+
{
80+
id: 'opt3',
81+
label: 'Colored Icon',
82+
renderAfterLabel: <IconEyeSolid />
83+
}
84+
]
85+
4886
describe('<Select/>', () => {
4987
it('should render dynamically colored icons before option', async () => {
5088
const renderBeforeLabel = (props: any) => {
@@ -338,4 +376,148 @@ describe('<Select/>', () => {
338376
cy.wrap(onRequestHighlightOption).should('have.been.calledOnce')
339377
cy.wrap(onKeyDown).should('have.been.calledTwice')
340378
})
379+
380+
it("should render the selected option's before content in the input field", async () => {
381+
const MyTestComponent = () => {
382+
const [selectedOptionId, setSelectedOptionId] = React.useState<string>(
383+
optionsWithBeforeContent[0].id
384+
)
385+
const [isShowingOptions, setIsShowingOptions] = React.useState(false)
386+
const [inputValue, setInputValue] = React.useState<string | undefined>(
387+
optionsWithBeforeContent[0].label
388+
)
389+
390+
const getOptionById = (selectedOptionId) => {
391+
return optionsWithBeforeContent.find(
392+
({ id }) => id === selectedOptionId
393+
)
394+
}
395+
396+
const handleShowOptions = () => {
397+
setIsShowingOptions(true)
398+
}
399+
400+
const handleHideOptions = (options) => {
401+
const option = getOptionById(selectedOptionId)?.label
402+
setIsShowingOptions(false)
403+
setInputValue(selectedOptionId ? option : '')
404+
}
405+
406+
const handleSelectOption = (event, data: { id?: string }) => {
407+
const option = data.id ? getOptionById(data.id)?.label : undefined
408+
if (data.id) {
409+
setSelectedOptionId(data.id)
410+
setInputValue(option || '')
411+
}
412+
setIsShowingOptions(false)
413+
}
414+
415+
return (
416+
<Select
417+
renderLabel="Choose an option"
418+
inputValue={optionsWithBeforeContent[0].label}
419+
isOptionContentAppliedToInput={true}
420+
isShowingOptions={isShowingOptions}
421+
onRequestShowOptions={handleShowOptions}
422+
onRequestHideOptions={handleHideOptions}
423+
onRequestSelectOption={handleSelectOption}
424+
>
425+
{optionsWithBeforeContent.map((opt) => (
426+
<Select.Option
427+
id={opt.id}
428+
key={opt.id}
429+
value={opt.label}
430+
renderBeforeLabel={opt.renderBeforeLabel}
431+
isSelected={opt.id === selectedOptionId}
432+
></Select.Option>
433+
))}
434+
</Select>
435+
)
436+
}
437+
cy.mount(<MyTestComponent />)
438+
439+
cy.get('span[class$="-textInput__beforeElement"]').should(
440+
'contain.text',
441+
'XY'
442+
)
443+
cy.get('input').click()
444+
cy.get('ul[role="listbox"]').as('listbox')
445+
cy.get('@listbox')
446+
cy.get('@listbox').find('li').eq(1).click()
447+
cy.get('span[class$="-textInput__beforeElement"]').should(
448+
'contain.text',
449+
'YY'
450+
)
451+
})
452+
453+
it("should render the selected option's after content in the input field", async () => {
454+
const MyTestComponent = () => {
455+
const [selectedOptionId, setSelectedOptionId] = React.useState<string>(
456+
optionsWithAfterContent[0].id
457+
)
458+
const [isShowingOptions, setIsShowingOptions] = React.useState(false)
459+
const [inputValue, setInputValue] = React.useState<string | undefined>(
460+
optionsWithAfterContent[0].label
461+
)
462+
463+
const getOptionById = (selectedOptionId) => {
464+
return optionsWithAfterContent.find(({ id }) => id === selectedOptionId)
465+
}
466+
467+
const handleShowOptions = () => {
468+
setIsShowingOptions(true)
469+
}
470+
471+
const handleHideOptions = (options) => {
472+
const option = getOptionById(selectedOptionId)?.label
473+
setIsShowingOptions(false)
474+
setInputValue(selectedOptionId ? option : '')
475+
}
476+
477+
const handleSelectOption = (event, data: { id?: string }) => {
478+
const option = data.id ? getOptionById(data.id)?.label : undefined
479+
if (data.id) {
480+
setSelectedOptionId(data.id)
481+
setInputValue(option || '')
482+
}
483+
setIsShowingOptions(false)
484+
}
485+
486+
return (
487+
<Select
488+
renderLabel="Choose an option"
489+
inputValue={optionsWithAfterContent[0].label}
490+
isOptionContentAppliedToInput={true}
491+
isShowingOptions={isShowingOptions}
492+
onRequestShowOptions={handleShowOptions}
493+
onRequestHideOptions={handleHideOptions}
494+
onRequestSelectOption={handleSelectOption}
495+
>
496+
{optionsWithAfterContent.map((opt) => (
497+
<Select.Option
498+
id={opt.id}
499+
key={opt.id}
500+
value={opt.label}
501+
renderAfterLabel={opt.renderAfterLabel}
502+
isSelected={opt.id === selectedOptionId}
503+
></Select.Option>
504+
))}
505+
</Select>
506+
)
507+
}
508+
cy.mount(<MyTestComponent />)
509+
510+
cy.get('span[class$="-textInput__afterElement"]').should(
511+
'contain.text',
512+
'XY'
513+
)
514+
cy.get('input').click()
515+
cy.get('ul[role="listbox"]').as('listbox')
516+
cy.get('@listbox')
517+
cy.get('@listbox').find('li').eq(1).click()
518+
cy.get('span[class$="-textInput__afterElement"]').should(
519+
'contain.text',
520+
'YY'
521+
)
522+
})
341523
})

packages/ui-select/src/Select/__examples__/Select.examples.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,7 @@ export default {
100100
],
101101
renderBeforeInput: [null, <IconUserLine key="0" inline={false} />]
102102
},
103+
excludeProps: ['isOptionContentAppliedToInput'],
103104
getComponentProps: () => {
104105
return {
105106
inputValue: 'Option one',

0 commit comments

Comments
 (0)