Skip to content
Merged
Show file tree
Hide file tree
Changes from 14 commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
bafa298
[base-trigger] added default width for LinkTrigger.Text
ilyabrower Apr 8, 2026
5f95dda
Merge branch 'release/v17' into UIK-5072/width-for-link-trigger-text
Valeria-Zimnitskaya Apr 10, 2026
218e9a8
[base-trigger] update story
Valeria-Zimnitskaya Apr 10, 2026
e808324
[base-trigger] update story
Valeria-Zimnitskaya Apr 10, 2026
7486d2d
[base-trigger] update story
Valeria-Zimnitskaya Apr 10, 2026
3ee395e
Merge branch 'release/v17' into UIK-5072/width-for-link-trigger-text
Valeria-Zimnitskaya Apr 10, 2026
6155284
Merge branch 'release/v17' into UIK-5072/width-for-link-trigger-text
Valeria-Zimnitskaya Apr 10, 2026
dd7aa24
[base-trigger] update story
Valeria-Zimnitskaya Apr 10, 2026
137d82e
[base-trigger] fixed LinkTrigger.Text width - subtract all addons by …
ilyabrower Apr 12, 2026
c917f30
Merge branch 'release/v17' into UIK-5072/width-for-link-trigger-text
Valeria-Zimnitskaya Apr 13, 2026
fcf9f90
[base-trigger] fixed LinkTrigger.Text width - subtract all addons by …
ilyabrower Apr 13, 2026
a9cc3d8
[chore] Merge branch 'UIK-5072/width-for-link-trigger-text' of github…
ilyabrower Apr 13, 2026
f9630b3
[base-trigger] update test story
Valeria-Zimnitskaya Apr 13, 2026
2c9e86e
[base-trigger] update snapshots
Valeria-Zimnitskaya Apr 13, 2026
d50813d
[base-trigger] update test story test and snapshots for link trigger
Valeria-Zimnitskaya Apr 14, 2026
333c71c
[base-trigger] removed unnecessary comment
ilyabrower Apr 14, 2026
1221f21
[base-trigger] update test story
Valeria-Zimnitskaya Apr 14, 2026
0ae6f82
[base-trigger] update test story
Valeria-Zimnitskaya Apr 14, 2026
d957e98
[base-trigger] added dynamically changed spin size
ilyabrower Apr 14, 2026
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
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
15 changes: 11 additions & 4 deletions semcore/base-trigger/src/LinkTrigger.jsx
Original file line number Diff line number Diff line change
@@ -1,27 +1,32 @@
import { ButtonLink } from '@semcore/button';
import { createComponent, Component, Root } from '@semcore/core';
import addonTextChildren from '@semcore/core/lib/utils/addonTextChildren';
import ChevronDown from '@semcore/icon/ChevronDown/m';
import { findAllComponents } from '@semcore/core/lib/utils/findComponent';
import ChevronDownL from '@semcore/icon/ChevronDown/l';
import ChevronDownM from '@semcore/icon/ChevronDown/m';
import Spin from '@semcore/spin';
import React from 'react';

