Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Inline multiselect tokens #3185

Merged
merged 4 commits into from
Jan 23, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@ -11210,6 +11210,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
Loading