From 182fce2df657c8ea9116b961ac6701ec3eaa057a Mon Sep 17 00:00:00 2001 From: Jose De Freitas Jr Date: Tue, 5 Aug 2025 11:15:34 -0600 Subject: [PATCH 01/10] feat(pie-tabs): DSW-3169 added files from generator --- .github/project-labeler.yml | 2 + .github/workflows/ci.yml | 3 +- apps/pie-storybook/package.json | 1 + apps/pie-storybook/stories/pie-tabs.mdx | 6 ++ .../pie-storybook/stories/pie-tabs.stories.ts | 34 +++++++ .../stories/testing/pie-tabs.test.stories.ts | 34 +++++++ bundlewatch.config.json | 4 + packages/components/pie-tabs/.eslintignore | 6 ++ packages/components/pie-tabs/README.md | 88 +++++++++++++++++++ .../custom-elements-manifest.config.mjs | 15 ++++ packages/components/pie-tabs/package.json | 51 +++++++++++ .../pie-tabs/pie-tabs.test.stories.ts | 34 +++++++ .../pie-tabs/playwright-lit-visual.config.ts | 4 + .../pie-tabs/playwright-lit.config.ts | 4 + .../components/pie-tabs/src/defs-react.ts | 8 ++ packages/components/pie-tabs/src/defs.ts | 3 + packages/components/pie-tabs/src/index.ts | 30 +++++++ packages/components/pie-tabs/src/tabs.scss | 7 ++ .../test/accessibility/pie-tabs.spec.ts | 17 ++++ .../pie-tabs/test/component/pie-tabs.spec.ts | 20 +++++ .../pie-tabs/test/visual/pie-tabs.spec.ts | 14 +++ packages/components/pie-tabs/tsconfig.json | 8 ++ packages/components/pie-tabs/turbo.json | 40 +++++++++ packages/components/pie-tabs/vite.config.js | 6 ++ .../components/pie-webc/components/tabs.d.ts | 1 + .../components/pie-webc/components/tabs.js | 1 + packages/components/pie-webc/package.json | 11 +++ packages/components/pie-webc/react/tabs.d.ts | 1 + packages/components/pie-webc/react/tabs.js | 1 + yarn.lock | 15 ++++ 30 files changed, 468 insertions(+), 1 deletion(-) create mode 100644 apps/pie-storybook/stories/pie-tabs.mdx create mode 100644 apps/pie-storybook/stories/pie-tabs.stories.ts create mode 100644 apps/pie-storybook/stories/testing/pie-tabs.test.stories.ts create mode 100644 packages/components/pie-tabs/.eslintignore create mode 100644 packages/components/pie-tabs/README.md create mode 100644 packages/components/pie-tabs/custom-elements-manifest.config.mjs create mode 100644 packages/components/pie-tabs/package.json create mode 100644 packages/components/pie-tabs/pie-tabs.test.stories.ts create mode 100644 packages/components/pie-tabs/playwright-lit-visual.config.ts create mode 100644 packages/components/pie-tabs/playwright-lit.config.ts create mode 100644 packages/components/pie-tabs/src/defs-react.ts create mode 100644 packages/components/pie-tabs/src/defs.ts create mode 100644 packages/components/pie-tabs/src/index.ts create mode 100644 packages/components/pie-tabs/src/tabs.scss create mode 100644 packages/components/pie-tabs/test/accessibility/pie-tabs.spec.ts create mode 100644 packages/components/pie-tabs/test/component/pie-tabs.spec.ts create mode 100644 packages/components/pie-tabs/test/visual/pie-tabs.spec.ts create mode 100644 packages/components/pie-tabs/tsconfig.json create mode 100644 packages/components/pie-tabs/turbo.json create mode 100644 packages/components/pie-tabs/vite.config.js create mode 100644 packages/components/pie-webc/components/tabs.d.ts create mode 100644 packages/components/pie-webc/components/tabs.js create mode 100644 packages/components/pie-webc/react/tabs.d.ts create mode 100644 packages/components/pie-webc/react/tabs.js diff --git a/.github/project-labeler.yml b/.github/project-labeler.yml index afed829572..8461dab81d 100644 --- a/.github/project-labeler.yml +++ b/.github/project-labeler.yml @@ -143,3 +143,5 @@ pie-avatar: - packages/components/pie-avatar/**/* pie-data-table: - packages/components/pie-data-table/**/* +pie-tabs: + - packages/components/pie-tabs/**/* diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c8af8de8b5..7d7386bf0f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -18,7 +18,7 @@ on: branches: - main -permissions: +permissions: contents: read concurrency: @@ -61,6 +61,7 @@ env: PERCY_TOKEN_PIE_LIST: ${{ secrets.PERCY_TOKEN_PIE_LIST }} PERCY_TOKEN_PIE_AVATAR: ${{ secrets.PERCY_TOKEN_PIE_AVATAR }} PERCY_TOKEN_PIE_DATA_TABLE: ${{ secrets.PERCY_TOKEN_PIE_DATA_TABLE }} + PERCY_TOKEN_PIE_TABS: ${{ secrets.PERCY_TOKEN_PIE_TABS }} jobs: check-change-type: diff --git a/apps/pie-storybook/package.json b/apps/pie-storybook/package.json index c44f0f3562..32434ef1fc 100644 --- a/apps/pie-storybook/package.json +++ b/apps/pie-storybook/package.json @@ -42,6 +42,7 @@ "@justeattakeaway/pie-select": "0.6.10", "@justeattakeaway/pie-spinner": "1.2.4", "@justeattakeaway/pie-switch": "2.0.6", + "@justeattakeaway/pie-tabs": "0.0.0", "@justeattakeaway/pie-tag": "0.17.4", "@justeattakeaway/pie-text-input": "0.28.11", "@justeattakeaway/pie-textarea": "0.16.11", diff --git a/apps/pie-storybook/stories/pie-tabs.mdx b/apps/pie-storybook/stories/pie-tabs.mdx new file mode 100644 index 0000000000..e8bb0af858 --- /dev/null +++ b/apps/pie-storybook/stories/pie-tabs.mdx @@ -0,0 +1,6 @@ +import { Meta } from '@storybook/addon-docs'; +import { Markdown } from '@storybook/blocks'; +import readme from '@justeattakeaway/pie-tabs/README.md?raw'; + + +{readme} diff --git a/apps/pie-storybook/stories/pie-tabs.stories.ts b/apps/pie-storybook/stories/pie-tabs.stories.ts new file mode 100644 index 0000000000..56281ac339 --- /dev/null +++ b/apps/pie-storybook/stories/pie-tabs.stories.ts @@ -0,0 +1,34 @@ +import { html } from 'lit'; +import { type Meta } from '@storybook/web-components'; + +import '@justeattakeaway/pie-tabs'; +import { type TabsProps } from '@justeattakeaway/pie-tabs'; + +import { createStory } from '../utilities'; + +type TabsStoryMeta = Meta; + +const defaultArgs: TabsProps = {}; + +const tabsStoryMeta: TabsStoryMeta = { + title: 'Components/Tabs', + component: 'pie-tabs', + argTypes: {}, + args: defaultArgs, + parameters: { + design: { + type: 'figma', + url: '', + }, + }, +}; + +export default tabsStoryMeta; + +// TODO: remove the eslint-disable rule when props are added +// eslint-disable-next-line no-empty-pattern +const Template = ({}: TabsProps) => html` + +`; + +export const Default = createStory(Template, defaultArgs)(); diff --git a/apps/pie-storybook/stories/testing/pie-tabs.test.stories.ts b/apps/pie-storybook/stories/testing/pie-tabs.test.stories.ts new file mode 100644 index 0000000000..b30c9d282e --- /dev/null +++ b/apps/pie-storybook/stories/testing/pie-tabs.test.stories.ts @@ -0,0 +1,34 @@ +import { html } from 'lit'; +import { type Meta } from '@storybook/web-components'; + +import '@justeattakeaway/pie-tabs'; +import { type TabsProps } from '@justeattakeaway/pie-tabs'; + +import { createStory } from '../../utilities'; + +type TabsStoryMeta = Meta; + +const defaultArgs: TabsProps = {}; + +const tabsStoryMeta: TabsStoryMeta = { + title: 'Tabs', + component: 'pie-tabs', + argTypes: {}, + args: defaultArgs, + parameters: { + design: { + type: 'figma', + url: '', + }, + }, +}; + +export default tabsStoryMeta; + +// TODO: remove the eslint-disable rule when props are added +// eslint-disable-next-line no-empty-pattern +const Template = ({}: TabsProps) => html` + +`; + +export const Default = createStory(Template, defaultArgs)(); diff --git a/bundlewatch.config.json b/bundlewatch.config.json index 669d2bbcda..b35ddd0f33 100644 --- a/bundlewatch.config.json +++ b/bundlewatch.config.json @@ -211,6 +211,10 @@ { "path": "./packages/components/pie-data-table/dist/*.js", "maxSize": "3 KB" + }, + { + "path": "./packages/components/pie-tabs/dist/*.js", + "maxSize": "3 KB" } ], "defaultCompression": "gzip", diff --git a/packages/components/pie-tabs/.eslintignore b/packages/components/pie-tabs/.eslintignore new file mode 100644 index 0000000000..a4c746553e --- /dev/null +++ b/packages/components/pie-tabs/.eslintignore @@ -0,0 +1,6 @@ +.turbo +dist +node_modules +lit-browsers-report +lit-visual-report +test-results diff --git a/packages/components/pie-tabs/README.md b/packages/components/pie-tabs/README.md new file mode 100644 index 0000000000..e34238709a --- /dev/null +++ b/packages/components/pie-tabs/README.md @@ -0,0 +1,88 @@ +# @justeattakeaway/pie-tabs +[Source Code](https://github.com/justeattakeaway/pie/tree/main/packages/components/pie-tabs) | [Design Documentation](https://pie.design/components/tabs) | [NPM](https://www.npmjs.com/package/@justeattakeaway/pie-tabs) + +