class RootLinkTrigger extends Component {
static displayName = 'LinkTrigger';

getTextProps(props) {
const { placeholder, empty } = this.asProps;
const { placeholder, empty, Children, size } = this.asProps;
const content = empty ? placeholder : props.children;
const addons = findAllComponents(Children, [LinkTrigger.Addon.displayName]);
const addonWidth = size >= 600 ? 28 : 20;

return {
'use:children': content,
empty,
'w': `calc(100% - ${addonWidth * (addons.length + 1)}px)`, // 20px is for the ChevronDown (16px) and marginLeft (4px) * addons - for addons count
};
}

render() {
const SLinkTrigger = Root;
const SLinkAddon = LinkTrigger.Addon;
const { Children, loading, empty, disabled } = this.asProps;
const { Children, loading, empty, disabled, size } = this.asProps;

return (
<SLinkTrigger
Expand All @@ -30,7 +35,9 @@ class RootLinkTrigger extends Component {
>
{addonTextChildren(Children, LinkTrigger.Text, LinkTrigger.Addon, empty)}
<SLinkAddon>
{loading ? <Spin size='xs' theme='currentColor' /> : <ChevronDown />}
{loading
? <Spin size='xs' theme='currentColor' />
: size >= 600 ? <ChevronDownL /> : <ChevronDownM />}
</SLinkAddon>
</SLinkTrigger>
);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,77 +1,183 @@
import MathPlusAltL from '@semcore/icon/MathPlusAlt/l';
import MathPlusAltM from '@semcore/icon/MathPlusAlt/m';
import Badge from '@semcore/ui/badge';
import { Flex } from '@semcore/ui/base-components';
import { LinkTrigger } from '@semcore/ui/base-trigger';
import type { LinkTriggerProps } from '@semcore/ui/base-trigger';
import Counter, { type CounterProps } from '@semcore/ui/counter';
import Flags from '@semcore/ui/flags';
import Select from '@semcore/ui/select';
import Tag from '@semcore/ui/tag';
import type { TextEllipsisProps } from '@semcore/ui/typography';
import { Text } from '@semcore/ui/typography';
import React from 'react';

type AddonType = 'icon' | 'badge' | 'counter' | 'flag' | 'tag';

interface Project {
id: number;
name: string;
}

type LinkTriggerSelectDDMenuExample = LinkTriggerProps;
type LinkTriggerSelectDDMenuExample = LinkTriggerProps & {
showAddonLeft?: boolean;
showAddonRight?: boolean;
addonLeftType?: AddonType;
addonRightType?: AddonType;
ellipsis?: TextEllipsisProps;
w?: number;
};

const Demo = (props: LinkTriggerSelectDDMenuExample) => {
const {
size = 300,
active,
empty,
placeholder,
disabled,
loading,
color,
showAddonLeft = false,
showAddonRight = false,
addonLeftType = 'icon',
addonRightType = 'icon',
ellipsis,
w,
} = props;

const [selectedProject, setSelectedProject] = React.useState<Project | undefined>();
const [deviceValue, setDeviceValue] = React.useState<string | null>(null);

const numSize = Number(size);
const IconAddon = numSize < 600 ? MathPlusAltM : MathPlusAltL;

let counterSize: CounterProps['size'];
if (numSize >= 600) {
counterSize = 'l';
} else if (numSize >= 300) {
counterSize = 'm';
}

const renderAddon = (show: boolean, type: AddonType) => {
if (!show) return null;

switch (type) {
case 'badge':
return <LinkTrigger.Addon><Badge type='new' /></LinkTrigger.Addon>;
case 'counter':
return <LinkTrigger.Addon><Counter size={counterSize}>17</Counter></LinkTrigger.Addon>;
case 'flag':
return <LinkTrigger.Addon><Flags name='US' /></LinkTrigger.Addon>;
case 'tag':
return <LinkTrigger.Addon><Tag>Label</Tag></LinkTrigger.Addon>;
case 'icon':
default:
return <LinkTrigger.Addon><IconAddon /></LinkTrigger.Addon>;
}
};

const hasEllipsis = ellipsis !== undefined && ellipsis.ellipsis !== false;
const ellipsisW = hasEllipsis ? (w || (numSize < 600 ? 150 : 300)) : undefined;

const handleSelect = (id: number) => {
setSelectedProject(() => projects.find((project) => project.id === id));
};

return (
<Flex gap={2} alignItems='start' direction='column'>
<Flex gap={4} alignItems='flex-start' direction='column'>
{/* Pattern 1: Select with tag={LinkTrigger} */}
<Select
tag={LinkTrigger}
options={devices}
data-test-id='base-trigger-as-tag-in-select'
aria-label='base addon'
size={props.size}
active={props.active}
empty={props.empty}
placeholder={props.placeholder}
disabled={props.disabled}
loading={props.loading}
color={props.color}
size={size}
active={active}
empty={empty}
placeholder={placeholder}
disabled={disabled}
loading={loading}
color={color}
/>

{/* Pattern 2: Select with Select.Trigger and LinkTrigger.Text with getTriggerProps */}
<Select
value={selectedProject?.id}
onChange={handleSelect}
placeholder='Select Project'
placeholder={placeholder}
>
{({ getTriggerProps }) => {
return (
<>
<Select.Trigger
tag={LinkTrigger}
active={props.active}
empty={props.empty}
placeholder={props.placeholder}
disabled={props.disabled}
loading={props.loading}
color={props.color}
size={props.size}
{({ getTriggerProps }) => (
<>
<Select.Trigger
tag={LinkTrigger}
active={active}
empty={empty}
disabled={disabled}
loading={loading}
color={color}
size={size}
>
{renderAddon(showAddonLeft, addonLeftType)}
<LinkTrigger.Text
tag={Text}
fontWeight={400}
w={ellipsisW}
{...ellipsis}
ellipsis:observeChildrenMutations
>
<LinkTrigger.Text
tag={Text}
fontWeight={400}
noWrap
>
{selectedProject?.name ??
(getTriggerProps().placeholder as string)}
</LinkTrigger.Text>
</Select.Trigger>
<Select.Popper aria-label='Projects'>
{selectedProject?.name ?? (getTriggerProps().placeholder as string)}
</LinkTrigger.Text>
{renderAddon(showAddonRight, addonRightType)}
</Select.Trigger>
<Select.Popper aria-label='Projects'>
<Select.List>
{projects.map((project) => (
<Select.Option key={project.id} value={project.id}>
{project.name}
</Select.Option>
))}
</Select.Popper>
</>
);
}}
</Select.List>
</Select.Popper>
</>
)}
</Select>

{/* Pattern 3: Select with in wMax in Select Trigger and ellipsis in Link trigger */}
<Select
value={deviceValue}
onChange={setDeviceValue}
placeholder={placeholder}

>
<Select.Trigger
tag={LinkTrigger}
wMax={ellipsisW}
active={active}
disabled={disabled}
loading={loading}
color={color}
size={size}
>
{renderAddon(showAddonLeft, addonLeftType)}
<LinkTrigger.Text
fontWeight={600}
{...ellipsis}
ellipsis:observeChildrenMutations
>
{deviceValue}
</LinkTrigger.Text>
{renderAddon(showAddonRight, addonRightType)}
</Select.Trigger>
<Select.Popper aria-label=''>
<Select.List>
{devices.map((option) => (
<Select.Option value={option.value} key={option.value}>
{option.children}
</Select.Option>
))}
</Select.List>
</Select.Popper>
</Select>
</Flex>
);
};
Expand All @@ -82,21 +188,20 @@ const devices = ['Desktop', 'Mobile', 'Tablet'].map((item) => ({
}));

const projects: Project[] = [
{
id: 1,
name: 'Semrush',
},
{ id: 1, name: 'Semrush' },
{ id: 2, name: 'Google' },
];

export const linkTriggerSelectExampleProps: LinkTriggerSelectDDMenuExample = {
size: 300,
active: undefined,
empty: undefined,
placeholder: undefined,
disabled: undefined,
loading: undefined,
placeholder: 'Select SEO option',
color: undefined,
showAddonLeft: false,
showAddonRight: false,
addonLeftType: 'icon',
addonRightType: 'icon',
ellipsis: { ellipsis: true },
w: 140,
};

Demo.defaultProps = linkTriggerSelectExampleProps;
Expand Down
37 changes: 32 additions & 5 deletions stories/components/base-trigger/tests/link-trigger.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,11 +42,11 @@ export const Base: StoryObj<typeof defaultLinkTriggerProps> = {
},
addonLeftType: {
control: { type: 'select' },
options: ['icon', 'badge', 'counter', 'spin'],
options: ['icon', 'badge', 'counter', 'spin', 'flag', 'tag'],
},
addonRightType: {
control: { type: 'select' },
options: ['icon', 'badge', 'counter', 'spin'],
options: ['icon', 'badge', 'counter', 'spin', 'flag', 'tag'],
},
merged: {
control: { type: 'boolean' },
Expand Down Expand Up @@ -92,7 +92,34 @@ export const Addon: StoryObj<typeof linkTriggerWithAddonExampleProps> = {

export const Select: StoryObj<typeof linkTriggerSelectExampleProps> = {
render: SelectExample,
argTypes: sharedArgTypes,
argTypes: {
...sharedArgTypes,
showAddonLeft: {
control: { type: 'boolean' },
},
showAddonRight: {
control: { type: 'boolean' },
},
addonLeftType: {
control: { type: 'select' },
options: ['icon', 'badge', 'counter', 'flag', 'tag'],
},
addonRightType: {
control: { type: 'select' },
options: ['icon', 'badge', 'counter', 'flag', 'tag'],
},
w: {
control: { type: 'number' },
},
ellipsis: {
control: { type: 'select' },
options: ['false', 'true'],
mapping: {
false: { ellipsis: false },
true: { ellipsis: true },
},
},
},
args: linkTriggerSelectExampleProps,
};

Expand All @@ -102,11 +129,11 @@ export const DifferentSizes: StoryObj<typeof defaultLinkTriggerSizesProps> = {
...sharedArgTypes,
addonLeft: {
control: { type: 'select' },
options: ['icon', 'badge', 'counter', 'spin'],
options: ['icon', 'badge', 'counter', 'spin', 'flag', 'tag'],
},
addonRight: {
control: { type: 'select' },
options: ['icon', 'badge', 'counter', 'spin', 'none'],
options: ['icon', 'badge', 'counter', 'spin', 'flag', 'none'],
},
ellipsis: { control: { type: 'boolean' } },
},
Expand Down
Loading