Skip to content
Open
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
2 changes: 1 addition & 1 deletion semcore/button/src/component/ButtonLink/ButtonLink.type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import type { NSText } from '@semcore/typography';
import type { NSButton } from '../Button/Button.type';

declare namespace NSButtonLink {
type Props = Intergalactic.InternalTypings.EfficientOmit<NSLink.Props, 'enableVisited'> & {
type Props = Intergalactic.InternalTypings.EfficientOmit<NSLink.Props, 'enableVisited' | 'use' | 'theme'> & {
/**
* Button link type
* @default primary
Expand Down
58 changes: 37 additions & 21 deletions semcore/side-panel/__tests__/side-panel.browser-test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,11 @@ export const locators = {
header: (page: Page) => page.locator('[data-ui-name="SidePanel.Header"]'),
body: (page: Page) => page.locator('[data-ui-name="SidePanel.Body"]'),
footer: (page: Page) => page.locator('[data-ui-name="SidePanel.Footer"]'),
title: (page: Page) => page.locator('h6[data-ui-name="SidePanel.Title"]'),
back: (page: Page) => page.locator('[data-ui-name="SidePanel.Back"]'),
// Title renders as h6 by default, but examples may override the tag (e.g. tag={Flex}),
// so the locators must not depend on the rendered tag name.
title: (page: Page) => page.locator('[data-ui-name="SidePanel.Title"]'),
backButton: (page: Page) => page.locator('[data-ui-name="SidePanel.Title"] [data-ui-name="ButtonLink"]'),
titleText: (page: Page) => page.locator('[data-ui-name="SidePanel.Title"] [data-ui-name="Text"]'),
dialog: (page: Page) => page.getByRole('dialog'),
hint: (page: Page) => page.locator('[data-ui-name="Hint"]'),

Expand Down Expand Up @@ -88,9 +91,11 @@ test.describe(`${TAG.VISUAL} `, () => {
await expect(locators.body(page)).toHaveCSS('padding-bottom', '16px');
});

await test.step('Verify body styles', async () => {
await expect(locators.footer(page)).toHaveCSS('padding-top', '8px');
await test.step('Verify footer styles', async () => {
await expect(locators.footer(page)).toHaveCSS('padding-top', '8px');
await expect(locators.footer(page)).toHaveCSS('padding-bottom', '8px');
await expect(locators.footer(page)).toHaveCSS('padding-left', '24px');
await expect(locators.footer(page)).toHaveCSS('padding-right', '24px');
});

const box = await locators.title(page).boundingBox();
Expand Down Expand Up @@ -196,13 +201,15 @@ test.describe(`${TAG.VISUAL} `, () => {
await expect(locators.button(page, 'Close')).toBeFocused();
await locators.hint(page).filter({ hasText: 'Close' }).waitFor({ state: 'visible' });

const title = locators.title(page);
await expect(title).toHaveText(titleText);
await expect(locators.title(page)).toHaveText(titleText);

// Ellipsis lives on the Text inside SidePanel.Title, the title itself has ellipsis={false}.
const titleTextNode = locators.titleText(page);
await expect.poll(async () => {
return title.evaluate((el) => el.scrollWidth > el.clientWidth);
return titleTextNode.evaluate((el) => el.scrollWidth > el.clientWidth);
}).toBe(true);

await title.hover();
await titleTextNode.hover();
await locators.hint(page).filter({ hasText: titleText }).waitFor({ state: 'visible' });
await page.waitForFunction((expectedText) => {
const titleHint = Array.from(document.querySelectorAll<HTMLElement>('[data-ui-name="Hint"]'))
Expand All @@ -214,35 +221,44 @@ test.describe(`${TAG.VISUAL} `, () => {
await expect(page).toHaveScreenshot({ maxDiffPixelRatio: 0.01 });
});

test('Verify SidePanel.Back with long text truncates via ellipsis', {
test('Verify back button stays intact while long title text truncates via ellipsis', {
tag: [TAG.PRIORITY_HIGH, '@side-panel', '@ellipsis', '@button'],
}, async ({ page }) => {
await loadPage(
page,
'stories/components/side-panel/tests/examples/side-panel-additional-states.tsx',
'en',
{
backText: 'This is a very long Back navigation label that must be truncated',
backWMax: 120,
ellipsisTitle: true,
withFooter: true,
animationsDisabled: true,
},
);

await page.keyboard.press('Tab');
await page.keyboard.press('Enter');
await locators.back(page).waitFor({ state: 'visible' });
await locators.backButton(page).waitFor({ state: 'visible' });

await test.step('Back container width is constrained by wMax', async () => {
const backBox = await locators.back(page).boundingBox();
expect(backBox!.width).toBeLessThanOrEqual(121);
await test.step('Back control is a single icon-only ButtonLink inside the title', async () => {
await expect(locators.backButton(page)).toHaveCount(1);
await expect(locators.backButton(page).locator('[data-ui-name="ButtonLink.Text"]'))
.toHaveCount(0);
});

await test.step('Back inner text node is actually truncated', async () => {
const textNode = locators.back(page).locator('[data-ui-name="ButtonLink.Text"]');
const isTruncated = await textNode.evaluate(
await test.step('Title text is truncated', async () => {
await expect.poll(async () => {
return locators.titleText(page).evaluate((el) => el.scrollWidth > el.clientWidth);
}).toBe(true);
});

await test.step('Back button keeps its full width and is not squeezed by the title', async () => {
const backBox = await locators.backButton(page).boundingBox();
expect(backBox!.width).toBeGreaterThan(0);

const isBackTruncated = await locators.backButton(page).evaluate(
(el) => el.scrollWidth > el.clientWidth,
);
expect(isTruncated).toBe(true);
expect(isBackTruncated).toBe(false);
});
});
});
Expand Down Expand Up @@ -274,7 +290,7 @@ test.describe(`${TAG.FUNCTIONAL} `, () => {

await test.step('Verify focus orted and is looped inside side panel', async () => {
await page.keyboard.press('Tab');
await expect(locators.back(page)).toBeFocused();
await expect(locators.backButton(page)).toBeFocused();

await page.keyboard.press('Tab');
await expect(footerButtons.first()).toBeFocused();
Expand All @@ -284,7 +300,7 @@ test.describe(`${TAG.FUNCTIONAL} `, () => {
});

await test.step('Verify closed by ESC when Close is not focused', async () => {
await expect(locators.back(page)).toBeFocused();
await expect(locators.backButton(page)).toBeFocused();
await page.keyboard.press('Escape');
await locators.dialog(page).waitFor({ state: 'hidden' });
await expect(page.getByRole('button')).toBeFocused();
Expand Down
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.
2 changes: 1 addition & 1 deletion semcore/side-panel/src/SidePanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -273,7 +273,7 @@ function Header(
const SHeader = Root;
const { Children, styles, title } = props;
return sstyled(styles)(
<SHeader render={Box} tag='header'>
<SHeader render={Flex} tag='header'>
{title && <SidePanel.Title children={title} />}
<Children />
</SHeader>,
Expand Down
3 changes: 3 additions & 0 deletions semcore/side-panel/src/SidePanel.type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,9 @@ declare namespace NSSidePanel {

type Component = Intergalactic.Component<'div', Props, Ctx> & {
Header: Header.Component;
/**
* @deprecated. Use ButtonLink instead.
*/
Back: Back.Component;
Body: Body.Component;
Footer: Footer.Component;
Expand Down
34 changes: 17 additions & 17 deletions semcore/side-panel/src/style/side-panel.shadow.css
Original file line number Diff line number Diff line change
Expand Up @@ -61,40 +61,40 @@ SClose {
position: absolute;
z-index: 1;
right: var(--intergalactic-spacing-2x, 8px);
top: var(--intergalactic-spacing-2x, 8px);

& SAddon:only-child {
margin-left: calc(var(--intergalactic-spacing-2x, 8px) - 1px);
margin-right: calc(var(--intergalactic-spacing-2x, 8px) - 1px);
}
top: var(--intergalactic-spacing-1x, 4px);
}

SBack {
max-width: 100%;
}

SHeader {
padding-left: var(--intergalactic-spacing-6x, 24px);
margin-bottom: var(--intergalactic-spacing-1x, 4px);
padding-right: calc(var(--intergalactic-spacing-10x, 40px) + var(--intergalactic-spacing-4x, 16px));
margin-top: var(--intergalactic-spacing-1x, 4px);
padding-left: var(--intergalactic-spacing-content-padding-xlarge-extended, 24px);
Comment thread
Valeria-Zimnitskaya marked this conversation as resolved.
padding-bottom: var(--intergalactic-spacing-content-padding-xsmall, 4px);
padding-right: var(--intergalactic-spacing-14x, 56px);
padding-top: var(--intergalactic-spacing-content-padding-xsmall, 4px);
min-height: calc(var(--intergalactic-spacing-10x, 40px) + var(--intergalactic-spacing-3x, 12px));
box-shadow: inset 0 -1px 0 var(--intergalactic-border-primary, oklch(0.137 0.026 175.7 / 0.161));
border-bottom: 1px solid var(--intergalactic-border-primary, oklch(0.137 0.026 175.7 / 0.161));
box-sizing: border-box;
flex-shrink: 0;
flex-direction: column;
justify-content: center;
Comment thread
Valeria-Zimnitskaya marked this conversation as resolved.
}

SFooter {
min-height: 44px;
box-shadow: inset 0 1px 0 var(--intergalactic-border-primary, oklch(0.137 0.026 175.7 / 0.161));
box-sizing: border-box;
padding: var(--intergalactic-spacing-content-padding-small, 8px) var(--intergalactic-spacing-content-padding-xlarge-extended, 24px);
justify-content: center;
align-items: center;
gap: var(--intergalactic-spacing-content-gap-xlarge, 12px);
border-top: 1px solid var(--intergalactic-border-primary, oklch(0.137 0.026 175.7 / 0.161));
}

SBody {
height: 100%;
overflow: auto;
padding-left: var(--intergalactic-spacing-6x, 24px);
padding-bottom:var(--intergalactic-spacing-4x, 16px);
padding-right: var(--intergalactic-spacing-6x, 24px);
padding-top: var(--intergalactic-spacing-4x, 16px);
padding-left: var(--intergalactic-spacing-content-padding-xlarge-extended, 24px);
padding-bottom:var(--intergalactic-spacing-content-padding-large, 16px);
padding-right: var(--intergalactic-spacing-content-padding-xlarge-extended, 24px);
padding-top: var(--intergalactic-spacing-content-padding-large, 16px);
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ const Demo = (props: NSSidePanel.Props) => {
<SidePanel.Overlay>
<SidePanel.Panel>
<SidePanel.Header>
<SidePanel.Title mt={3}>Taking the Stage</SidePanel.Title>
<SidePanel.Title>Taking the Stage</SidePanel.Title>
</SidePanel.Header>
<SidePanel.Body>
<Text size={300} tag='p'>
Expand Down
16 changes: 10 additions & 6 deletions stories/components/side-panel/docs/examples/advanced_example.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import Button from '@semcore/ui/button';
import ArrowLeft from '@semcore/icon/ArrowLeft/m';
import { Flex } from '@semcore/ui/base-components';
import Button, { ButtonLink } from '@semcore/ui/button';
import SidePanel from '@semcore/ui/side-panel';
import type { NSSidePanel } from '@semcore/ui/side-panel';
import React from 'react';
Expand All @@ -17,14 +19,16 @@ const Demo = (props: NSSidePanel.Props) => {
closable={props.closable}
disablePreventScroll={props.disablePreventScroll}
>
<SidePanel.Header h='64px'>
<SidePanel.Back>Go to Tool Name</SidePanel.Back>
<SidePanel.Title mt={3}>SidePanel title</SidePanel.Title>
<SidePanel.Header>
<SidePanel.Title ellipsis={false} tag={Flex} alignItems='start'>
<ButtonLink use='secondary' size={300} addonLeft={ArrowLeft} mr={2} title='Back to {Step}' />
SidePanel title
</SidePanel.Title>
</SidePanel.Header>
<SidePanel.Body>Content</SidePanel.Body>
<SidePanel.Footer justifyContent='center' pt={2} h='80px'>
<SidePanel.Footer>
<Button size='l' use='primary'>Got it!</Button>
<Button size='l' ml={2}>Cancel</Button>
<Button size='l'>Cancel</Button>
</SidePanel.Footer>
</SidePanel>
</>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ const Demo = (props: NSSidePanel.Props) => {
disablePreventScroll={props.disablePreventScroll}
>
<SidePanel.Header>
<SidePanel.Title mt={3}>SidePanel Title</SidePanel.Title>
<SidePanel.Title>SidePanel Title</SidePanel.Title>
</SidePanel.Header>
<SidePanel.Body>
<Text size={300} tag='p'>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ const Demo = (props: NSSidePanel.Props) => {
>
<SidePanel.Panel aria-label='SidePanel example' mt={20}>
<SidePanel.Header>
<SidePanel.Title mt={3}>SidePanel Title</SidePanel.Title>
<SidePanel.Title>SidePanel Title</SidePanel.Title>
</SidePanel.Header>
<SidePanel.Body>
<Text size={300} tag='p'>
Expand Down
2 changes: 1 addition & 1 deletion stories/components/side-panel/docs/examples/portals.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ const Demo = () => {
<SidePanel visible={visible} onClose={() => setVisible(false)} disablePortal>
<SidePanel.Panel aria-label='Taking the stage' mt={20}>
<SidePanel.Header>
<SidePanel.Title mt={3}>SidePanel Title</SidePanel.Title>
<SidePanel.Title>SidePanel Title</SidePanel.Title>
</SidePanel.Header>
<SidePanel.Body>
<Text size={300} tag='p'>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import Button from '@semcore/ui/button';
import ArrowLeft from '@semcore/icon/ArrowLeft/m';
import Button, { ButtonLink } from '@semcore/ui/button';
import SidePanel from '@semcore/ui/side-panel';
import { Text } from '@semcore/ui/typography';
import React from 'react';
Expand All @@ -20,9 +21,8 @@ export default function Demo() {
<SidePanel.Close />

<SidePanel.Header>
<SidePanel.Back>Back</SidePanel.Back>

<SidePanel.Title ellipsis={false}>
<ButtonLink color='text-hint' size={100} addonLeft={ArrowLeft} mr={2} />
<Text>Static text: </Text>
<Text use='secondary' ellipsis={withEllipsis} w={80}>{dynamicText}</Text>
</SidePanel.Title>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import ArrowLeft from '@semcore/icon/ArrowLeft/m';
import FileExportM from '@semcore/icon/FileExport/m';
import { Box, Flex } from '@semcore/ui/base-components';
import Button from '@semcore/ui/button';
import Button, { ButtonLink } from '@semcore/ui/button';
import SidePanel from '@semcore/ui/side-panel';
import Tooltip from '@semcore/ui/tooltip';
import { Text } from '@semcore/ui/typography';
import React from 'react';

export type SidePanelDemoProps = {
Expand All @@ -12,7 +14,6 @@ export type SidePanelDemoProps = {
withAdditionalHeaderContent?: boolean;
withTooltipInBody?: boolean;
withFooter?: boolean;
backText?: string;
backWMax?: number;
animationsDisabled?: boolean;
forcedAdvancedMode?: boolean;
Expand All @@ -25,22 +26,26 @@ export const defaultSidePanelDemoProps: SidePanelDemoProps = {
withAdditionalHeaderContent: false,
withTooltipInBody: false,
withFooter: false,
backText: 'Go to Tool Name',
animationsDisabled: false,
forcedAdvancedMode: false,
};

const Demo = (props: SidePanelDemoProps) => {
const [visible, setVisible] = React.useState(false);
const backText = props.backText ?? defaultSidePanelDemoProps.backText;
const ellipsisProps = {
'ellipsis': props.ellipsisTitle,
'ellipsis:maxLine': props.ellipsisMaxLine && props.ellipsisMaxLine > 1 ? props.ellipsisMaxLine : undefined,
};

const content = (
<>
{props.withClose && <SidePanel.Close />}
<SidePanel.Header>
<SidePanel.Back wMax={props.backWMax}>{backText}</SidePanel.Back>
<SidePanel.Title w={100} ellipsis={props.ellipsisTitle} ellipsis:maxLine={props.ellipsisMaxLine}>
Heading 6, 16px Heading 6, 16px
<SidePanel.Title ellipsis={false}>
<ButtonLink color='text-hint' size={100} addonLeft={ArrowLeft} mr={2} />
<Text w={100} {...ellipsisProps}>
Heading 6, 16px Heading 6, 16px
</Text>
</SidePanel.Title>
{props.withAdditionalHeaderContent && (
<Flex direction='column'>
Expand All @@ -61,9 +66,9 @@ const Demo = (props: SidePanelDemoProps) => {
)}
</SidePanel.Body>
{props.withFooter && (
<SidePanel.Footer justifyContent='center' pt={2}>
<SidePanel.Footer>
<Button use='primary'>Primary</Button>
<Button ml={2}>Cancel</Button>
<Button>Cancel</Button>
</SidePanel.Footer>
)}
</>
Expand Down
1 change: 0 additions & 1 deletion stories/components/side-panel/tests/side-panel.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ export const AdditionalStates: StoryObj<typeof defaultSidePanelDemoProps> = {
withAdditionalHeaderContent: { control: { type: 'boolean' } },
withTooltipInBody: { control: { type: 'boolean' } },
withFooter: { control: { type: 'boolean' } },
backText: { control: { type: 'text' } },
backWMax: { control: { type: 'number' } },
},
};
Expand Down
Loading
Loading