+ + GitHub Workflow Status + +

+ +`@justeattakeaway/pie-tabs` is a Web Component built using the Lit library. It offers a simple and accessible button component for web applications. + +## Table of Contents + +- [Installation](#installation) +- [Documentation](#documentation) + - [Properties](#properties) + - [Slots](#slots) + - [CSS Variables](#css-variables) + - [Events](#events) +- [Usage Examples](#usage-examples) +- [Questions and Support](#questions-and-support) +- [Contributing](#contributing) + +## Installation + +> To install any of our web components in your application, we would suggest following the [getting started guide](https://webc.pie.design/?path=/docs/introduction-getting-started--docs) to set up your project. + +Ideally, you should install the component using the **`@justeattakeaway/pie-webc`** package, which includes all of the components. Or you can install the individual component package. + +## Documentation + +### Properties +| Prop | Options | Description | Default | +|------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|----------------| +| | | | | + +### Slots +| Slot | Description | +|----------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| | | + +### CSS Variables +| Variable | Description | Default | +|----------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------| +| | | | + +### Events +| Event | Type | Description | +|------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-------------| +| | | | + +## Usage Examples + +**For HTML:** + +```js +// import as module into a js file e.g. main.js +import '@justeattakeaway/pie-webc/components/tabs.js' +``` + +```html + + + +``` + +**For Native JS Applications, Vue, Angular, Svelte etc.:** + +```js +// Vue templates (using Nuxt 3) +import '@justeattakeaway/pie-webc/components/tabs.js' + +``` + +**For React Applications:** + +```jsx +import { PieTabs } from '@justeattakeaway/pie-webc/react/tabs.js'; + +``` + +## Questions and Support + +If you work at Just Eat Takeaway.com, please contact us on **#help-designsystem**. Otherwise, please raise an issue on [Github](https://github.com/justeattakeaway/pie/issues). + +## Contributing + +Check out our [contributing guide](https://github.com/justeattakeaway/pie/wiki/Contributing-Guide) for more information on [local development](https://github.com/justeattakeaway/pie/wiki/Contributing-Guide#local-development) and how to run specific [component tests](https://github.com/justeattakeaway/pie/wiki/Contributing-Guide#testing). diff --git a/packages/components/pie-tabs/custom-elements-manifest.config.mjs b/packages/components/pie-tabs/custom-elements-manifest.config.mjs new file mode 100644 index 0000000000..b4be0cb5ed --- /dev/null +++ b/packages/components/pie-tabs/custom-elements-manifest.config.mjs @@ -0,0 +1,15 @@ +import { moduleFileExtensionsPlugin } from 'cem-plugin-module-file-extensions'; + +export default { + globs: [ + './src/**/!(*.css).ts', + ], + exclude: [ + '**/*.d.ts', + '**/*.d.js', + '**/react.ts', + '**/test/**', + '**/node_modules/**', + ], + plugins: [moduleFileExtensionsPlugin()], +}; diff --git a/packages/components/pie-tabs/package.json b/packages/components/pie-tabs/package.json new file mode 100644 index 0000000000..17bfee0181 --- /dev/null +++ b/packages/components/pie-tabs/package.json @@ -0,0 +1,51 @@ +{ + "name": "@justeattakeaway/pie-tabs", + "description": "PIE Design System Tabs built using Web Components", + "version": "0.0.0", + "type": "module", + "main": "dist/index.js", + "module": "dist/index.js", + "types": "dist/index.d.ts", + "files": [ + "custom-elements.json", + "src", + "dist", + "**/*.d.ts" + ], + "pieMetadata": { + "componentStatus": "alpha" + }, + "scripts": { + "build": "run -T vite build", + "build:react-wrapper": "npx build-react-wrapper", + "create:manifest": "yarn cem analyze --litelement", + "lint:scripts": "run -T eslint .", + "lint:scripts:fix": "yarn lint:scripts --fix", + "lint:style": "run -T stylelint ./src/**/*.{css,scss}", + "lint:style:fix": "yarn lint:style --fix", + "watch": "run -T vite build --watch", + "test:browsers": "npx playwright test -c ./playwright-lit.config.ts", + "test:browsers:ci": "yarn test:browsers", + "test:visual": "run -T cross-env-shell PERCY_TOKEN=${PERCY_TOKEN_PIE_TABS} percy exec --allowed-hostname cloudfront.net -- npx playwright test -c ./playwright-lit-visual.config.ts", + "test:visual:ci": "yarn test:visual" + }, + "author": "Just Eat Takeaway.com - Design System Team", + "license": "Apache-2.0", + "devDependencies": { + "@custom-elements-manifest/analyzer": "0.9.0", + "@justeattakeaway/pie-components-config": "0.21.0", + "@justeattakeaway/pie-css": "0.19.0", + "@justeattakeaway/pie-monorepo-utils": "0.6.0", + "cem-plugin-module-file-extensions": "0.0.5" + }, + "dependencies": { + "@justeattakeaway/pie-webc-core": "1.0.0" + }, + "volta": { + "extends": "../../../package.json" + }, + "customElements": "custom-elements.json", + "sideEffects": [ + "dist/*.js" + ] +} diff --git a/packages/components/pie-tabs/pie-tabs.test.stories.ts b/packages/components/pie-tabs/pie-tabs.test.stories.ts new file mode 100644 index 0000000000..b30c9d282e --- /dev/null +++ b/packages/components/pie-tabs/pie-tabs.test.stories.ts @@ -0,0 +1,34 @@ +import { html } from 'lit'; +import { type Meta } from '@storybook/web-components'; + +import '@justeattakeaway/pie-tabs'; +import { type TabsProps } from '@justeattakeaway/pie-tabs'; + +import { createStory } from '../../utilities'; + +type TabsStoryMeta = Meta; + +const defaultArgs: TabsProps = {}; + +const tabsStoryMeta: TabsStoryMeta = { + title: 'Tabs', + component: 'pie-tabs', + argTypes: {}, + args: defaultArgs, + parameters: { + design: { + type: 'figma', + url: '', + }, + }, +}; + +export default tabsStoryMeta; + +// TODO: remove the eslint-disable rule when props are added +// eslint-disable-next-line no-empty-pattern +const Template = ({}: TabsProps) => html` + +`; + +export const Default = createStory(Template, defaultArgs)(); diff --git a/packages/components/pie-tabs/playwright-lit-visual.config.ts b/packages/components/pie-tabs/playwright-lit-visual.config.ts new file mode 100644 index 0000000000..2fd82d7d5f --- /dev/null +++ b/packages/components/pie-tabs/playwright-lit-visual.config.ts @@ -0,0 +1,4 @@ +import { defineConfig } from '@playwright/test'; +import { getPlaywrightNativeVisualConfig } from '@justeattakeaway/pie-components-config'; + +export default defineConfig(getPlaywrightNativeVisualConfig()); diff --git a/packages/components/pie-tabs/playwright-lit.config.ts b/packages/components/pie-tabs/playwright-lit.config.ts new file mode 100644 index 0000000000..6dcc0f833d --- /dev/null +++ b/packages/components/pie-tabs/playwright-lit.config.ts @@ -0,0 +1,4 @@ +import { defineConfig } from '@playwright/test'; +import { getPlaywrightNativeConfig } from '@justeattakeaway/pie-components-config'; + +export default defineConfig(getPlaywrightNativeConfig()); diff --git a/packages/components/pie-tabs/src/defs-react.ts b/packages/components/pie-tabs/src/defs-react.ts new file mode 100644 index 0000000000..2714b9afa2 --- /dev/null +++ b/packages/components/pie-tabs/src/defs-react.ts @@ -0,0 +1,8 @@ +import type React from 'react'; +/** + * TODO: Verify if ReactBaseType can be set as a more specific React interface + * Use the React IntrinsicElements interface to find how to map standard HTML elements to existing React Interfaces + * Example: an HTML button maps to `React.ButtonHTMLAttributes` + * https://github.com/DefinitelyTyped/DefinitelyTyped/blob/0bb210867d16170c4a08d9ce5d132817651a0f80/types/react/index.d.ts#L2829 + */ +export type ReactBaseType = React.HTMLAttributes diff --git a/packages/components/pie-tabs/src/defs.ts b/packages/components/pie-tabs/src/defs.ts new file mode 100644 index 0000000000..a134a6c8fe --- /dev/null +++ b/packages/components/pie-tabs/src/defs.ts @@ -0,0 +1,3 @@ +// TODO - please remove the eslint disable comment below when you add props to this interface +// eslint-disable-next-line @typescript-eslint/no-empty-interface +export interface TabsProps {} diff --git a/packages/components/pie-tabs/src/index.ts b/packages/components/pie-tabs/src/index.ts new file mode 100644 index 0000000000..91d34cabce --- /dev/null +++ b/packages/components/pie-tabs/src/index.ts @@ -0,0 +1,30 @@ +import { html, unsafeCSS } from 'lit'; +import { PieElement } from '@justeattakeaway/pie-webc-core/src/internals/PieElement'; +import { RtlMixin, safeCustomElement } from '@justeattakeaway/pie-webc-core'; + +import styles from './tabs.scss?inline'; +import { type TabsProps } from './defs'; + +// Valid values available to consumers +export * from './defs'; + +const componentSelector = 'pie-tabs'; + +/** + * @tagname pie-tabs + */ +@safeCustomElement('pie-tabs') +export class PieTabs extends RtlMixin(PieElement) implements TabsProps { + render () { + return html`

