Skip to content

Commit

Permalink
feat: Inline multiselect tokens (#3185)
Browse files Browse the repository at this point in the history
Co-authored-by: Katie George <[email protected]>
  • Loading branch information
katiegeorge and Katie George authored Jan 23, 2025
1 parent 3839ca0 commit 76a85a0
Show file tree
Hide file tree
Showing 6 changed files with 42 additions and 9 deletions.
22 changes: 22 additions & 0 deletions pages/multiselect/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,25 @@ export const deselectAriaLabel = (option: MultiselectProps.Option) => {
const label = option?.value || option?.label;
return label ? `Deselect ${label}` : 'no label';
};

export const getInlineAriaLabel = (selectedOptions: MultiselectProps.Options) => {
let label;

if (selectedOptions.length === 0) {
label = 0;
}

if (selectedOptions.length === 1) {
label = selectedOptions[0].label;
}

if (selectedOptions.length === 2) {
label = `${selectedOptions[0].label} and ${selectedOptions[1].label}`;
}

if (selectedOptions.length > 2) {
label = `${selectedOptions[0].label}, ${selectedOptions[1].label}, and ${selectedOptions.length - 2} more`;
}

return label + ' selected';
};
7 changes: 4 additions & 3 deletions pages/multiselect/multiselect.test.page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import * as React from 'react';
import Box from '~components/box';
import Multiselect, { MultiselectProps } from '~components/multiselect';

import { deselectAriaLabel, i18nStrings } from './constants';
import { deselectAriaLabel, getInlineAriaLabel, i18nStrings } from './constants';

const _selectedOptions1 = [
{
Expand Down Expand Up @@ -113,6 +113,7 @@ const options2 = [
],
},
];

export default function MultiselectPage() {
const [selectedOptions1, setSelectedOptions1] = React.useState<MultiselectProps.Options>(_selectedOptions1);
const [selectedOptions2, setSelectedOptions2] = React.useState<MultiselectProps.Options>(_selectedOptions1);
Expand Down Expand Up @@ -235,14 +236,14 @@ export default function MultiselectPage() {
<Box variant="h1">Test: Inline tokens</Box>
<div style={{ width: 200 }}>
<Multiselect
{...{ inlineTokens: true }}
inlineTokens={true}
statusType="pending"
filteringType="none"
options={options1}
placeholder={'Choose option'}
selectedOptions={selectedOptions7}
i18nStrings={i18nStrings}
ariaLabel={`${selectedOptions7.length} accounts selected`}
ariaLabel={getInlineAriaLabel(selectedOptions7)}
onChange={event => {
setSelectedOptions7(event.detail.selectedOptions);
}}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11259,6 +11259,12 @@ use the \`id\` attribute, consider setting it on a parent element instead.",
"optional": true,
"type": "string",
},
{
"description": "Shows tokens inside the trigger instead of below it.",
"name": "inlineTokens",
"optional": true,
"type": "boolean",
},
{
"description": "Overrides the invalidation state. Usually the invalid state
comes from the parent \`FormField\`component,
Expand Down
10 changes: 5 additions & 5 deletions src/multiselect/__tests__/multiselect.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -650,10 +650,10 @@ test('Trigger receives focus when autofocus is true', () => {
expect(document.activeElement).toBe(wrapper.findTrigger().getElement());
});

describe('With inline tokens (private API)', () => {
describe('With inline tokens', () => {
it('can render inline tokens', () => {
const { wrapper } = renderMultiselect(
<Multiselect {...{ inlineTokens: true }} options={defaultOptions} selectedOptions={[defaultOptions[0]]} />
<Multiselect inlineTokens={true} options={defaultOptions} selectedOptions={[defaultOptions[0]]} />
);

// Trigger contains token labels and the number of selected items
Expand All @@ -666,7 +666,7 @@ describe('With inline tokens (private API)', () => {

it('shows placeholder when no items are selected', () => {
const { wrapper } = renderMultiselect(
<Multiselect {...{ inlineTokens: true }} selectedOptions={[]} placeholder="Choose something" />
<Multiselect inlineTokens={true} selectedOptions={[]} placeholder="Choose something" />
);

expect(wrapper.findTrigger().getElement()).toHaveTextContent('Choose something');
Expand All @@ -677,7 +677,7 @@ describe('With inline tokens (private API)', () => {
{ value: '1', label: 'First', description: 'description', tags: ['tag'], labelTag: 'label' },
];
const { wrapper } = renderMultiselect(
<Multiselect {...{ inlineTokens: true }} options={extendedOptions} selectedOptions={[extendedOptions[0]]} />
<Multiselect inlineTokens={true} options={extendedOptions} selectedOptions={[extendedOptions[0]]} />
);

expect(wrapper.findTrigger().getElement()).toHaveTextContent('First');
Expand All @@ -689,7 +689,7 @@ describe('With inline tokens (private API)', () => {
it('shows multiple selected options inline', () => {
const { wrapper } = renderMultiselect(
<Multiselect
{...{ inlineTokens: true }}
inlineTokens={true}
options={defaultOptions}
selectedOptions={[defaultOptions[0], defaultOptions[1], defaultOptions[2]]}
/>
Expand Down
4 changes: 4 additions & 0 deletions src/multiselect/interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@ export interface MultiselectProps extends BaseSelectProps {
* Only use this if the selected options are displayed elsewhere on the page.
*/
hideTokens?: boolean;
/**
* Shows tokens inside the trigger instead of below it.
*/
inlineTokens?: boolean;
/**
* Specifies an `aria-label` for the token deselection button.
* @i18n
Expand Down
2 changes: 1 addition & 1 deletion src/multiselect/internal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ type InternalMultiselectProps = SomeRequired<
MultiselectProps,
'options' | 'selectedOptions' | 'filteringType' | 'statusType' | 'keepOpen' | 'hideTokens'
> &
InternalBaseComponentProps & { inlineTokens?: boolean };
InternalBaseComponentProps;

const InternalMultiselect = React.forwardRef(
(
Expand Down

0 comments on commit 76a85a0

Please sign in to comment.