From 32f9068fd1ed0ffb28f99d9a9154e2bf2011b2b4 Mon Sep 17 00:00:00 2001 From: Ihor Romanchuk Date: Fri, 4 Sep 2026 17:31:31 +0200 Subject: [PATCH 1/3] feat(design-system): add "DsIllustration" component [AR-77735] --- .changeset/great-papers-yawn.md | 5 + CONTEXT.md | 14 ++ .../__snapshots__/ds-illustration.docs.snap | 9 ++ .../ds-illustration.browser.test.tsx | 16 +++ .../ds-illustration/ds-illustration.figma.ts | 34 +++++ .../ds-illustration.module.scss | 6 + .../ds-illustration.stories.module.scss | 17 +++ .../ds-illustration.stories.tsx | 55 ++++++++ .../ds-illustration/ds-illustration.tsx | 29 ++++ .../ds-illustration/ds-illustration.types.ts | 45 ++++++ .../illustrations/configuration.tsx | 51 +++++++ .../ds-illustration/illustrations/device.tsx | 133 ++++++++++++++++++ .../ds-illustration/illustrations/done.tsx | 107 ++++++++++++++ .../illustrations/empty-inbox.tsx | 53 +++++++ .../ds-illustration/illustrations/error.tsx | 86 +++++++++++ .../illustrations/go-filter.tsx | 51 +++++++ .../illustrations/illustration-clouds.tsx | 10 ++ .../ds-illustration/illustrations/index.ts | 39 +++++ .../illustrations/no-connection.tsx | 54 +++++++ .../illustrations/no-credit-card.tsx | 48 +++++++ .../illustrations/no-device.tsx | 61 ++++++++ .../illustrations/no-documents.tsx | 52 +++++++ .../ds-illustration/illustrations/no-gps.tsx | 57 ++++++++ .../illustrations/no-images.tsx | 82 +++++++++++ .../illustrations/no-items-cart.tsx | 86 +++++++++++ .../illustrations/no-messages.tsx | 51 +++++++ .../illustrations/no-search-result.tsx | 76 ++++++++++ .../illustrations/no-tasks.tsx | 61 ++++++++ .../ds-illustration/illustrations/site.tsx | 80 +++++++++++ .../src/components/ds-illustration/index.ts | 3 + packages/design-system/src/index.ts | 1 + .../storybook/docs-snippets.docs.test.ts | 1 + 32 files changed, 1473 insertions(+) create mode 100644 .changeset/great-papers-yawn.md create mode 100644 packages/design-system/src/components/ds-illustration/__tests__/__snapshots__/ds-illustration.docs.snap create mode 100644 packages/design-system/src/components/ds-illustration/__tests__/ds-illustration.browser.test.tsx create mode 100644 packages/design-system/src/components/ds-illustration/ds-illustration.figma.ts create mode 100644 packages/design-system/src/components/ds-illustration/ds-illustration.module.scss create mode 100644 packages/design-system/src/components/ds-illustration/ds-illustration.stories.module.scss create mode 100644 packages/design-system/src/components/ds-illustration/ds-illustration.stories.tsx create mode 100644 packages/design-system/src/components/ds-illustration/ds-illustration.tsx create mode 100644 packages/design-system/src/components/ds-illustration/ds-illustration.types.ts create mode 100644 packages/design-system/src/components/ds-illustration/illustrations/configuration.tsx create mode 100644 packages/design-system/src/components/ds-illustration/illustrations/device.tsx create mode 100644 packages/design-system/src/components/ds-illustration/illustrations/done.tsx create mode 100644 packages/design-system/src/components/ds-illustration/illustrations/empty-inbox.tsx create mode 100644 packages/design-system/src/components/ds-illustration/illustrations/error.tsx create mode 100644 packages/design-system/src/components/ds-illustration/illustrations/go-filter.tsx create mode 100644 packages/design-system/src/components/ds-illustration/illustrations/illustration-clouds.tsx create mode 100644 packages/design-system/src/components/ds-illustration/illustrations/index.ts create mode 100644 packages/design-system/src/components/ds-illustration/illustrations/no-connection.tsx create mode 100644 packages/design-system/src/components/ds-illustration/illustrations/no-credit-card.tsx create mode 100644 packages/design-system/src/components/ds-illustration/illustrations/no-device.tsx create mode 100644 packages/design-system/src/components/ds-illustration/illustrations/no-documents.tsx create mode 100644 packages/design-system/src/components/ds-illustration/illustrations/no-gps.tsx create mode 100644 packages/design-system/src/components/ds-illustration/illustrations/no-images.tsx create mode 100644 packages/design-system/src/components/ds-illustration/illustrations/no-items-cart.tsx create mode 100644 packages/design-system/src/components/ds-illustration/illustrations/no-messages.tsx create mode 100644 packages/design-system/src/components/ds-illustration/illustrations/no-search-result.tsx create mode 100644 packages/design-system/src/components/ds-illustration/illustrations/no-tasks.tsx create mode 100644 packages/design-system/src/components/ds-illustration/illustrations/site.tsx create mode 100644 packages/design-system/src/components/ds-illustration/index.ts diff --git a/.changeset/great-papers-yawn.md b/.changeset/great-papers-yawn.md new file mode 100644 index 000000000..076636aeb --- /dev/null +++ b/.changeset/great-papers-yawn.md @@ -0,0 +1,5 @@ +--- +'@drivenets/design-system': patch +--- + +Add `DsIllustration` component diff --git a/CONTEXT.md b/CONTEXT.md index 8a88f7e2a..f06abb338 100644 --- a/CONTEXT.md +++ b/CONTEXT.md @@ -48,6 +48,14 @@ _Avoid_: sub props, child props Content a **Component** renders when it has genuinely zero data items. Distinct from a loading/skeleton state and from a transient frame where a virtualized body has not yet produced rows. Gate it on data count, never on rendered/virtual row count. _Avoid_: no-data view, blank state, placeholder +**Icon**: +A monochrome glyph rendered by `DsIcon` — Material Symbols or a `special-*` custom SVG — at 16–32px, tinted with `currentColor`. +_Avoid_: rich icon, state icon (those tiles are **Illustrations**) + +**Illustration**: +A 250×200 colorful empty-screen tile rendered by `DsIllustration` (clouds plus a scene). Not an **Icon**. +_Avoid_: rich icon, DAP_State Icon (as a code name), Cluster NE (for the Device tile) + **Successor component**: A new **Component** folder that supersedes an older one while the old export remains (often `ds-{name}-v2` or `ds-{name}-v3`). Mark the old API `@deprecated` in types and stories; prefer the successor in new code. _Avoid_: replacement, v2 component (as a synonym for the pattern name) @@ -136,6 +144,7 @@ _Avoid_: Scrollbar gutter, placeholder, gutter cell - A **Successor component** supersedes a **Deprecated component**; both may ship until consumers migrate - **Tokens** flow from design into SCSS; **Components** consume tokens, not raw hex from Figma in new work - A **Component** shows its **Empty state** only when it has zero data items; a virtualized body gates **Empty state** on data count, never on the count of currently rendered (virtual) rows +- An **Icon** is not an **Illustration**; `DsIcon` never renders empty-screen tiles - **Changesets** attach to package releases; a new **Component** or breaking API change typically needs one - A **Workspace page** has one header and one **Workspace body**; the body may combine **Side menu panel**, **Left side panel**, **Content area**, and **Right side panel** - **Side menu panel** and **Left side panel** differ: only **Left side panel** toggles **Content area** horizontal margins (24px vs 40px) @@ -172,6 +181,9 @@ _Avoid_: Scrollbar gutter, placeholder, gutter cell > **Dev:** "When I make the expander column wider, does that also size the nested details columns?" > **Domain expert:** "No — that width belongs to the expander **Utility column**. Nested details are consumer **Leaf columns**." +> **Dev:** "Should Cluster NE from the empty-screen tile go on `DsIcon` as `rich-cluster-ne`?" +> **Domain expert:** "No — that's an **Illustration**, variant `device`. `special-cluster-ne` is the **Icon**." + > **Dev:** "Should a long column title wrap onto two lines in the header?" > **Domain expert:** "No — that's a **Header label**. It stays one line with an ellipsis; don't call it a column title." @@ -182,3 +194,5 @@ _Avoid_: Scrollbar gutter, placeholder, gutter cell - "placeholder" in header layout meant **Scrollbar spacer**, not TanStack `header.isPlaceholder` (spanning-cell hole) and not **Empty state**. - "Scrollbar gutter" meant CSS reservation on `thead`; the structure is a **Scrollbar spacer**. - "Expandable column width" was used to mean nested details columns — resolved: that width belongs to the expander **Utility column**. +- "rich icon" / "state icon" / Figma DAP_State Icon — resolved: **Illustration**. +- "Cluster NE" on the linked empty-screen instance — resolved: **Illustration** variant `device`; `special-cluster-ne` remains the **Icon**. diff --git a/packages/design-system/src/components/ds-illustration/__tests__/__snapshots__/ds-illustration.docs.snap b/packages/design-system/src/components/ds-illustration/__tests__/__snapshots__/ds-illustration.docs.snap new file mode 100644 index 000000000..0a5199200 --- /dev/null +++ b/packages/design-system/src/components/ds-illustration/__tests__/__snapshots__/ds-illustration.docs.snap @@ -0,0 +1,9 @@ +# DsIllustration docs snippets + +## Default + +### Show code + + +### MCP manifest +const Default = () => ; \ No newline at end of file diff --git a/packages/design-system/src/components/ds-illustration/__tests__/ds-illustration.browser.test.tsx b/packages/design-system/src/components/ds-illustration/__tests__/ds-illustration.browser.test.tsx new file mode 100644 index 000000000..e1c491a05 --- /dev/null +++ b/packages/design-system/src/components/ds-illustration/__tests__/ds-illustration.browser.test.tsx @@ -0,0 +1,16 @@ +import { describe, expect, it } from 'vitest'; +import { page } from 'vitest/browser'; +import { DsIllustration } from '../index'; + +describe('DsIllustration', () => { + it('is decorative by default and exposes a label when asked', async () => { + const { container, rerender } = await page.render(); + const svg = container.querySelector('svg'); + + expect(svg).toHaveAttribute('aria-hidden', 'true'); + + await rerender(); + + await expect.element(page.getByLabelText('Device scene')).toBeVisible(); + }); +}); diff --git a/packages/design-system/src/components/ds-illustration/ds-illustration.figma.ts b/packages/design-system/src/components/ds-illustration/ds-illustration.figma.ts new file mode 100644 index 000000000..c9f483a0b --- /dev/null +++ b/packages/design-system/src/components/ds-illustration/ds-illustration.figma.ts @@ -0,0 +1,34 @@ +// url=https://www.figma.com/design/nha3m67y7S57cHCSuQO2gp/DAP-Design-System-1.2?node-id=27983-34300 +// source=https://github.com/drivenets/design-system/tree/main/packages/design-system/src/components/ds-illustration +// component=DsIllustration +import figma from 'figma'; + +const instance = figma.selectedInstance; + +const variant = + instance.getEnum('Property 1', { + Done: 'done', + EmptyInbox: 'empty-inbox', + Error: 'error', + NoConnection: 'no-connection', + NoCreditCard: 'no-credit-card', + NoDocuments: 'no-documents', + NoGPS: 'no-gps', + NoImages: 'no-images', + NoItemsCart: 'no-items-cart', + NoMessages: 'no-messages', + NoSearchResult: 'no-search-result', + NoTasks: 'no-tasks', + 'No Device': 'no-device', + 'Go Filter': 'go-filter', + Device: 'device', + Site: 'site', + configuration: 'configuration', + }) ?? 'no-tasks'; + +export default { + example: figma.code``, + imports: ["import { DsIllustration } from '@drivenets/design-system';"], + id: 'ds-illustration', + metadata: { nestable: true }, +} satisfies figma.Template; diff --git a/packages/design-system/src/components/ds-illustration/ds-illustration.module.scss b/packages/design-system/src/components/ds-illustration/ds-illustration.module.scss new file mode 100644 index 000000000..cb5d03dc3 --- /dev/null +++ b/packages/design-system/src/components/ds-illustration/ds-illustration.module.scss @@ -0,0 +1,6 @@ +.root { + display: block; + width: 250px; + height: 200px; + max-width: 100%; +} diff --git a/packages/design-system/src/components/ds-illustration/ds-illustration.stories.module.scss b/packages/design-system/src/components/ds-illustration/ds-illustration.stories.module.scss new file mode 100644 index 000000000..d854c0697 --- /dev/null +++ b/packages/design-system/src/components/ds-illustration/ds-illustration.stories.module.scss @@ -0,0 +1,17 @@ +.showcase { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(250px, 1fr)); + gap: var(--standard); + padding: var(--standard); +} + +.tile { + display: flex; + flex-direction: column; + align-items: center; + gap: var(--xs); +} + +.label { + color: var(--font-secondary); +} diff --git a/packages/design-system/src/components/ds-illustration/ds-illustration.stories.tsx b/packages/design-system/src/components/ds-illustration/ds-illustration.stories.tsx new file mode 100644 index 000000000..3bf7941f2 --- /dev/null +++ b/packages/design-system/src/components/ds-illustration/ds-illustration.stories.tsx @@ -0,0 +1,55 @@ +import type { Meta, StoryObj } from '@storybook/react-vite'; +import { DsTypography } from '../ds-typography'; +import { DsIllustration } from './index'; +import { dsIllustrationVariants } from './ds-illustration.types'; +import styles from './ds-illustration.stories.module.scss'; + +const meta: Meta = { + title: 'Components/Illustration', + component: DsIllustration, + parameters: { layout: 'centered' }, + argTypes: { + variant: { control: 'select', options: dsIllustrationVariants }, + className: { table: { disable: true } }, + style: { table: { disable: true } }, + ref: { table: { disable: true } }, + 'aria-hidden': { table: { disable: true } }, + 'aria-label': { table: { disable: true } }, + }, +}; + +export default meta; +type Story = StoryObj; + +/** + * Default empty-screen tile (`no-tasks`). Swap `variant` for the other + * 250×200 scenes. Not an Icon — do not pass these names to `DsIcon`. + */ +export const Default: Story = { + args: { + variant: 'no-tasks', + }, +}; + +/** + * Every published tile. Visual catalog only — pick a `variant` in product code. + */ +export const Showcase: Story = { + tags: ['!manifest'], + parameters: { + layout: 'fullscreen', + docs: { canvas: { sourceState: 'none' } }, + }, + render: () => ( +
+ {dsIllustrationVariants.map((variant) => ( +
+ + + {variant} + +
+ ))} +
+ ), +}; diff --git a/packages/design-system/src/components/ds-illustration/ds-illustration.tsx b/packages/design-system/src/components/ds-illustration/ds-illustration.tsx new file mode 100644 index 000000000..78b2ab673 --- /dev/null +++ b/packages/design-system/src/components/ds-illustration/ds-illustration.tsx @@ -0,0 +1,29 @@ +import classNames from 'classnames'; +import styles from './ds-illustration.module.scss'; +import { illustrations } from './illustrations'; +import type { DsIllustrationProps } from './ds-illustration.types'; + +const DsIllustration = ({ + variant = 'no-tasks', + className, + style, + ref, + 'aria-hidden': ariaHidden = true, + 'aria-label': ariaLabel, +}: DsIllustrationProps) => { + const Svg = illustrations[variant]; + + return ( + + ); +}; + +DsIllustration.displayName = 'DsIllustration'; + +export default DsIllustration; diff --git a/packages/design-system/src/components/ds-illustration/ds-illustration.types.ts b/packages/design-system/src/components/ds-illustration/ds-illustration.types.ts new file mode 100644 index 000000000..661da713d --- /dev/null +++ b/packages/design-system/src/components/ds-illustration/ds-illustration.types.ts @@ -0,0 +1,45 @@ +import type { CSSProperties, Ref } from 'react'; + +export const dsIllustrationVariants = [ + 'configuration', + 'device', + 'done', + 'empty-inbox', + 'error', + 'go-filter', + 'no-connection', + 'no-credit-card', + 'no-device', + 'no-documents', + 'no-gps', + 'no-images', + 'no-items-cart', + 'no-messages', + 'no-search-result', + 'no-tasks', + 'site', +] as const; + +export type DsIllustrationVariant = (typeof dsIllustrationVariants)[number]; + +export interface DsIllustrationProps { + /** + * Which empty-screen tile to render. + * @default 'no-tasks' + */ + variant?: DsIllustrationVariant; + + className?: string; + + style?: CSSProperties; + + ref?: Ref; + + /** + * Decorative by default. Set `false` with `aria-label` when the tile is informative. + * @default true + */ + 'aria-hidden'?: boolean | 'true' | 'false'; + + 'aria-label'?: string; +} diff --git a/packages/design-system/src/components/ds-illustration/illustrations/configuration.tsx b/packages/design-system/src/components/ds-illustration/illustrations/configuration.tsx new file mode 100644 index 000000000..cf4f06329 --- /dev/null +++ b/packages/design-system/src/components/ds-illustration/illustrations/configuration.tsx @@ -0,0 +1,51 @@ +import type { FC, SVGProps } from 'react'; +import { IllustrationClouds } from './illustration-clouds'; + +export const ConfigurationIllustration: FC> = (props) => ( + + + + + + + + + + +); diff --git a/packages/design-system/src/components/ds-illustration/illustrations/device.tsx b/packages/design-system/src/components/ds-illustration/illustrations/device.tsx new file mode 100644 index 000000000..9140d39ff --- /dev/null +++ b/packages/design-system/src/components/ds-illustration/illustrations/device.tsx @@ -0,0 +1,133 @@ +import type { FC, SVGProps } from 'react'; +import { IllustrationClouds } from './illustration-clouds'; + +export const DeviceIllustration: FC> = (props) => ( + + + + + + + + + + + + + + + + + + + + + + +); diff --git a/packages/design-system/src/components/ds-illustration/illustrations/done.tsx b/packages/design-system/src/components/ds-illustration/illustrations/done.tsx new file mode 100644 index 000000000..2dfe3c9bc --- /dev/null +++ b/packages/design-system/src/components/ds-illustration/illustrations/done.tsx @@ -0,0 +1,107 @@ +import type { FC, SVGProps } from 'react'; +import { IllustrationClouds } from './illustration-clouds'; + +export const DoneIllustration: FC> = (props) => ( + + + + + + + + + + + + + + + + + +); diff --git a/packages/design-system/src/components/ds-illustration/illustrations/empty-inbox.tsx b/packages/design-system/src/components/ds-illustration/illustrations/empty-inbox.tsx new file mode 100644 index 000000000..3f1f099f2 --- /dev/null +++ b/packages/design-system/src/components/ds-illustration/illustrations/empty-inbox.tsx @@ -0,0 +1,53 @@ +import type { FC, SVGProps } from 'react'; +import { IllustrationClouds } from './illustration-clouds'; + +export const EmptyInboxIllustration: FC> = (props) => ( + + + + + + + + + + +); diff --git a/packages/design-system/src/components/ds-illustration/illustrations/error.tsx b/packages/design-system/src/components/ds-illustration/illustrations/error.tsx new file mode 100644 index 000000000..dd84f3693 --- /dev/null +++ b/packages/design-system/src/components/ds-illustration/illustrations/error.tsx @@ -0,0 +1,86 @@ +import type { FC, SVGProps } from 'react'; +import { IllustrationClouds } from './illustration-clouds'; + +export const ErrorIllustration: FC> = (props) => ( + + + + + + + + + + + + + + + + +); diff --git a/packages/design-system/src/components/ds-illustration/illustrations/go-filter.tsx b/packages/design-system/src/components/ds-illustration/illustrations/go-filter.tsx new file mode 100644 index 000000000..8217964c2 --- /dev/null +++ b/packages/design-system/src/components/ds-illustration/illustrations/go-filter.tsx @@ -0,0 +1,51 @@ +import type { FC, SVGProps } from 'react'; +import { IllustrationClouds } from './illustration-clouds'; + +export const GoFilterIllustration: FC> = (props) => ( + + + + + + + + + + + + + + + +); diff --git a/packages/design-system/src/components/ds-illustration/illustrations/illustration-clouds.tsx b/packages/design-system/src/components/ds-illustration/illustrations/illustration-clouds.tsx new file mode 100644 index 000000000..bee58de6e --- /dev/null +++ b/packages/design-system/src/components/ds-illustration/illustrations/illustration-clouds.tsx @@ -0,0 +1,10 @@ +import type { FC } from 'react'; + +export const IllustrationClouds: FC = () => ( + +); diff --git a/packages/design-system/src/components/ds-illustration/illustrations/index.ts b/packages/design-system/src/components/ds-illustration/illustrations/index.ts new file mode 100644 index 000000000..3dd828b1e --- /dev/null +++ b/packages/design-system/src/components/ds-illustration/illustrations/index.ts @@ -0,0 +1,39 @@ +import type { FC, SVGProps } from 'react'; +import type { DsIllustrationVariant } from '../ds-illustration.types'; +import { ConfigurationIllustration } from './configuration'; +import { DeviceIllustration } from './device'; +import { DoneIllustration } from './done'; +import { EmptyInboxIllustration } from './empty-inbox'; +import { ErrorIllustration } from './error'; +import { GoFilterIllustration } from './go-filter'; +import { NoConnectionIllustration } from './no-connection'; +import { NoCreditCardIllustration } from './no-credit-card'; +import { NoDeviceIllustration } from './no-device'; +import { NoDocumentsIllustration } from './no-documents'; +import { NoGpsIllustration } from './no-gps'; +import { NoImagesIllustration } from './no-images'; +import { NoItemsCartIllustration } from './no-items-cart'; +import { NoMessagesIllustration } from './no-messages'; +import { NoSearchResultIllustration } from './no-search-result'; +import { NoTasksIllustration } from './no-tasks'; +import { SiteIllustration } from './site'; + +export const illustrations: Record>> = Object.freeze({ + configuration: ConfigurationIllustration, + device: DeviceIllustration, + done: DoneIllustration, + 'empty-inbox': EmptyInboxIllustration, + error: ErrorIllustration, + 'go-filter': GoFilterIllustration, + 'no-connection': NoConnectionIllustration, + 'no-credit-card': NoCreditCardIllustration, + 'no-device': NoDeviceIllustration, + 'no-documents': NoDocumentsIllustration, + 'no-gps': NoGpsIllustration, + 'no-images': NoImagesIllustration, + 'no-items-cart': NoItemsCartIllustration, + 'no-messages': NoMessagesIllustration, + 'no-search-result': NoSearchResultIllustration, + 'no-tasks': NoTasksIllustration, + site: SiteIllustration, +}); diff --git a/packages/design-system/src/components/ds-illustration/illustrations/no-connection.tsx b/packages/design-system/src/components/ds-illustration/illustrations/no-connection.tsx new file mode 100644 index 000000000..95425fc0a --- /dev/null +++ b/packages/design-system/src/components/ds-illustration/illustrations/no-connection.tsx @@ -0,0 +1,54 @@ +import type { FC, SVGProps } from 'react'; +import { IllustrationClouds } from './illustration-clouds'; + +export const NoConnectionIllustration: FC> = (props) => ( + + + + + + + + + + + +); diff --git a/packages/design-system/src/components/ds-illustration/illustrations/no-credit-card.tsx b/packages/design-system/src/components/ds-illustration/illustrations/no-credit-card.tsx new file mode 100644 index 000000000..880a5e4cf --- /dev/null +++ b/packages/design-system/src/components/ds-illustration/illustrations/no-credit-card.tsx @@ -0,0 +1,48 @@ +import type { FC, SVGProps } from 'react'; +import { IllustrationClouds } from './illustration-clouds'; + +export const NoCreditCardIllustration: FC> = (props) => ( + + + + + + + + + + +); diff --git a/packages/design-system/src/components/ds-illustration/illustrations/no-device.tsx b/packages/design-system/src/components/ds-illustration/illustrations/no-device.tsx new file mode 100644 index 000000000..7818f48e7 --- /dev/null +++ b/packages/design-system/src/components/ds-illustration/illustrations/no-device.tsx @@ -0,0 +1,61 @@ +import type { FC, SVGProps } from 'react'; +import { IllustrationClouds } from './illustration-clouds'; + +export const NoDeviceIllustration: FC> = (props) => ( + + + + + + + + + + + + + + + + + + + + +); diff --git a/packages/design-system/src/components/ds-illustration/illustrations/no-documents.tsx b/packages/design-system/src/components/ds-illustration/illustrations/no-documents.tsx new file mode 100644 index 000000000..0d9035fba --- /dev/null +++ b/packages/design-system/src/components/ds-illustration/illustrations/no-documents.tsx @@ -0,0 +1,52 @@ +import type { FC, SVGProps } from 'react'; +import { IllustrationClouds } from './illustration-clouds'; + +export const NoDocumentsIllustration: FC> = (props) => ( + + + + + + + + + + +); diff --git a/packages/design-system/src/components/ds-illustration/illustrations/no-gps.tsx b/packages/design-system/src/components/ds-illustration/illustrations/no-gps.tsx new file mode 100644 index 000000000..53bb43882 --- /dev/null +++ b/packages/design-system/src/components/ds-illustration/illustrations/no-gps.tsx @@ -0,0 +1,57 @@ +import type { FC, SVGProps } from 'react'; +import { IllustrationClouds } from './illustration-clouds'; + +export const NoGpsIllustration: FC> = (props) => ( + + + + + + + + + + + +); diff --git a/packages/design-system/src/components/ds-illustration/illustrations/no-images.tsx b/packages/design-system/src/components/ds-illustration/illustrations/no-images.tsx new file mode 100644 index 000000000..63fb8223a --- /dev/null +++ b/packages/design-system/src/components/ds-illustration/illustrations/no-images.tsx @@ -0,0 +1,82 @@ +import type { FC, SVGProps } from 'react'; +import { IllustrationClouds } from './illustration-clouds'; + +export const NoImagesIllustration: FC> = (props) => ( + + + + + + + + + + + + + + +); diff --git a/packages/design-system/src/components/ds-illustration/illustrations/no-items-cart.tsx b/packages/design-system/src/components/ds-illustration/illustrations/no-items-cart.tsx new file mode 100644 index 000000000..12e43c3c4 --- /dev/null +++ b/packages/design-system/src/components/ds-illustration/illustrations/no-items-cart.tsx @@ -0,0 +1,86 @@ +import type { FC, SVGProps } from 'react'; +import { IllustrationClouds } from './illustration-clouds'; + +export const NoItemsCartIllustration: FC> = (props) => ( + + + + + + + + + + + + + + + + +); diff --git a/packages/design-system/src/components/ds-illustration/illustrations/no-messages.tsx b/packages/design-system/src/components/ds-illustration/illustrations/no-messages.tsx new file mode 100644 index 000000000..6343bc04b --- /dev/null +++ b/packages/design-system/src/components/ds-illustration/illustrations/no-messages.tsx @@ -0,0 +1,51 @@ +import type { FC, SVGProps } from 'react'; +import { IllustrationClouds } from './illustration-clouds'; + +export const NoMessagesIllustration: FC> = (props) => ( + + + + + + + + + +); diff --git a/packages/design-system/src/components/ds-illustration/illustrations/no-search-result.tsx b/packages/design-system/src/components/ds-illustration/illustrations/no-search-result.tsx new file mode 100644 index 000000000..1391358f2 --- /dev/null +++ b/packages/design-system/src/components/ds-illustration/illustrations/no-search-result.tsx @@ -0,0 +1,76 @@ +import type { FC, SVGProps } from 'react'; +import { IllustrationClouds } from './illustration-clouds'; + +export const NoSearchResultIllustration: FC> = (props) => ( + + + + + + + + + + + + + + + +); diff --git a/packages/design-system/src/components/ds-illustration/illustrations/no-tasks.tsx b/packages/design-system/src/components/ds-illustration/illustrations/no-tasks.tsx new file mode 100644 index 000000000..8727d6aab --- /dev/null +++ b/packages/design-system/src/components/ds-illustration/illustrations/no-tasks.tsx @@ -0,0 +1,61 @@ +import type { FC, SVGProps } from 'react'; +import { IllustrationClouds } from './illustration-clouds'; + +export const NoTasksIllustration: FC> = (props) => ( + + + + + + + + + + + +); diff --git a/packages/design-system/src/components/ds-illustration/illustrations/site.tsx b/packages/design-system/src/components/ds-illustration/illustrations/site.tsx new file mode 100644 index 000000000..66af78ec9 --- /dev/null +++ b/packages/design-system/src/components/ds-illustration/illustrations/site.tsx @@ -0,0 +1,80 @@ +import type { FC, SVGProps } from 'react'; +import { IllustrationClouds } from './illustration-clouds'; + +export const SiteIllustration: FC> = (props) => ( + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +); diff --git a/packages/design-system/src/components/ds-illustration/index.ts b/packages/design-system/src/components/ds-illustration/index.ts new file mode 100644 index 000000000..bab0559d0 --- /dev/null +++ b/packages/design-system/src/components/ds-illustration/index.ts @@ -0,0 +1,3 @@ +export { default as DsIllustration } from './ds-illustration'; +export type { DsIllustrationProps, DsIllustrationVariant } from './ds-illustration.types'; +export { dsIllustrationVariants } from './ds-illustration.types'; diff --git a/packages/design-system/src/index.ts b/packages/design-system/src/index.ts index 38e176239..912305c4d 100644 --- a/packages/design-system/src/index.ts +++ b/packages/design-system/src/index.ts @@ -30,6 +30,7 @@ export * from './components/ds-filter-status-icon'; export * from './components/ds-form-control'; export * from './components/ds-grid'; export * from './components/ds-icon'; +export * from './components/ds-illustration'; export * from './components/ds-key-value-pair'; export * from './components/ds-loader'; export * from './components/ds-main-menu'; diff --git a/packages/design-system/tests/storybook/docs-snippets.docs.test.ts b/packages/design-system/tests/storybook/docs-snippets.docs.test.ts index d56963214..816586f9e 100644 --- a/packages/design-system/tests/storybook/docs-snippets.docs.test.ts +++ b/packages/design-system/tests/storybook/docs-snippets.docs.test.ts @@ -29,6 +29,7 @@ const COMPONENTS = [ 'form-control', 'grid', 'icon', + 'illustration', 'key-value-pair', 'loader', 'main-menu', From de72e5148be3e4ed09718fb4b5a1a10165c1e479 Mon Sep 17 00:00:00 2001 From: Ihor Romanchuk Date: Fri, 4 Sep 2026 17:39:38 +0200 Subject: [PATCH 2/3] Use DsStack --- .../ds-illustration.stories.module.scss | 11 ----------- .../ds-illustration/ds-illustration.stories.tsx | 7 ++++--- 2 files changed, 4 insertions(+), 14 deletions(-) diff --git a/packages/design-system/src/components/ds-illustration/ds-illustration.stories.module.scss b/packages/design-system/src/components/ds-illustration/ds-illustration.stories.module.scss index d854c0697..f85e538d8 100644 --- a/packages/design-system/src/components/ds-illustration/ds-illustration.stories.module.scss +++ b/packages/design-system/src/components/ds-illustration/ds-illustration.stories.module.scss @@ -4,14 +4,3 @@ gap: var(--standard); padding: var(--standard); } - -.tile { - display: flex; - flex-direction: column; - align-items: center; - gap: var(--xs); -} - -.label { - color: var(--font-secondary); -} diff --git a/packages/design-system/src/components/ds-illustration/ds-illustration.stories.tsx b/packages/design-system/src/components/ds-illustration/ds-illustration.stories.tsx index 3bf7941f2..9fff17df0 100644 --- a/packages/design-system/src/components/ds-illustration/ds-illustration.stories.tsx +++ b/packages/design-system/src/components/ds-illustration/ds-illustration.stories.tsx @@ -3,6 +3,7 @@ import { DsTypography } from '../ds-typography'; import { DsIllustration } from './index'; import { dsIllustrationVariants } from './ds-illustration.types'; import styles from './ds-illustration.stories.module.scss'; +import { DsStack } from '../ds-stack'; const meta: Meta = { title: 'Components/Illustration', @@ -43,12 +44,12 @@ export const Showcase: Story = { render: () => (
{dsIllustrationVariants.map((variant) => ( -
+ - + {variant} -
+ ))}
), From 3edc0ea9019619d672f2239a7a420528f8c5913a Mon Sep 17 00:00:00 2001 From: Ihor Romanchuk Date: Fri, 4 Sep 2026 17:45:37 +0200 Subject: [PATCH 3/3] Cleanup comments --- CONTEXT.md | 6 ------ .../components/ds-illustration/ds-illustration.stories.tsx | 4 ---- 2 files changed, 10 deletions(-) diff --git a/CONTEXT.md b/CONTEXT.md index f06abb338..c470b9f3c 100644 --- a/CONTEXT.md +++ b/CONTEXT.md @@ -144,7 +144,6 @@ _Avoid_: Scrollbar gutter, placeholder, gutter cell - A **Successor component** supersedes a **Deprecated component**; both may ship until consumers migrate - **Tokens** flow from design into SCSS; **Components** consume tokens, not raw hex from Figma in new work - A **Component** shows its **Empty state** only when it has zero data items; a virtualized body gates **Empty state** on data count, never on the count of currently rendered (virtual) rows -- An **Icon** is not an **Illustration**; `DsIcon` never renders empty-screen tiles - **Changesets** attach to package releases; a new **Component** or breaking API change typically needs one - A **Workspace page** has one header and one **Workspace body**; the body may combine **Side menu panel**, **Left side panel**, **Content area**, and **Right side panel** - **Side menu panel** and **Left side panel** differ: only **Left side panel** toggles **Content area** horizontal margins (24px vs 40px) @@ -181,9 +180,6 @@ _Avoid_: Scrollbar gutter, placeholder, gutter cell > **Dev:** "When I make the expander column wider, does that also size the nested details columns?" > **Domain expert:** "No — that width belongs to the expander **Utility column**. Nested details are consumer **Leaf columns**." -> **Dev:** "Should Cluster NE from the empty-screen tile go on `DsIcon` as `rich-cluster-ne`?" -> **Domain expert:** "No — that's an **Illustration**, variant `device`. `special-cluster-ne` is the **Icon**." - > **Dev:** "Should a long column title wrap onto two lines in the header?" > **Domain expert:** "No — that's a **Header label**. It stays one line with an ellipsis; don't call it a column title." @@ -194,5 +190,3 @@ _Avoid_: Scrollbar gutter, placeholder, gutter cell - "placeholder" in header layout meant **Scrollbar spacer**, not TanStack `header.isPlaceholder` (spanning-cell hole) and not **Empty state**. - "Scrollbar gutter" meant CSS reservation on `thead`; the structure is a **Scrollbar spacer**. - "Expandable column width" was used to mean nested details columns — resolved: that width belongs to the expander **Utility column**. -- "rich icon" / "state icon" / Figma DAP_State Icon — resolved: **Illustration**. -- "Cluster NE" on the linked empty-screen instance — resolved: **Illustration** variant `device`; `special-cluster-ne` remains the **Icon**. diff --git a/packages/design-system/src/components/ds-illustration/ds-illustration.stories.tsx b/packages/design-system/src/components/ds-illustration/ds-illustration.stories.tsx index 9fff17df0..042e5c1f9 100644 --- a/packages/design-system/src/components/ds-illustration/ds-illustration.stories.tsx +++ b/packages/design-system/src/components/ds-illustration/ds-illustration.stories.tsx @@ -22,10 +22,6 @@ const meta: Meta = { export default meta; type Story = StoryObj; -/** - * Default empty-screen tile (`no-tasks`). Swap `variant` for the other - * 250×200 scenes. Not an Icon — do not pass these names to `DsIcon`. - */ export const Default: Story = { args: { variant: 'no-tasks',