Hello world!

`; + } + + // Renders a `CSSResult` generated from SCSS by Vite + static styles = unsafeCSS(styles); +} + +declare global { + interface HTMLElementTagNameMap { + [componentSelector]: PieTabs; + } +} diff --git a/packages/components/pie-tabs/src/tabs.scss b/packages/components/pie-tabs/src/tabs.scss new file mode 100644 index 0000000000..c862d8f1ae --- /dev/null +++ b/packages/components/pie-tabs/src/tabs.scss @@ -0,0 +1,7 @@ +@use '@justeattakeaway/pie-css/scss' as p; + +:host { + // Note: For consistency sake, the recommended display should be either + // "block" or "inline-block", otherwise it can be ommited for floating elements + display: block; +} diff --git a/packages/components/pie-tabs/test/accessibility/pie-tabs.spec.ts b/packages/components/pie-tabs/test/accessibility/pie-tabs.spec.ts new file mode 100644 index 0000000000..f1a327289a --- /dev/null +++ b/packages/components/pie-tabs/test/accessibility/pie-tabs.spec.ts @@ -0,0 +1,17 @@ +import { test, expect } from '@justeattakeaway/pie-webc-testing/src/playwright/playwright-fixtures.ts'; +import { BasePage } from '@justeattakeaway/pie-webc-testing/src/helpers/page-object/base-page.ts'; + +test.describe('PieTabs - Accessibility tests', () => { + test('a11y - should test the PieTabs component WCAG compliance', async ({ page, makeAxeBuilder }) => { + // Arrange + const basePage = new BasePage(page, 'tabs--default'); + + basePage.load(); + await page.waitForTimeout(2500); + + // Act + const results = await makeAxeBuilder().analyze(); + + expect(results.violations).toEqual([]); + }); +}); diff --git a/packages/components/pie-tabs/test/component/pie-tabs.spec.ts b/packages/components/pie-tabs/test/component/pie-tabs.spec.ts new file mode 100644 index 0000000000..f31a9ae358 --- /dev/null +++ b/packages/components/pie-tabs/test/component/pie-tabs.spec.ts @@ -0,0 +1,20 @@ +import { test, expect } from '@playwright/test'; +import { BasePage } from '@justeattakeaway/pie-webc-testing/src/helpers/page-object/base-page.ts'; + +const componentSelector = '[data-test-id="pie-tabs"]'; + +test.describe('PieTabs - Component tests', () => { + test('should render successfully', async ({ page }) => { + // Arrange + const basePage = new BasePage(page, 'tabs--default'); + + basePage.load(); + await page.waitForTimeout(2500); + + // Act + const tabs = page.locator(componentSelector); + + // Assert + expect(tabs).toBeVisible(); + }); +}); diff --git a/packages/components/pie-tabs/test/visual/pie-tabs.spec.ts b/packages/components/pie-tabs/test/visual/pie-tabs.spec.ts new file mode 100644 index 0000000000..0112f68303 --- /dev/null +++ b/packages/components/pie-tabs/test/visual/pie-tabs.spec.ts @@ -0,0 +1,14 @@ +import { test } from '@playwright/test'; +import percySnapshot from '@percy/playwright'; +import { BasePage } from '@justeattakeaway/pie-webc-testing/src/helpers/page-object/base-page.ts'; + +test.describe('PieTabs - Visual tests`', () => { + test('should display the PieTabs component successfully', async ({ page }) => { + const basePage = new BasePage(page, 'tabs--default'); + + basePage.load(); + await page.waitForTimeout(2500); + + await percySnapshot(page, 'PieTabs - Visual Test'); + }); +}); diff --git a/packages/components/pie-tabs/tsconfig.json b/packages/components/pie-tabs/tsconfig.json new file mode 100644 index 0000000000..f36acff08c --- /dev/null +++ b/packages/components/pie-tabs/tsconfig.json @@ -0,0 +1,8 @@ +{ + "extends": "@justeattakeaway/pie-components-config/tsconfig.json", + "compilerOptions": { + "baseUrl": ".", + "rootDir": ".", + }, + "include": ["src/**/*.ts", "../../../configs/pie-components-config/declaration.d.ts", "test/**/*.ts", "playwright-lit-visual.config.ts", "playwright-lit.config.ts"], +} diff --git a/packages/components/pie-tabs/turbo.json b/packages/components/pie-tabs/turbo.json new file mode 100644 index 0000000000..20c44cdde7 --- /dev/null +++ b/packages/components/pie-tabs/turbo.json @@ -0,0 +1,40 @@ +{ + "$schema": "https://turborepo.org/schema.json", + "extends": [ + "//" + ], + "pipeline": { + "test:browsers": { + "cache": true, + "dependsOn": [], + "inputs": [ + "$TURBO_DEFAULT$", + "../../../apps/pie-storybook/stories/testing/pie-tabs.test.stories.ts" + ] + }, + "test:browsers:ci": { + "cache": true, + "dependsOn": [], + "inputs": [ + "$TURBO_DEFAULT$", + "../../../apps/pie-storybook/stories/testing/pie-tabs.test.stories.ts" + ] + }, + "test:visual": { + "cache": false, + "dependsOn": [], + "inputs": [ + "$TURBO_DEFAULT$", + "../../../apps/pie-storybook/stories/testing/pie-tabs.test.stories.ts" + ] + }, + "test:visual:ci": { + "cache": false, + "dependsOn": [], + "inputs": [ + "$TURBO_DEFAULT$", + "../../../apps/pie-storybook/stories/testing/pie-tabs.test.stories.ts" + ] + } + } +} \ No newline at end of file diff --git a/packages/components/pie-tabs/vite.config.js b/packages/components/pie-tabs/vite.config.js new file mode 100644 index 0000000000..a2807e30e9 --- /dev/null +++ b/packages/components/pie-tabs/vite.config.js @@ -0,0 +1,6 @@ +import viteConfig from '@justeattakeaway/pie-components-config/vite.config'; +import getPackageVersion from '@justeattakeaway/pie-monorepo-utils/utils/get-package-version.js'; + +export default viteConfig({ + version: getPackageVersion(__dirname), +}); diff --git a/packages/components/pie-webc/components/tabs.d.ts b/packages/components/pie-webc/components/tabs.d.ts new file mode 100644 index 0000000000..0706b0bc5c --- /dev/null +++ b/packages/components/pie-webc/components/tabs.d.ts @@ -0,0 +1 @@ +export * from '@justeattakeaway/pie-tabs'; diff --git a/packages/components/pie-webc/components/tabs.js b/packages/components/pie-webc/components/tabs.js new file mode 100644 index 0000000000..0706b0bc5c --- /dev/null +++ b/packages/components/pie-webc/components/tabs.js @@ -0,0 +1 @@ +export * from '@justeattakeaway/pie-tabs'; diff --git a/packages/components/pie-webc/package.json b/packages/components/pie-webc/package.json index 7a1d415186..2b127b9326 100644 --- a/packages/components/pie-webc/package.json +++ b/packages/components/pie-webc/package.json @@ -264,6 +264,16 @@ "require": "./react/switch.js", "types": "./react/switch.d.ts" }, + "./components/tabs.js": { + "import": "./components/tabs.js", + "require": "./components/tabs.js", + "types": "./components/tabs.d.ts" + }, + "./react/tabs.js": { + "import": "./react/tabs.js", + "require": "./react/tabs.js", + "types": "./react/tabs.d.ts" + }, "./components/tag.js": { "import": "./components/tag.js", "require": "./components/tag.js", @@ -364,6 +374,7 @@ "@justeattakeaway/pie-select": "0.6.10", "@justeattakeaway/pie-spinner": "1.2.4", "@justeattakeaway/pie-switch": "2.0.6", + "@justeattakeaway/pie-tabs": "0.0.0", "@justeattakeaway/pie-tag": "0.17.4", "@justeattakeaway/pie-text-input": "0.28.11", "@justeattakeaway/pie-textarea": "0.16.11", diff --git a/packages/components/pie-webc/react/tabs.d.ts b/packages/components/pie-webc/react/tabs.d.ts new file mode 100644 index 0000000000..0ca857d5b6 --- /dev/null +++ b/packages/components/pie-webc/react/tabs.d.ts @@ -0,0 +1 @@ +export * from '@justeattakeaway/pie-tabs/dist/react.js'; diff --git a/packages/components/pie-webc/react/tabs.js b/packages/components/pie-webc/react/tabs.js new file mode 100644 index 0000000000..0ca857d5b6 --- /dev/null +++ b/packages/components/pie-webc/react/tabs.js @@ -0,0 +1 @@ +export * from '@justeattakeaway/pie-tabs/dist/react.js'; diff --git a/yarn.lock b/yarn.lock index 732412dc60..89ed0d3d58 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3818,6 +3818,7 @@ __metadata: "@justeattakeaway/pie-select": 0.6.10 "@justeattakeaway/pie-spinner": 1.2.4 "@justeattakeaway/pie-switch": 2.0.6 + "@justeattakeaway/pie-tabs": 0.0.0 "@justeattakeaway/pie-tag": 0.17.4 "@justeattakeaway/pie-text-input": 0.28.11 "@justeattakeaway/pie-textarea": 0.16.11 @@ -3863,6 +3864,19 @@ __metadata: languageName: unknown linkType: soft +"@justeattakeaway/pie-tabs@0.0.0, @justeattakeaway/pie-tabs@workspace:packages/components/pie-tabs": + version: 0.0.0-use.local + resolution: "@justeattakeaway/pie-tabs@workspace:packages/components/pie-tabs" + dependencies: + "@custom-elements-manifest/analyzer": 0.9.0 + "@justeattakeaway/pie-components-config": 0.21.0 + "@justeattakeaway/pie-css": 0.19.0 + "@justeattakeaway/pie-monorepo-utils": 0.6.0 + "@justeattakeaway/pie-webc-core": 1.0.0 + cem-plugin-module-file-extensions: 0.0.5 + languageName: unknown + linkType: soft + "@justeattakeaway/pie-tag@0.17.4, @justeattakeaway/pie-tag@workspace:packages/components/pie-tag": version: 0.0.0-use.local resolution: "@justeattakeaway/pie-tag@workspace:packages/components/pie-tag" @@ -4002,6 +4016,7 @@ __metadata: "@justeattakeaway/pie-select": 0.6.10 "@justeattakeaway/pie-spinner": 1.2.4 "@justeattakeaway/pie-switch": 2.0.6 + "@justeattakeaway/pie-tabs": 0.0.0 "@justeattakeaway/pie-tag": 0.17.4 "@justeattakeaway/pie-text-input": 0.28.11 "@justeattakeaway/pie-textarea": 0.16.11 From 4c8a914c3ef8b0925298a8ccb1bb9d9dba276750 Mon Sep 17 00:00:00 2001 From: Jose De Freitas Jr Date: Tue, 5 Aug 2025 11:21:23 -0600 Subject: [PATCH 02/10] feat(pie-tabs): DSW-3169 added changeset entry --- .changeset/empty-pets-collect.md | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 .changeset/empty-pets-collect.md diff --git a/.changeset/empty-pets-collect.md b/.changeset/empty-pets-collect.md new file mode 100644 index 0000000000..7e349a9290 --- /dev/null +++ b/.changeset/empty-pets-collect.md @@ -0,0 +1,6 @@ +--- +"@justeattakeaway/pie-tabs": minor +"@justeattakeaway/pie-storybook": minor +--- + +[Added] pie-tabs basic html markup From 503bf2511358d519610613c19c50854e527f14ae Mon Sep 17 00:00:00 2001 From: Jose De Freitas Jr Date: Tue, 5 Aug 2025 14:19:45 -0600 Subject: [PATCH 03/10] feat(pie-tabs): DSW-3303 implemented pie-tab-panel sub component --- .../pie-tabs/src/pie-tab-panel/defs-react.ts | 3 ++ .../pie-tabs/src/pie-tab-panel/defs.ts | 17 ++++++++ .../pie-tabs/src/pie-tab-panel/index.ts | 40 +++++++++++++++++++ .../pie-tabs/src/pie-tab-panel/tab-panel.scss | 7 ++++ packages/components/pie-tabs/vite.config.js | 12 ++++++ 5 files changed, 79 insertions(+) create mode 100644 packages/components/pie-tabs/src/pie-tab-panel/defs-react.ts create mode 100644 packages/components/pie-tabs/src/pie-tab-panel/defs.ts create mode 100644 packages/components/pie-tabs/src/pie-tab-panel/index.ts create mode 100644 packages/components/pie-tabs/src/pie-tab-panel/tab-panel.scss diff --git a/packages/components/pie-tabs/src/pie-tab-panel/defs-react.ts b/packages/components/pie-tabs/src/pie-tab-panel/defs-react.ts new file mode 100644 index 0000000000..3fbd6d3f13 --- /dev/null +++ b/packages/components/pie-tabs/src/pie-tab-panel/defs-react.ts @@ -0,0 +1,3 @@ +import type React from 'react'; + +export type ReactBaseType = React.HTMLAttributes diff --git a/packages/components/pie-tabs/src/pie-tab-panel/defs.ts b/packages/components/pie-tabs/src/pie-tab-panel/defs.ts new file mode 100644 index 0000000000..94f4d2bdd7 --- /dev/null +++ b/packages/components/pie-tabs/src/pie-tab-panel/defs.ts @@ -0,0 +1,17 @@ +export interface TabPanelProps { + /** + * Tab's title. + * This is used to display the title of the tab in the tab list. + */ + title: string; + /** + * Optional property to indicate if the tab panel is selected. + * If true, the tab panel will be displayed as selected. + */ + selected?: boolean; + /** + * Optional property to indicate if the tab panel is disabled. + * If true, the tab panel will be displayed as disabled and not selectable. + */ + disabled?: boolean; +} diff --git a/packages/components/pie-tabs/src/pie-tab-panel/index.ts b/packages/components/pie-tabs/src/pie-tab-panel/index.ts new file mode 100644 index 0000000000..c3ace186dc --- /dev/null +++ b/packages/components/pie-tabs/src/pie-tab-panel/index.ts @@ -0,0 +1,40 @@ +import { html, unsafeCSS } from 'lit'; +import { property } from 'lit/decorators.js'; +import { PieElement } from '@justeattakeaway/pie-webc-core/src/internals/PieElement'; +import { RtlMixin, safeCustomElement } from '@justeattakeaway/pie-webc-core'; + +import styles from './tab-panel.scss?inline'; +import { type TabPanelProps } from './defs'; + +const componentSelector = 'pie-tab-panel'; + +// Valid values available to consumers +export * from './defs'; + +/** + * @tagname pie-tab-panel; + */ +@safeCustomElement('pie-tab-panel') +export class PieTabPanel extends RtlMixin(PieElement) implements TabPanelProps { + @property({ type: String }) + public title: TabPanelProps['title'] = ''; + + @property({ type: Boolean, reflect: true }) + public selected = false; + + @property({ type: Boolean, reflect: true }) + public disabled = false; + + render () { + return html``; + } + + // Renders a `CSSResult` generated from SCSS by Vite + static styles = unsafeCSS(styles); +} + +declare global { + interface HTMLElementTagNameMap { + [componentSelector]: PieTabPanel; + } +} diff --git a/packages/components/pie-tabs/src/pie-tab-panel/tab-panel.scss b/packages/components/pie-tabs/src/pie-tab-panel/tab-panel.scss new file mode 100644 index 0000000000..04580ccc5c --- /dev/null +++ b/packages/components/pie-tabs/src/pie-tab-panel/tab-panel.scss @@ -0,0 +1,7 @@ +:host { + display: none; +} + +:host([selected]) { + display: block; +} \ No newline at end of file diff --git a/packages/components/pie-tabs/vite.config.js b/packages/components/pie-tabs/vite.config.js index a2807e30e9..355249c84a 100644 --- a/packages/components/pie-tabs/vite.config.js +++ b/packages/components/pie-tabs/vite.config.js @@ -3,4 +3,16 @@ import getPackageVersion from '@justeattakeaway/pie-monorepo-utils/utils/get-pac export default viteConfig({ version: getPackageVersion(__dirname), + build: { + lib: { + entry: { + 'pie-tab-panel/index': 'src/pie-tab-panel/index.ts', + 'pie-tab-panel/react': 'src/pie-tab-panel/react.ts', + }, + }, + }, + dtsConfig: { + entryRoot: 'src', + rollupTypes: false, + }, }); From 0a96f63aec263b76542daf4e2f7dfb0cc14374f7 Mon Sep 17 00:00:00 2001 From: Jose De Freitas Jr Date: Tue, 5 Aug 2025 14:55:06 -0600 Subject: [PATCH 04/10] feat(pie-tabs): DSW-3303 implemented default properties --- packages/components/pie-tabs/src/defs.ts | 27 +++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/packages/components/pie-tabs/src/defs.ts b/packages/components/pie-tabs/src/defs.ts index a134a6c8fe..8d8b3e7db7 100644 --- a/packages/components/pie-tabs/src/defs.ts +++ b/packages/components/pie-tabs/src/defs.ts @@ -1,3 +1,24 @@ -// TODO - please remove the eslint disable comment below when you add props to this interface -// eslint-disable-next-line @typescript-eslint/no-empty-interface -export interface TabsProps {} +import { type ComponentDefaultProps } from '@justeattakeaway/pie-webc-core'; + +export const variants = ['global', 'contained'] as const; +export const orientations = ['horizontal', 'vertical'] as const; + +export interface TabsProps { + /** + * Optional variant for styling the tabs component. + * Default is 'global'. + */ + variant?: typeof variants[number]; + /** + * Optional property to set the orientation of the tabs. + * Default is 'horizontal'. + */ + orientation?: typeof orientations[number]; +} + +export type DefaultProps = ComponentDefaultProps; + +export const defaultProps: DefaultProps = { + variant: 'global', + orientation: 'horizontal', +}; From 99da21da6050375fa74eda71b890988994cb484f Mon Sep 17 00:00:00 2001 From: Jose De Freitas Jr Date: Tue, 5 Aug 2025 14:55:25 -0600 Subject: [PATCH 05/10] feat(pie-tabs): DSW-3303 implemented basic styling --- packages/components/pie-tabs/src/tabs.scss | 149 ++++++++++++++++++++- 1 file changed, 145 insertions(+), 4 deletions(-) diff --git a/packages/components/pie-tabs/src/tabs.scss b/packages/components/pie-tabs/src/tabs.scss index c862d8f1ae..b82b6d4e2a 100644 --- a/packages/components/pie-tabs/src/tabs.scss +++ b/packages/components/pie-tabs/src/tabs.scss @@ -1,7 +1,148 @@ @use '@justeattakeaway/pie-css/scss' as p; -:host { - // Note: For consistency sake, the recommended display should be either - // "block" or "inline-block", otherwise it can be ommited for floating elements - display: block; +.c-tabs { + --tabs-background-color: var(--dt-color-transparent); + --tabs-flex-global-orientation: row; + --tabs-flex-navigation-orientation: column; + --tabs-navigation-item-border-radius: var(--dt-radius-02); + + display: flex; + flex-direction: var(--tabs-flex-global-orientation); +} + +.c-tabs-variant--contained { + --tabs-background-color: var(--dt-color-container-default); +} + +.c-tabs-orientation--horizontal { + --tabs-flex-global-orientation: column; + --tabs-flex-navigation-orientation: row; +} + +.c-tabs-navigation { + ul { + display: flex; + flex-direction: var(--tabs-flex-navigation-orientation); + padding: 0; + margin: 0; + + li { + cursor: pointer; + + list-style: none; + display: flex; + position: relative; + gap: var(--dt-spacing-b); + color: var(--dt-color-content-subdued); + + padding-block-start: var(--dt-spacing-b); + padding-block-end: var(--dt-spacing-b); + padding-inline-start: var(--dt-spacing-d); + padding-inline-end: var(--dt-spacing-d); + + font-size: #{p.font-size(--dt-font-size-16)}; + line-height: #{p.line-height(--dt-font-size-16-line-height)}; + font-weight: 700; + border-radius: var(--tabs-navigation-item-border-radius); + + &:hover { + background-color: color-mix(in srgb, var(--dt-color-content-subdued) var(--dt-color-hover-01), var(--tabs-background-color)); + } + + &:focus-visible { + outline: solid calc(var(--dt-spacing-a) / 2) var(--dt-color-focus-outer); + outline-offset: calc(var(--dt-spacing-a) / 2 * -1.5); + } + + &:active { + background-color: color-mix(in srgb, var(--dt-color-content-subdued) var(--dt-color-active-01), var(--tabs-background-color)); + } + + &.selected { + background-color: var(--tabs-background-color); + border-radius: 0; + + &:after { + content: ''; + position: absolute; + background-color: var(--dt-color-interactive-brand); + } + } + + // Horizontal and vertical tabs on selected state + &.c-tabs-navigation-item--horizontal { + &.selected { + &:after { + height: 4px; + width: 100%; + left: 0; + right: 0; + border-top-left-radius: calc(var(--dt-radius-02) / 2); + border-top-right-radius: calc(var(--dt-radius-02) / 2); + } + } + + &.c-tabs-navigation-item-variant--global { + &.selected { + &:after { + bottom: calc(var(--dt-spacing-a) * -1); + } + } + } + + &.c-tabs-navigation-item-variant--contained { + &.selected { + &:after { + top: calc(var(--dt-spacing-a) * -1); + } + } + } + } + + &.c-tabs-navigation-item--vertical { + &.selected { + &:after { + top: 0; + left: calc(var(--dt-spacing-a) * -1); + width: 4px; + height:100%; + } + } + + &.c-tabs-navigation-item-variant--global { + &.selected { + &:after { + border-top-right-radius: calc(var(--dt-radius-02) / 2); + border-bottom-right-radius: calc(var(--dt-radius-02) / 2); + } + } + } + + &.c-tabs-navigation-item-variant--contained { + &.selected { + &:after { + border-top-left-radius: calc(var(--dt-radius-02) / 2); + border-bottom-left-radius: calc(var(--dt-radius-02) / 2); + } + } + } + } + + // End of horizontal and vertical tabs on selected state + + &.disabled { + cursor: not-allowed; + user-select: none; + color: var(--dt-color-content-disabled); + + &:hover, &:focus, &:focus-visible, &:active { + outline: none; + } + } + } + } } + +.c-tabs-panels { + background-color: var(--tabs-background-color); +} \ No newline at end of file From 9573f3f98adc7d36a11788f8cdf32b6532a3a8cc Mon Sep 17 00:00:00 2001 From: Jose De Freitas Jr Date: Tue, 5 Aug 2025 14:57:57 -0600 Subject: [PATCH 06/10] feat(pie-tabs): DSW-3303 implemented basic capabilities for tabs --- packages/components/pie-tabs/src/index.ts | 116 ++++++++++++++++++++-- 1 file changed, 109 insertions(+), 7 deletions(-) diff --git a/packages/components/pie-tabs/src/index.ts b/packages/components/pie-tabs/src/index.ts index 91d34cabce..5da3f5de2d 100644 --- a/packages/components/pie-tabs/src/index.ts +++ b/packages/components/pie-tabs/src/index.ts @@ -1,22 +1,124 @@ import { html, unsafeCSS } from 'lit'; import { PieElement } from '@justeattakeaway/pie-webc-core/src/internals/PieElement'; -import { RtlMixin, safeCustomElement } from '@justeattakeaway/pie-webc-core'; +import { safeCustomElement, validPropertyValues } from '@justeattakeaway/pie-webc-core'; +import { classMap } from 'lit/directives/class-map.js'; +import { property, queryAssignedElements } from 'lit/decorators.js'; +import { repeat } from 'lit/directives/repeat.js'; import styles from './tabs.scss?inline'; -import { type TabsProps } from './defs'; +import { + type TabsProps, + variants, + defaultProps, + orientations, +} from './defs'; +import { type TabPanelProps } from './pie-tab-panel/defs'; + +const componentSelector = 'pie-tabs'; // Valid values available to consumers export * from './defs'; -const componentSelector = 'pie-tabs'; - /** * @tagname pie-tabs */ -@safeCustomElement('pie-tabs') -export class PieTabs extends RtlMixin(PieElement) implements TabsProps { +@safeCustomElement(componentSelector) +export class PieTabs extends PieElement implements TabsProps { + @property({ type: String }) + @validPropertyValues(componentSelector, variants, defaultProps.variant) + public variant = defaultProps.variant; + + @property({ type: String }) + @validPropertyValues(componentSelector, orientations, defaultProps.orientation) + public orientation = defaultProps.orientation; + + @queryAssignedElements() _pieTabPanelSlots!: Array; + + private _selectedTab = 0; + + firstUpdated (): void { + this.requestUpdate(); + } + + updated (): void { + this.updateSelectedPanel(); + } + + /** + * Handles the click event on a tab. + * This method updates the selected tab index and updates the displayed panel accordingly. + * + * @param index The index of the tab that was clicked. + * + * @private + */ + private handleTabClick (index: number) { + this._selectedTab = index; + this.updateSelectedPanel(); + this.requestUpdate(); + } + + /** + * Updates the selected state of each tab panel based on the currently selected tab index. + * This method iterates through all tab panels and sets the `selected` property accordingly. + * + * @private + */ + private updateSelectedPanel () { + this._pieTabPanelSlots.forEach((panel, index) => { + panel.selected = index === this._selectedTab; + }); + } + render () { - return html`

Hello world!

`; + const classes = { + 'c-tabs': true, + 'c-tabs-variant--contained': this.variant === 'contained', + [`c-tabs-orientation--${this.orientation}`]: true, + }; + + return html` +
+ ${this._pieTabPanelSlots && this._pieTabPanelSlots.length > 0 && (html` + + `)} +
+ +
+
+ `; } // Renders a `CSSResult` generated from SCSS by Vite From a7c0ed9c0a7610243e6dcd1c943e17f7a7f1c189 Mon Sep 17 00:00:00 2001 From: Jose De Freitas Jr Date: Tue, 5 Aug 2025 14:58:21 -0600 Subject: [PATCH 07/10] feat(pie-tabs): DSW-3303 added controls into storybook --- .../pie-storybook/stories/pie-tabs.stories.ts | 56 ++++++++++++++++--- 1 file changed, 48 insertions(+), 8 deletions(-) diff --git a/apps/pie-storybook/stories/pie-tabs.stories.ts b/apps/pie-storybook/stories/pie-tabs.stories.ts index 56281ac339..5723d6377d 100644 --- a/apps/pie-storybook/stories/pie-tabs.stories.ts +++ b/apps/pie-storybook/stories/pie-tabs.stories.ts @@ -1,24 +1,58 @@ import { html } from 'lit'; import { type Meta } from '@storybook/web-components'; +import { type TabsProps as TabsPropsBase } from '@justeattakeaway/pie-tabs'; +import { ifDefined } from 'lit/directives/if-defined.js'; import '@justeattakeaway/pie-tabs'; -import { type TabsProps } from '@justeattakeaway/pie-tabs'; +import '@justeattakeaway/pie-tabs/dist/pie-tab-panel'; -import { createStory } from '../utilities'; +import { type SlottedComponentProps } from '../types'; +import { createStory, sanitizeAndRenderHTML } from '../utilities'; +type TabsProps = SlottedComponentProps; type TabsStoryMeta = Meta; -const defaultArgs: TabsProps = {}; +const slot = ` + Content 1 + Content 2 + Content 3 + Content 4 + `; + +const defaultArgs: TabsProps = { + slot, +}; const tabsStoryMeta: TabsStoryMeta = { - title: 'Components/Tabs', + title: 'Tabs', component: 'pie-tabs', - argTypes: {}, + argTypes: { + variant: { + description: 'Set the variant of the tabs.', + control: 'select', + options: ['global', 'contained'], + defaultValue: { + summary: 'global', + }, + }, + orientation: { + description: 'Set the orientation of the tabs.', + control: 'select', + options: ['horizontal', 'vertical'], + defaultValue: { + summary: 'horizontal', + }, + }, + slot: { + description: 'The default slot is used to pass `pie-tab-panel` elements. You must provide at least one `pie-tab-panel` element for the tabs to be visible.', + control: 'text', + }, + }, args: defaultArgs, parameters: { design: { type: 'figma', - url: '', + url: 'https://www.figma.com/design/pPSC73rPin4csb8DiK1CRr/branch/Mn7rERbBnNmaO2UAHT8qDz/%E2%9C%A8--Core--Web-Components--PIE-3-?node-id=12502-11771', }, }, }; @@ -27,8 +61,14 @@ export default tabsStoryMeta; // TODO: remove the eslint-disable rule when props are added // eslint-disable-next-line no-empty-pattern -const Template = ({}: TabsProps) => html` - +const Template = ({ slot, variant, orientation }: TabsProps) => html` + + ${sanitizeAndRenderHTML(slot, { ALLOWED_TAGS: ['pie-tab-panel'] })} + `; export const Default = createStory(Template, defaultArgs)(); From 0723a5c5034fa229387783f7ff2205e27df8ba4a Mon Sep 17 00:00:00 2001 From: Jose De Freitas Jr Date: Tue, 5 Aug 2025 15:34:17 -0600 Subject: [PATCH 08/10] feat(pie-tabs): DSW-3303 removed wrong file --- .../pie-tabs/pie-tabs.test.stories.ts | 34 ------------------- 1 file changed, 34 deletions(-) delete mode 100644 packages/components/pie-tabs/pie-tabs.test.stories.ts diff --git a/packages/components/pie-tabs/pie-tabs.test.stories.ts b/packages/components/pie-tabs/pie-tabs.test.stories.ts deleted file mode 100644 index b30c9d282e..0000000000 --- a/packages/components/pie-tabs/pie-tabs.test.stories.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { html } from 'lit'; -import { type Meta } from '@storybook/web-components'; - -import '@justeattakeaway/pie-tabs'; -import { type TabsProps } from '@justeattakeaway/pie-tabs'; - -import { createStory } from '../../utilities'; - -type TabsStoryMeta = Meta; - -const defaultArgs: TabsProps = {}; - -const tabsStoryMeta: TabsStoryMeta = { - title: 'Tabs', - component: 'pie-tabs', - argTypes: {}, - args: defaultArgs, - parameters: { - design: { - type: 'figma', - url: '', - }, - }, -}; - -export default tabsStoryMeta; - -// TODO: remove the eslint-disable rule when props are added -// eslint-disable-next-line no-empty-pattern -const Template = ({}: TabsProps) => html` - -`; - -export const Default = createStory(Template, defaultArgs)(); From e48917375125f0c2b397dbe3c3071bf6e98a344d Mon Sep 17 00:00:00 2001 From: Jose De Freitas Jr Date: Tue, 5 Aug 2025 16:14:52 -0600 Subject: [PATCH 09/10] feat(pie-tabs): DSW-3303 added basic tests --- .../stories/testing/pie-tabs.test.stories.ts | 64 ++++++++++++++++--- .../test/accessibility/pie-tabs.spec.ts | 30 +++++++++ .../pie-tabs/test/component/pie-tabs.spec.ts | 39 +++++++++-- .../pie-tabs/test/visual/pie-tabs.spec.ts | 54 ++++++++++++++-- 4 files changed, 170 insertions(+), 17 deletions(-) diff --git a/apps/pie-storybook/stories/testing/pie-tabs.test.stories.ts b/apps/pie-storybook/stories/testing/pie-tabs.test.stories.ts index b30c9d282e..cf47caa570 100644 --- a/apps/pie-storybook/stories/testing/pie-tabs.test.stories.ts +++ b/apps/pie-storybook/stories/testing/pie-tabs.test.stories.ts @@ -1,34 +1,80 @@ import { html } from 'lit'; +import { ifDefined } from 'lit/directives/if-defined.js'; import { type Meta } from '@storybook/web-components'; import '@justeattakeaway/pie-tabs'; -import { type TabsProps } from '@justeattakeaway/pie-tabs'; +import { type TabsProps as TabsPropsBase } from '@justeattakeaway/pie-tabs'; -import { createStory } from '../../utilities'; +import { createStory, createVariantStory, sanitizeAndRenderHTML } from '../../utilities'; +import { type SlottedComponentProps } from '../../types'; +type TabsProps = SlottedComponentProps; type TabsStoryMeta = Meta; -const defaultArgs: TabsProps = {}; +const slot = ` + Content 1 + Content 2 + Content 3 + Content 4 + `; + +const defaultArgs: TabsProps = { + slot, +}; const tabsStoryMeta: TabsStoryMeta = { title: 'Tabs', component: 'pie-tabs', - argTypes: {}, + argTypes: { + variant: { + description: 'Set the variant of the tabs.', + control: 'select', + options: ['global', 'contained'], + defaultValue: { + summary: 'global', + }, + }, + orientation: { + description: 'Set the orientation of the tabs.', + control: 'select', + options: ['horizontal', 'vertical'], + defaultValue: { + summary: 'horizontal', + }, + }, + slot: { + description: 'The default slot is used to pass `pie-tab-panel` elements. You must provide at least one `pie-tab-panel` element for the tabs to be visible.', + control: 'text', + }, + }, args: defaultArgs, parameters: { design: { type: 'figma', - url: '', + url: 'https://www.figma.com/design/pPSC73rPin4csb8DiK1CRr/branch/Mn7rERbBnNmaO2UAHT8qDz/%E2%9C%A8--Core--Web-Components--PIE-3-?node-id=12502-11771', }, }, }; export default tabsStoryMeta; -// TODO: remove the eslint-disable rule when props are added -// eslint-disable-next-line no-empty-pattern -const Template = ({}: TabsProps) => html` - +const Template = ({ slot, variant, orientation }: TabsProps) => html` + + ${sanitizeAndRenderHTML(slot, { ALLOWED_TAGS: ['pie-tab-panel'] })} + `; export const Default = createStory(Template, defaultArgs)(); + +const sharedProperties = { + orientation: ['horizontal', 'vertical'], + variant: ['global', 'contained'], + slot: [slot], +}; + +export const DefaultPropVariations = createVariantStory(Template, sharedProperties); + diff --git a/packages/components/pie-tabs/test/accessibility/pie-tabs.spec.ts b/packages/components/pie-tabs/test/accessibility/pie-tabs.spec.ts index f1a327289a..2b668888ae 100644 --- a/packages/components/pie-tabs/test/accessibility/pie-tabs.spec.ts +++ b/packages/components/pie-tabs/test/accessibility/pie-tabs.spec.ts @@ -14,4 +14,34 @@ test.describe('PieTabs - Accessibility tests', () => { expect(results.violations).toEqual([]); }); + + test('should have correct ARIA roles', async ({ page }) => { + // Arrange + const basePage = new BasePage(page, 'tabs--default'); + await basePage.load(); + await page.waitForTimeout(1000); + + // Act & Assert + const tabsList = page.locator('[data-test-id="pie-tabs"] ul'); + const tabs = page.locator('[data-test-id="pie-tabs"] li'); + + await expect(tabsList).toHaveAttribute('role', 'tablist'); + await expect(tabs.nth(0)).toHaveAttribute('role', 'tab'); + await expect(tabs.nth(1)).toHaveAttribute('role', 'tab'); + await expect(tabs.nth(2)).toHaveAttribute('role', 'tab'); + }); + + test('should have correct tabindex values', async ({ page }) => { + // Arrange + const basePage = new BasePage(page, 'tabs--default'); + await basePage.load(); + await page.waitForTimeout(1000); + + // Act & Assert + const tabs = page.locator('[data-test-id="pie-tabs"] li[role="tab"]'); + + await expect(tabs.nth(0)).toHaveAttribute('tabindex', '0'); + await expect(tabs.nth(1)).toHaveAttribute('tabindex', '1'); + await expect(tabs.nth(2)).toHaveAttribute('tabindex', '2'); + }); }); diff --git a/packages/components/pie-tabs/test/component/pie-tabs.spec.ts b/packages/components/pie-tabs/test/component/pie-tabs.spec.ts index f31a9ae358..c86273206d 100644 --- a/packages/components/pie-tabs/test/component/pie-tabs.spec.ts +++ b/packages/components/pie-tabs/test/component/pie-tabs.spec.ts @@ -7,14 +7,45 @@ test.describe('PieTabs - Component tests', () => { test('should render successfully', async ({ page }) => { // Arrange const basePage = new BasePage(page, 'tabs--default'); - - basePage.load(); - await page.waitForTimeout(2500); + await basePage.load(); + await page.waitForTimeout(2000); // Act const tabs = page.locator(componentSelector); // Assert - expect(tabs).toBeVisible(); + await expect(tabs).toBeVisible(); + }); + + test.describe('Tab Navigation', () => { + test('should show correct tab titles', async ({ page }) => { + // Arrange + const basePage = new BasePage(page, 'tabs--default'); + await basePage.load(); + await page.waitForTimeout(1000); + + // Act + const tabs = page.locator('[data-test-id="pie-tabs"] li[role="tab"]'); + + // Assert + await expect(tabs.nth(0)).toContainText('Tab 1'); + await expect(tabs.nth(1)).toContainText('Tab 2'); + await expect(tabs.nth(2)).toContainText('Tab 3'); + }); + + test('should have first tab selected by default', async ({ page }) => { + // Arrange + const basePage = new BasePage(page, 'tabs--default'); + await basePage.load(); + await page.waitForTimeout(1000); + + // Act + const firstTab = page.locator('[data-test-id="pie-tabs"] li[role="tab"]').nth(0); + const secondTab = page.locator('[data-test-id="pie-tabs"] li[role="tab"]').nth(1); + + // Assert + await expect(firstTab).toHaveClass(/selected/); + await expect(secondTab).not.toHaveClass(/selected/); + }); }); }); diff --git a/packages/components/pie-tabs/test/visual/pie-tabs.spec.ts b/packages/components/pie-tabs/test/visual/pie-tabs.spec.ts index 0112f68303..7b95f339df 100644 --- a/packages/components/pie-tabs/test/visual/pie-tabs.spec.ts +++ b/packages/components/pie-tabs/test/visual/pie-tabs.spec.ts @@ -2,13 +2,59 @@ import { test } from '@playwright/test'; import percySnapshot from '@percy/playwright'; import { BasePage } from '@justeattakeaway/pie-webc-testing/src/helpers/page-object/base-page.ts'; -test.describe('PieTabs - Visual tests`', () => { +test.describe('PieTabs - Visual tests', () => { test('should display the PieTabs component successfully', async ({ page }) => { + // Arrange const basePage = new BasePage(page, 'tabs--default'); + await basePage.load(); + await page.waitForTimeout(1000); - basePage.load(); - await page.waitForTimeout(2500); + // Act & Assert + await percySnapshot(page, 'PieTabs - Default'); + }); + + test('should display the contained variant correctly', async ({ page }) => { + // Arrange + const basePage = new BasePage(page, 'tabs--default'); + await basePage.load({ variant: 'contained' }); + await page.waitForTimeout(1000); + + // Act & Assert + await percySnapshot(page, 'PieTabs - Contained Variant'); + }); + + test('should display the vertical orientation correctly', async ({ page }) => { + // Arrange + const basePage = new BasePage(page, 'tabs--default'); + await basePage.load({ orientation: 'vertical' }); + await page.waitForTimeout(1000); + + // Act & Assert + await percySnapshot(page, 'PieTabs - Vertical Orientation'); + }); + + test('should display vertical contained variant correctly', async ({ page }) => { + // Arrange + const basePage = new BasePage(page, 'tabs--default'); + await basePage.load({ variant: 'contained', orientation: 'vertical' }); + await page.waitForTimeout(1000); + + // Act & Assert + await percySnapshot(page, 'PieTabs - Vertical Contained'); + }); + + test('should display second tab selected state', async ({ page }) => { + // Arrange + const basePage = new BasePage(page, 'tabs--default'); + await basePage.load(); + await page.waitForTimeout(1000); + + // Act - Click second tab + const secondTab = page.locator('[data-test-id="pie-tabs"] li[role="tab"]').nth(1); + await secondTab.click(); + await page.waitForTimeout(500); - await percySnapshot(page, 'PieTabs - Visual Test'); + // Assert + await percySnapshot(page, 'PieTabs - Second Tab Selected'); }); }); From bb56a12f4f6be4e9e442af8d0565acd3bb37e99a Mon Sep 17 00:00:00 2001 From: Jose De Freitas Jr Date: Tue, 5 Aug 2025 16:26:23 -0600 Subject: [PATCH 10/10] feat(pie-tabs): DSW-3303 added changeset entry --- .changeset/two-points-wash.md | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 .changeset/two-points-wash.md diff --git a/.changeset/two-points-wash.md b/.changeset/two-points-wash.md new file mode 100644 index 0000000000..ed70d2c55f --- /dev/null +++ b/.changeset/two-points-wash.md @@ -0,0 +1,6 @@ +--- +"@justeattakeaway/pie-tabs": minor +"@justeattakeaway/pie-storybook": minor +--- + +[Added] - basic styling and default properties into pie-tabs