diff --git a/libs/angular-components/.codecov.yml b/.codecov.yml similarity index 100% rename from libs/angular-components/.codecov.yml rename to .codecov.yml diff --git a/libs/angular-components/.eslintrc.json b/.eslintrc.json similarity index 97% rename from libs/angular-components/.eslintrc.json rename to .eslintrc.json index 3ee53a897..047f80b48 100644 --- a/libs/angular-components/.eslintrc.json +++ b/.eslintrc.json @@ -1,7 +1,7 @@ { "overrides": [ { - "files": ["*.ts"], + "files": ["*.ts", "**/*.ts"], "parserOptions": { "project": ["tsconfig.eslint.json"] }, diff --git a/.github/workflows/angular-release.yml b/.github/workflows/angular-release.yml index 4986521a6..9303b465e 100644 --- a/.github/workflows/angular-release.yml +++ b/.github/workflows/angular-release.yml @@ -5,8 +5,6 @@ on: branches: - rc - main - paths-ignore: - - "libs/react-components/**" jobs: lint: @@ -22,8 +20,6 @@ jobs: node-version: "lts/*" - name: Lint run: | - npm ci --cache .npm --prefer-offline - cd libs/angular-components npm ci --cache .npm --prefer-offline npm run lint @@ -38,8 +34,6 @@ jobs: node-version: "lts/*" - name: Test run: | - npm ci --cache .npm --prefer-offline - cd libs/angular-components npm ci --cache .npm --prefer-offline npm run test @@ -59,16 +53,12 @@ jobs: - name: Install dependencies run: | npm ci --cache .npm --prefer-offline - cd libs/angular-components - npm ci --cache .npm --prefer-offline - name: Build Storybook run: | - cd libs/angular-components npm run build:sb - name: Publish to Chromatic run: | - cd libs/angular-components - CHROMATIC_PROJECT_TOKEN=${{ secrets.CHROMATIC_TOKEN_ANGULAR }} npm run chromatic + CHROMATIC_PROJECT_TOKEN=${{ secrets.CHROMATIC_TOKEN }} npm run chromatic build-and-deploy: needs: [lint, test] @@ -88,8 +78,6 @@ jobs: run: | HUSKY=0 npm ci - cd libs/angular-components - npm ci npm run build git pull npm run release diff --git a/.github/workflows/angular-test-and-lint.yml b/.github/workflows/angular-test-and-lint.yml index 8905b1dab..b0f22c744 100644 --- a/.github/workflows/angular-test-and-lint.yml +++ b/.github/workflows/angular-test-and-lint.yml @@ -5,8 +5,6 @@ on: branches: - main - rc - paths-ignore: - - "libs/react-components/**" jobs: lint: runs-on: ubuntu-latest @@ -21,8 +19,6 @@ jobs: node-version: "lts/*" - name: Lint run: | - npm ci --cache .npm --prefer-offline - cd libs/angular-components npm ci --cache .npm --prefer-offline npm run lint @@ -37,8 +33,6 @@ jobs: node-version: "lts/*" - name: Test run: | - npm ci --cache .npm --prefer-offline - cd libs/angular-components npm ci --cache .npm --prefer-offline npm run test @@ -56,19 +50,16 @@ jobs: - name: Install dependencies run: | npm ci --cache .npm --prefer-offline - cd libs/angular-components - npm ci --cache .npm --prefer-offline - name: Run coverage tests run: | - cd libs/angular-components npm run test:coverage - name: Upload results to Codecov uses: codecov/codecov-action@v5 with: token: ${{ secrets.CODECOV_TOKEN }} - working-directory: ./libs/angular-components + working-directory: ./ chromatic: runs-on: ubuntu-latest @@ -98,16 +89,12 @@ jobs: - name: Install dependencies run: | npm ci --cache .npm --prefer-offline - cd libs/angular-components - npm ci --cache .npm --prefer-offline - name: Build Storybook run: | - cd libs/angular-components npm run build:sb - name: Publish to Chromatic run: | - cd libs/angular-components - echo "CHROMATIC_PROJECT_TOKEN: ${{ secrets.CHROMATIC_TOKEN_ANGULAR }}" - CHROMATIC_PROJECT_TOKEN=${{ secrets.CHROMATIC_TOKEN_ANGULAR }} npm run chromatic + echo "CHROMATIC_PROJECT_TOKEN: ${{ secrets.CHROMATIC_TOKEN }}" + CHROMATIC_PROJECT_TOKEN=${{ secrets.CHROMATIC_TOKEN }} npm run chromatic diff --git a/.github/workflows/core-release.yml b/.github/workflows/core-release.yml deleted file mode 100644 index b1f2b8f3d..000000000 --- a/.github/workflows/core-release.yml +++ /dev/null @@ -1,36 +0,0 @@ -name: Release TEDI Core library - -on: - push: - branches: - - rc - paths-ignore: - - "libs/react-components/**" - - "libs/angular-components/**" - -jobs: - build-and-deploy: - runs-on: ubuntu-latest - permissions: - contents: write - steps: - - name: Checkout code - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - name: Use Node.js - uses: actions/setup-node@v4 - with: - node-version: "lts/*" - - name: Build and publish npm package - run: | - HUSKY=0 - npm ci - cd libs/tedi-core - npm ci - npm run build - git pull - npm run release - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - NPM_TOKEN: ${{ secrets.NPMJS_TEDI_AUTH_TOKEN }} diff --git a/.github/workflows/deploy-github-pages.yml b/.github/workflows/deploy-github-pages.yml index 0f3baeceb..896bdecc5 100644 --- a/.github/workflows/deploy-github-pages.yml +++ b/.github/workflows/deploy-github-pages.yml @@ -2,19 +2,16 @@ name: Continuous Deployment on: workflow_dispatch: - push: branches: - "**" -# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages permissions: contents: read pages: write id-token: write actions: read -# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued. concurrency: group: "pages" cancel-in-progress: true @@ -37,8 +34,7 @@ jobs: build: runs-on: ubuntu-latest name: Build - needs: - - build_matrix + needs: build_matrix strategy: fail-fast: false matrix: @@ -52,19 +48,12 @@ jobs: - uses: actions/setup-node@v4 with: node-version: 20 - - name: Build + - name: Install and Build Storybook run: | - npm ci - cd libs/angular-components - npm ci - npm run build:sb - cd ../react-components npm ci npm run build:sb - cd ../.. - mkdir compiled-storybook - mv libs/angular-components/dist/storybook-static compiled-storybook/angular - mv libs/react-components/dist/storybook/react-components compiled-storybook/react + mkdir -p compiled-storybook + mv dist/storybook-static compiled-storybook/ - uses: actions/upload-artifact@v4 with: name: ${{ strategy.job-index }} @@ -89,13 +78,13 @@ jobs: name: "Download artifacts" with: path: branches/ - - name: Create public folder + - name: Create gpages folder shell: bash run: | ls -la branches - mkdir public - mv .github/workflows/index.html ./public/index.html - cd public + mkdir gpages + mv .github/workflows/index.html ./gpages/index.html + cd gpages branches=$(echo '${{ needs.build_matrix.outputs.json_branches }}' | jq -r '.[]') valid_branches="" i=-1 @@ -119,7 +108,7 @@ jobs: - name: Upload artifact uses: actions/upload-pages-artifact@v3 with: - path: ./public/ + path: ./gpages/ - name: Deploy to GitHub Pages id: deployment uses: actions/deploy-pages@v4 diff --git a/.github/workflows/index_suffix.html b/.github/workflows/index_suffix.html index baef21e45..d6936cf10 100644 --- a/.github/workflows/index_suffix.html +++ b/.github/workflows/index_suffix.html @@ -3,16 +3,15 @@ let featureBranchRoot = document.getElementById("feature-branches"); branches.forEach(branch => { -let li = document.createElement("li"); + let li = document.createElement("li"); + li.className = "py-2"; + li.innerHTML = `${branch} - View`; -li.className = "py-2"; -li.innerHTML = `${branch} - React | Angular`; - -if (mainBranches.indexOf(branch) !== -1) { -mainBranchRoot.appendChild(li); -} else { -featureBranchRoot.appendChild(li); -} + if (mainBranches.indexOf(branch) !== -1) { + mainBranchRoot.appendChild(li); + } else { + featureBranchRoot.appendChild(li); + } }); }); diff --git a/.github/workflows/publish_mirror.sh b/.github/workflows/publish_mirror.sh deleted file mode 100755 index 443141f7e..000000000 --- a/.github/workflows/publish_mirror.sh +++ /dev/null @@ -1,21 +0,0 @@ -#!/bin/bash - -package_json="dist/package.json" - -ignorable_version="0.0.0-semantic-version" -current_version=$(grep version $package_json | awk -F \" '{print $4}') - -echo "$ignorable_version != $current_version" -if [ "$ignorable_version" != "$current_version" ] ; then - echo "Overriding package json with new name" - echo "Running sed with \"s/$1/$2/g\" on $package_json" - sed -i "s/$1/$2/g" $package_json - - cd dist - cat package.json - echo "Running npm publish" - npm publish --userconfig ../.npmrc -else - echo "versions $ignorable_version and $current_version match, aborting!" -fi - diff --git a/.github/workflows/react-release.yml b/.github/workflows/react-release.yml deleted file mode 100644 index 658478a40..000000000 --- a/.github/workflows/react-release.yml +++ /dev/null @@ -1,100 +0,0 @@ -name: Release TEDI React library - -on: - push: - branches: - - rc - - main - paths-ignore: - - "libs/angular-components/**" - -jobs: - lint: - runs-on: ubuntu-latest - steps: - - name: Checkout code - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - name: Setup Node.js - uses: actions/setup-node@v4 - with: - node-version: "lts/*" - - name: Lint - run: | - npm ci --cache .npm --prefer-offline - cd libs/react-components - npm ci --cache .npm --prefer-offline - npm run lint - - test: - runs-on: ubuntu-latest - steps: - - name: Checkout code - uses: actions/checkout@v4 - - name: Setup Node.js - uses: actions/setup-node@v4 - with: - node-version: "lts/*" - - name: Test - run: | - npm ci --cache .npm --prefer-offline - cd libs/react-components - npm ci --cache .npm --prefer-offline - npm run test - - chromatic: - needs: [lint, test] - runs-on: ubuntu-latest - if: github.event_name == 'push' && (github.ref == 'refs/heads/rc' || github.ref == 'refs/heads/main') - steps: - - name: Checkout code - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - name: Setup Node.js - uses: actions/setup-node@v4 - with: - node-version: "lts/*" - - name: Install dependencies - run: | - npm ci --cache .npm --prefer-offline - cd libs/react-components - npm ci --cache .npm --prefer-offline - - name: Build Storybook - run: | - cd libs/react-components - npm run build:sb - - name: Publish to Chromatic - run: | - cd libs/react-components - CHROMATIC_PROJECT_TOKEN=${{ secrets.CHROMATIC_TOKEN_REACT }} npm run chromatic - - build-and-deploy: - needs: [lint, test] - runs-on: ubuntu-latest - permissions: - contents: write - steps: - - name: Checkout code - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - name: Use Node.js - uses: actions/setup-node@v4 - with: - node-version: "lts/*" - - name: Build and publish npm package - run: | - HUSKY=0 - npm ci - cd libs/react-components - npm ci - npm run build - ls -la dist - git pull - echo "//registry.npmjs.org/:_authToken=$NPM_TOKEN" > .npmrc - npm run release - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - NPM_TOKEN: ${{ secrets.NPMJS_TEDI_AUTH_TOKEN }} diff --git a/.github/workflows/react-test-and-lint.yml b/.github/workflows/react-test-and-lint.yml deleted file mode 100644 index a1f429770..000000000 --- a/.github/workflows/react-test-and-lint.yml +++ /dev/null @@ -1,109 +0,0 @@ -name: Test and lint React - -on: - pull_request: - branches: - - main - - rc - paths-ignore: - - "libs/angular-components/**" - -jobs: - lint: - runs-on: ubuntu-latest - steps: - - name: Checkout code - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - name: Setup Node.js - uses: actions/setup-node@v4 - with: - node-version: "lts/*" - - name: Lint - run: | - npm ci --cache .npm --prefer-offline - cd libs/react-components - npm ci --cache .npm --prefer-offline - npm run lint - - test: - runs-on: ubuntu-latest - steps: - - name: Checkout code - uses: actions/checkout@v4 - - name: Setup Node.js - uses: actions/setup-node@v4 - with: - node-version: "lts/*" - - name: Test - run: | - npm ci --cache .npm --prefer-offline - cd libs/react-components - npm ci --cache .npm --prefer-offline - npm run test - - test-coverage: - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - - name: Set up Node - uses: actions/setup-node@v4 - - - name: Install dependencies - run: | - npm ci --cache .npm --prefer-offline - cd libs/react-components - npm ci --cache .npm --prefer-offline - - - name: Run coverage tests - run: | - cd libs/react-components - npm run test:coverage - - - name: Upload results to Codecov - uses: codecov/codecov-action@v5 - with: - token: ${{ secrets.CODECOV_TOKEN }} - working-directory: ./libs/react-components - - chromatic: - runs-on: ubuntu-latest - if: | - ( - github.event_name == 'pull_request' && - github.event.pull_request.head.repo.full_name == github.repository && - github.event.pull_request.draft == false && - github.actor != 'dependabot[bot]' - ) || ( - github.event_name == 'push' && - github.ref == 'refs/heads/rc' - ) - steps: - - name: Checkout - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - - name: Set up Node - uses: actions/setup-node@v4 - - - name: Install dependencies - run: | - npm ci --cache .npm --prefer-offline - cd libs/react-components - npm ci --cache .npm --prefer-offline - - - name: Build Storybook - run: | - cd libs/react-components - npm run build:sb - - - name: Publish to Chromatic - run: | - cd libs/react-components - CHROMATIC_PROJECT_TOKEN=${{ secrets.CHROMATIC_TOKEN_REACT }} npm run chromatic diff --git a/.gitignore b/.gitignore index d53aa3841..7b7211861 100644 --- a/.gitignore +++ b/.gitignore @@ -4,9 +4,6 @@ # compiled output /dist -/libs/react-components/dist -/libs/angular-components/dist -/libs/tedi-core/dist /tmp /out-tsc @@ -57,5 +54,4 @@ documentation.json *storybook.log # Coverage -/libs/react-components/coverage -/libs/angular-components/coverage +/coverage diff --git a/libs/angular-components/.releaserc.json b/.releaserc.json similarity index 100% rename from libs/angular-components/.releaserc.json rename to .releaserc.json diff --git a/libs/angular-components/.storybook/main.ts b/.storybook/main.ts similarity index 72% rename from libs/angular-components/.storybook/main.ts rename to .storybook/main.ts index 62257f4d3..dc6d5fd37 100644 --- a/libs/angular-components/.storybook/main.ts +++ b/.storybook/main.ts @@ -2,12 +2,12 @@ import { StorybookConfig } from "@storybook/angular"; const config: StorybookConfig = { stories: [ - "../docs/welcome.mdx", - "../docs/get-started.mdx", - "../docs/changelog.mdx", - "../docs/badges.mdx", - "../docs/colors/colors.mdx", - "../docs/**/*.stories.@(js|jsx|mjs|ts|tsx)", + "../src/docs/welcome.mdx", + "../src/docs/get-started.mdx", + "../src/docs/changelog.mdx", + "../src/docs/badges.mdx", + "../src/docs/colors/colors.mdx", + "../src/docs/**/*.stories.@(js|jsx|mjs|ts|tsx)", "../tedi/**/*.stories.@(js|jsx|mjs|ts|tsx)", "../community/**/*.stories.@(js|jsx|mjs|ts|tsx)", ], @@ -25,7 +25,7 @@ const config: StorybookConfig = { builder: "angular", }, }, - staticDirs: ["../../tedi-core/public"], + staticDirs: ["../public"], docs: { autodocs: true, }, diff --git a/libs/angular-components/.storybook/manager-head.html b/.storybook/manager-head.html similarity index 100% rename from libs/angular-components/.storybook/manager-head.html rename to .storybook/manager-head.html diff --git a/libs/angular-components/.storybook/manager.js b/.storybook/manager.js similarity index 100% rename from libs/angular-components/.storybook/manager.js rename to .storybook/manager.js diff --git a/libs/angular-components/.storybook/preview-head.html b/.storybook/preview-head.html similarity index 100% rename from libs/angular-components/.storybook/preview-head.html rename to .storybook/preview-head.html diff --git a/libs/angular-components/.storybook/preview.tsx b/.storybook/preview.tsx similarity index 100% rename from libs/angular-components/.storybook/preview.tsx rename to .storybook/preview.tsx diff --git a/libs/angular-components/.storybook/tehik-theme.js b/.storybook/tehik-theme.js similarity index 100% rename from libs/angular-components/.storybook/tehik-theme.js rename to .storybook/tehik-theme.js diff --git a/libs/angular-components/.storybook/tsconfig.json b/.storybook/tsconfig.json similarity index 100% rename from libs/angular-components/.storybook/tsconfig.json rename to .storybook/tsconfig.json diff --git a/libs/angular-components/.storybook/typings.d.ts b/.storybook/typings.d.ts similarity index 100% rename from libs/angular-components/.storybook/typings.d.ts rename to .storybook/typings.d.ts diff --git a/libs/angular-components/CHANGELOG.md b/CHANGELOG.md similarity index 98% rename from libs/angular-components/CHANGELOG.md rename to CHANGELOG.md index 0c69d9b36..1f36db57f 100644 --- a/libs/angular-components/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,48 @@ +# [3.0.0-rc.1](https://github.com/TEDI-Design-System/angular/compare/angular-2.5.0-rc.2...angular-3.0.0-rc.1) (2025-10-10) + + +### Bug Fixes + +* **alert,toast:** enhance accessibility, add titleElement prop to override default heading element, better Toast stories, add documentation [#1009](https://github.com/TEDI-Design-System/angular/issues/1009) ([#1126](https://github.com/TEDI-Design-System/angular/issues/1126)) ([f922907](https://github.com/TEDI-Design-System/angular/commit/f92290772259b8b3ff19a2cc5a1f1d9e9b24d610)) +* **select:** fix label and placeholder accessibility [#1014](https://github.com/TEDI-Design-System/angular/issues/1014) ([#1130](https://github.com/TEDI-Design-System/angular/issues/1130)) ([649b52b](https://github.com/TEDI-Design-System/angular/commit/649b52b8b221293c7a9d5ad26695b4777e1bb94e)) +* **separator:** fix stories for 9.1.4.10 criteria [#1007](https://github.com/TEDI-Design-System/angular/issues/1007) ([#1125](https://github.com/TEDI-Design-System/angular/issues/1125)) ([80d9ac8](https://github.com/TEDI-Design-System/angular/commit/80d9ac82ebb75418e1f2debd236b3ad5cedbf1d6)) +* **textfield:** fix input param passing [#1132](https://github.com/TEDI-Design-System/angular/issues/1132) ([#1166](https://github.com/TEDI-Design-System/angular/issues/1166)) ([6380a9a](https://github.com/TEDI-Design-System/angular/commit/6380a9a82637ea360c4d0622799cc7591ef3486d)) + + +### Code Refactoring + +* **angular:** move Angular to standalone repo [#148](https://github.com/TEDI-Design-System/angular/issues/148) ([ce0dcfd](https://github.com/TEDI-Design-System/angular/commit/ce0dcfd02df7b88c389f7d1afe752545d5b63377)) + + +### Features + +* **vertical-stepper:** design changes and additional slot for info components [#512](https://github.com/TEDI-Design-System/angular/issues/512) ([#1131](https://github.com/TEDI-Design-System/angular/issues/1131)) ([1d4f0da](https://github.com/TEDI-Design-System/angular/commit/1d4f0da901e5810de64c4acaa4e566a445d4af18)) + + +### BREAKING CHANGES + +* **angular:** the package name has changed. +Update all imports and dependencies to use the new package name. + +# [@tehik-ee/tedi-angular-v2.5.0-rc.2](https://github.com/TEHIK-EE/tedi-design-system/compare/angular-2.5.0-rc.1...angular-2.5.0-rc.2) (2025-10-01) + + +### Bug Fixes + +* **file-dropzone:** better error message [#860](https://github.com/TEHIK-EE/tedi-design-system/issues/860) ([90638d8](https://github.com/TEHIK-EE/tedi-design-system/commit/90638d8177485e52a0f8b0a1f61e9f53ab52fb12)) + + +### Features + +* **file-dropzone:** add toggle for SI and IEC standards of showing filesize fix filesize [#860](https://github.com/TEHIK-EE/tedi-design-system/issues/860) ([162cae1](https://github.com/TEHIK-EE/tedi-design-system/commit/162cae130dabd4c9967165331d46c3fe36512a84)) + +# [@tehik-ee/tedi-angular-v2.5.0-rc.1](https://github.com/TEHIK-EE/tedi-design-system/compare/angular-2.4.0...angular-2.5.0-rc.1) (2025-09-29) + + +### Features + +* **timeline:** TEDI-Ready component [#1094](https://github.com/TEHIK-EE/tedi-design-system/issues/1094) ([#1122](https://github.com/TEHIK-EE/tedi-design-system/issues/1122)) ([c6e9fb4](https://github.com/TEHIK-EE/tedi-design-system/commit/c6e9fb44653b04dee705a9ffd2718045bc3d22ec)) + # [@tehik-ee/tedi-angular-v2.4.0](https://github.com/TEHIK-EE/tedi-design-system/compare/angular-2.3.0...angular-2.4.0) (2025-09-25) diff --git a/README.md b/README.md index b2838c382..2e7b3d3ca 100644 --- a/README.md +++ b/README.md @@ -1,68 +1,44 @@ -# TEDI Design System +# TEDI Design System for Angular [![codecov](https://codecov.io/gh/TEHIK-EE/tedi-design-system/graph/badge.svg?token=NKNNJSG19D)](https://codecov.io/gh/TEHIK-EE/tedi-design-system) [![semantic-release](https://img.shields.io/badge/semantic--release-e10079?logo=semantic-release)](https://github.com/semantic-release/semantic-release) -The TEDI Design System provides reusable UI components for multiple frameworks. +`@tedi-design-system/angular` is a library of Angular components implementing the TEDI Design System. +It provides reusable, accessible, and consistent UI components to streamline building Angular applications. -Usage instructions can be found in the [wiki](https://github.com/TEHIK-EE/tedi-design-system/wiki). +Usage instructions and detailed documentation can be found in the [TEDI Design System wiki](https://github.com/TEDI-Design-System/general). --- -## Development Guide - -### Setting Up the Project - -Components are separated into packages by framework. Each package is managed separately. - -#### 1. Installing dependencies. - -Run the following command at the root to install shared dependencies: +## Installation -``` -npm install -``` - -Then, install dependencies for the specific library you are working on: - -For **React components**: +Install the dependencies for your Angular library: ``` -npm run install:react +npm run i ``` -For **Angular components**: - -``` -npm run install:angular -``` - -Alternatively, you can install everything from root `package.json`: +--- -``` - npm run install:all -``` +## Development Guide -#### 2. Running Storybook +### Running Storybook -Run your desired Storybook from root: +To view and develop components in isolation, start Storybook for Angular: ``` -npm run start:react -npm run start:angular +npm run start ``` ---- - -### **Additional Resources** +## Contributing -Check the [wiki](https://github.com/TEHIK-EE/tedi-design-system/wiki) for detailed documentation. -Report issues or contribute via [GitHub Issues](https://github.com/TEHIK-EE/tedi-design-system/issues). +Check the [wiki](https://github.com/TEDI-Design-System/general) for component guidelines and coding standards. +Report issues or contribute via [GitHub Issues](https://github.com/TEDI-Design-System/angular/issues). --- -### Thanks +## Visual Testing Chromatic -Thanks to [Chromatic](https://www.chromatic.com/) for providing the visual testing platform that helps us review UI changes and catch visual regressions. +We use [Chromatic](https://www.chromatic.com/) for visual testing, reviewing UI changes, and preventing visual regressions. diff --git a/libs/angular-components/angular.json b/angular.json similarity index 81% rename from libs/angular-components/angular.json rename to angular.json index c2525478f..59e9c5f30 100644 --- a/libs/angular-components/angular.json +++ b/angular.json @@ -5,8 +5,8 @@ "angular-components": { "projectType": "library", "prefix": "tedi", - "root": ".", - "sourceRoot": ".", + "root": "src/", + "sourceRoot": "src/", "architect": { "build": { "builder": "@angular/build:ng-packagr", @@ -32,8 +32,8 @@ "compodocArgs": ["-e", "json", "-d", "."], "port": 6006, "styles": [ - "styles/index", - "../tedi-core/src/tedi-storybook-styles" + "./src/styles/index", + "./node_modules/@tedi-design-system/core/index.scss" ], "experimentalZoneless": true } @@ -46,8 +46,8 @@ "compodocArgs": ["-e", "json", "-d", "."], "outputDir": "dist/storybook-static", "styles": [ - "styles/index", - "../tedi-core/src/tedi-storybook-styles" + "./src/styles/index", + "./node_modules/@tedi-design-system/core/index.scss" ], "experimentalZoneless": true } @@ -56,12 +56,12 @@ "builder": "@angular-eslint/builder:lint", "options": { "lintFilePatterns": [ - "docs/**/*.ts", - "docs/**/*.html", - "tedi/**/*.ts", - "tedi/**/*.html", - "community/**/*.ts", - "community/**/*.html" + "./src/docs/**/*.ts", + "./src/docs/**/*.html", + "./tedi/**/*.ts", + "./tedi/**/*.html", + "./community/**/*.ts", + "./community/**/*.html" ] } } diff --git a/libs/angular-components/chromatic.config.json b/chromatic.config.json similarity index 100% rename from libs/angular-components/chromatic.config.json rename to chromatic.config.json diff --git a/libs/angular-components/community/components/cards/accordion/accordion-icon/accordion-icon.component.html b/community/components/cards/accordion/accordion-icon/accordion-icon.component.html similarity index 100% rename from libs/angular-components/community/components/cards/accordion/accordion-icon/accordion-icon.component.html rename to community/components/cards/accordion/accordion-icon/accordion-icon.component.html diff --git a/libs/angular-components/community/components/cards/accordion/accordion-icon/accordion-icon.component.scss b/community/components/cards/accordion/accordion-icon/accordion-icon.component.scss similarity index 100% rename from libs/angular-components/community/components/cards/accordion/accordion-icon/accordion-icon.component.scss rename to community/components/cards/accordion/accordion-icon/accordion-icon.component.scss diff --git a/libs/angular-components/community/components/cards/accordion/accordion-icon/accordion-icon.component.ts b/community/components/cards/accordion/accordion-icon/accordion-icon.component.ts similarity index 100% rename from libs/angular-components/community/components/cards/accordion/accordion-icon/accordion-icon.component.ts rename to community/components/cards/accordion/accordion-icon/accordion-icon.component.ts diff --git a/libs/angular-components/community/components/cards/accordion/accordion-item-content/accordion-item-content.component.html b/community/components/cards/accordion/accordion-item-content/accordion-item-content.component.html similarity index 100% rename from libs/angular-components/community/components/cards/accordion/accordion-item-content/accordion-item-content.component.html rename to community/components/cards/accordion/accordion-item-content/accordion-item-content.component.html diff --git a/libs/angular-components/community/components/cards/accordion/accordion-item-content/accordion-item-content.component.scss b/community/components/cards/accordion/accordion-item-content/accordion-item-content.component.scss similarity index 100% rename from libs/angular-components/community/components/cards/accordion/accordion-item-content/accordion-item-content.component.scss rename to community/components/cards/accordion/accordion-item-content/accordion-item-content.component.scss diff --git a/libs/angular-components/community/components/cards/accordion/accordion-item-content/accordion-item-content.component.ts b/community/components/cards/accordion/accordion-item-content/accordion-item-content.component.ts similarity index 100% rename from libs/angular-components/community/components/cards/accordion/accordion-item-content/accordion-item-content.component.ts rename to community/components/cards/accordion/accordion-item-content/accordion-item-content.component.ts diff --git a/libs/angular-components/community/components/cards/accordion/accordion-item-header/accordion-item-header.component.html b/community/components/cards/accordion/accordion-item-header/accordion-item-header.component.html similarity index 100% rename from libs/angular-components/community/components/cards/accordion/accordion-item-header/accordion-item-header.component.html rename to community/components/cards/accordion/accordion-item-header/accordion-item-header.component.html diff --git a/libs/angular-components/community/components/cards/accordion/accordion-item-header/accordion-item-header.component.scss b/community/components/cards/accordion/accordion-item-header/accordion-item-header.component.scss similarity index 100% rename from libs/angular-components/community/components/cards/accordion/accordion-item-header/accordion-item-header.component.scss rename to community/components/cards/accordion/accordion-item-header/accordion-item-header.component.scss diff --git a/libs/angular-components/community/components/cards/accordion/accordion-item-header/accordion-item-header.component.ts b/community/components/cards/accordion/accordion-item-header/accordion-item-header.component.ts similarity index 95% rename from libs/angular-components/community/components/cards/accordion/accordion-item-header/accordion-item-header.component.ts rename to community/components/cards/accordion/accordion-item-header/accordion-item-header.component.ts index c1c85edf0..6c77a811d 100644 --- a/libs/angular-components/community/components/cards/accordion/accordion-item-header/accordion-item-header.component.ts +++ b/community/components/cards/accordion/accordion-item-header/accordion-item-header.component.ts @@ -12,7 +12,7 @@ import { CardHeaderComponent, CardHeaderVariant, } from "../../card/card-header/card-header.component"; -import { IconColor, IconComponent, ButtonComponent } from "@tehik-ee/tedi-angular/tedi"; +import { IconColor, IconComponent, ButtonComponent } from "@tedi-design-system/angular/tedi"; const WHITE_ICON_VARIANTS = ["brand", "brand-dark"]; diff --git a/libs/angular-components/community/components/cards/accordion/accordion-item/accordion-item.component.html b/community/components/cards/accordion/accordion-item/accordion-item.component.html similarity index 100% rename from libs/angular-components/community/components/cards/accordion/accordion-item/accordion-item.component.html rename to community/components/cards/accordion/accordion-item/accordion-item.component.html diff --git a/libs/angular-components/community/components/cards/accordion/accordion-item/accordion-item.component.scss b/community/components/cards/accordion/accordion-item/accordion-item.component.scss similarity index 100% rename from libs/angular-components/community/components/cards/accordion/accordion-item/accordion-item.component.scss rename to community/components/cards/accordion/accordion-item/accordion-item.component.scss diff --git a/libs/angular-components/community/components/cards/accordion/accordion-item/accordion-item.component.ts b/community/components/cards/accordion/accordion-item/accordion-item.component.ts similarity index 100% rename from libs/angular-components/community/components/cards/accordion/accordion-item/accordion-item.component.ts rename to community/components/cards/accordion/accordion-item/accordion-item.component.ts diff --git a/libs/angular-components/community/components/cards/accordion/accordion.stories.ts b/community/components/cards/accordion/accordion.stories.ts similarity index 99% rename from libs/angular-components/community/components/cards/accordion/accordion.stories.ts rename to community/components/cards/accordion/accordion.stories.ts index 0b68b4114..9601a1e91 100644 --- a/libs/angular-components/community/components/cards/accordion/accordion.stories.ts +++ b/community/components/cards/accordion/accordion.stories.ts @@ -5,7 +5,7 @@ import { IconComponent, TextComponent, ButtonComponent, -} from "@tehik-ee/tedi-angular/tedi"; +} from "@tedi-design-system/angular/tedi"; import { AccordionIconComponent } from "./accordion-icon/accordion-icon.component"; import { AccordionItemContentComponent } from "./accordion-item-content/accordion-item-content.component"; import { AccordionItemHeaderComponent } from "./accordion-item-header/accordion-item-header.component"; diff --git a/libs/angular-components/community/components/cards/accordion/accordion/accordion.component.html b/community/components/cards/accordion/accordion/accordion.component.html similarity index 100% rename from libs/angular-components/community/components/cards/accordion/accordion/accordion.component.html rename to community/components/cards/accordion/accordion/accordion.component.html diff --git a/libs/angular-components/community/components/cards/accordion/accordion/accordion.component.scss b/community/components/cards/accordion/accordion/accordion.component.scss similarity index 100% rename from libs/angular-components/community/components/cards/accordion/accordion/accordion.component.scss rename to community/components/cards/accordion/accordion/accordion.component.scss diff --git a/libs/angular-components/community/components/cards/accordion/accordion/accordion.component.ts b/community/components/cards/accordion/accordion/accordion.component.ts similarity index 100% rename from libs/angular-components/community/components/cards/accordion/accordion/accordion.component.ts rename to community/components/cards/accordion/accordion/accordion.component.ts diff --git a/libs/angular-components/community/components/cards/accordion/index.ts b/community/components/cards/accordion/index.ts similarity index 100% rename from libs/angular-components/community/components/cards/accordion/index.ts rename to community/components/cards/accordion/index.ts diff --git a/libs/angular-components/community/components/cards/card/_card-mixins.scss b/community/components/cards/card/_card-mixins.scss similarity index 98% rename from libs/angular-components/community/components/cards/card/_card-mixins.scss rename to community/components/cards/card/_card-mixins.scss index d80adf695..e17f63670 100644 --- a/libs/angular-components/community/components/cards/card/_card-mixins.scss +++ b/community/components/cards/card/_card-mixins.scss @@ -1,4 +1,4 @@ -@use "@tehik-ee/tedi-core/mixins"; +@use "@tedi-design-system/core/mixins"; @use "sass:map"; $card-colors: ( diff --git a/libs/angular-components/community/components/cards/card/card-colors.directive.ts b/community/components/cards/card/card-colors.directive.ts similarity index 100% rename from libs/angular-components/community/components/cards/card/card-colors.directive.ts rename to community/components/cards/card/card-colors.directive.ts diff --git a/libs/angular-components/community/components/cards/card/card-content/card-content.component.html b/community/components/cards/card/card-content/card-content.component.html similarity index 100% rename from libs/angular-components/community/components/cards/card/card-content/card-content.component.html rename to community/components/cards/card/card-content/card-content.component.html diff --git a/libs/angular-components/community/components/cards/card/card-content/card-content.component.scss b/community/components/cards/card/card-content/card-content.component.scss similarity index 100% rename from libs/angular-components/community/components/cards/card/card-content/card-content.component.scss rename to community/components/cards/card/card-content/card-content.component.scss diff --git a/libs/angular-components/community/components/cards/card/card-content/card-content.component.ts b/community/components/cards/card/card-content/card-content.component.ts similarity index 100% rename from libs/angular-components/community/components/cards/card/card-content/card-content.component.ts rename to community/components/cards/card/card-content/card-content.component.ts diff --git a/libs/angular-components/community/components/cards/card/card-header/card-header.component.html b/community/components/cards/card/card-header/card-header.component.html similarity index 100% rename from libs/angular-components/community/components/cards/card/card-header/card-header.component.html rename to community/components/cards/card/card-header/card-header.component.html diff --git a/libs/angular-components/community/components/cards/card/card-header/card-header.component.scss b/community/components/cards/card/card-header/card-header.component.scss similarity index 100% rename from libs/angular-components/community/components/cards/card/card-header/card-header.component.scss rename to community/components/cards/card/card-header/card-header.component.scss diff --git a/libs/angular-components/community/components/cards/card/card-header/card-header.component.ts b/community/components/cards/card/card-header/card-header.component.ts similarity index 100% rename from libs/angular-components/community/components/cards/card/card-header/card-header.component.ts rename to community/components/cards/card/card-header/card-header.component.ts diff --git a/libs/angular-components/community/components/cards/card/card-padding.directive.ts b/community/components/cards/card/card-padding.directive.ts similarity index 100% rename from libs/angular-components/community/components/cards/card/card-padding.directive.ts rename to community/components/cards/card/card-padding.directive.ts diff --git a/libs/angular-components/community/components/cards/card/card-row/card-row.component.html b/community/components/cards/card/card-row/card-row.component.html similarity index 100% rename from libs/angular-components/community/components/cards/card/card-row/card-row.component.html rename to community/components/cards/card/card-row/card-row.component.html diff --git a/libs/angular-components/community/components/cards/card/card-row/card-row.component.scss b/community/components/cards/card/card-row/card-row.component.scss similarity index 100% rename from libs/angular-components/community/components/cards/card/card-row/card-row.component.scss rename to community/components/cards/card/card-row/card-row.component.scss diff --git a/libs/angular-components/community/components/cards/card/card-row/card-row.component.ts b/community/components/cards/card/card-row/card-row.component.ts similarity index 100% rename from libs/angular-components/community/components/cards/card/card-row/card-row.component.ts rename to community/components/cards/card/card-row/card-row.component.ts diff --git a/libs/angular-components/community/components/cards/card/card.component.html b/community/components/cards/card/card.component.html similarity index 100% rename from libs/angular-components/community/components/cards/card/card.component.html rename to community/components/cards/card/card.component.html diff --git a/libs/angular-components/community/components/cards/card/card.component.scss b/community/components/cards/card/card.component.scss similarity index 98% rename from libs/angular-components/community/components/cards/card/card.component.scss rename to community/components/cards/card/card.component.scss index e610ecee0..df280e01b 100644 --- a/libs/angular-components/community/components/cards/card/card.component.scss +++ b/community/components/cards/card/card.component.scss @@ -1,4 +1,4 @@ -@use "@tehik-ee/tedi-core/mixins"; +@use "@tedi-design-system/core/mixins"; @use "./card-mixins"; .tedi-card { diff --git a/libs/angular-components/community/components/cards/card/card.component.ts b/community/components/cards/card/card.component.ts similarity index 100% rename from libs/angular-components/community/components/cards/card/card.component.ts rename to community/components/cards/card/card.component.ts diff --git a/libs/angular-components/community/components/cards/card/index.ts b/community/components/cards/card/index.ts similarity index 100% rename from libs/angular-components/community/components/cards/card/index.ts rename to community/components/cards/card/index.ts diff --git a/libs/angular-components/community/components/cards/card/stories/card-story-templates.ts b/community/components/cards/card/stories/card-story-templates.ts similarity index 100% rename from libs/angular-components/community/components/cards/card/stories/card-story-templates.ts rename to community/components/cards/card/stories/card-story-templates.ts diff --git a/libs/angular-components/community/components/cards/card/stories/card.stories.ts b/community/components/cards/card/stories/card.stories.ts similarity index 97% rename from libs/angular-components/community/components/cards/card/stories/card.stories.ts rename to community/components/cards/card/stories/card.stories.ts index e2d8e5055..3f52620a2 100644 --- a/libs/angular-components/community/components/cards/card/stories/card.stories.ts +++ b/community/components/cards/card/stories/card.stories.ts @@ -19,7 +19,7 @@ import { CardRowComponent } from "../card-row/card-row.component"; import { CardHeaderComponent } from "../card-header/card-header.component"; import { CardContentComponent } from "../card-content/card-content.component"; import { CardComponent } from "../card.component"; -import { IconComponent, TextComponent } from "@tehik-ee/tedi-angular/tedi"; +import { IconComponent, TextComponent } from "@tedi-design-system/angular/tedi"; export default { title: "Community/Cards/Card", diff --git a/libs/angular-components/community/components/cards/index.ts b/community/components/cards/index.ts similarity index 65% rename from libs/angular-components/community/components/cards/index.ts rename to community/components/cards/index.ts index 71585fff0..303fdfc1a 100644 --- a/libs/angular-components/community/components/cards/index.ts +++ b/community/components/cards/index.ts @@ -1,3 +1,2 @@ export * from "./accordion"; export * from "./card"; -export * from "./timeline"; diff --git a/libs/angular-components/community/components/form/checkbox/checkbox-card-group/checkbox-card-group.component.ts b/community/components/form/checkbox/checkbox-card-group/checkbox-card-group.component.ts similarity index 96% rename from libs/angular-components/community/components/form/checkbox/checkbox-card-group/checkbox-card-group.component.ts rename to community/components/form/checkbox/checkbox-card-group/checkbox-card-group.component.ts index ed2e00940..43e00097c 100644 --- a/libs/angular-components/community/components/form/checkbox/checkbox-card-group/checkbox-card-group.component.ts +++ b/community/components/form/checkbox/checkbox-card-group/checkbox-card-group.component.ts @@ -11,7 +11,7 @@ import { } from "@angular/core"; import { ControlValueAccessor, NG_VALUE_ACCESSOR } from "@angular/forms"; import { ChoiceGroupDirective } from "../../choicegroup/choicegroup.directive"; -import { LabelComponent, FeedbackTextComponent } from "@tehik-ee/tedi-angular/tedi"; +import { LabelComponent, FeedbackTextComponent } from "@tedi-design-system/angular/tedi"; import { CheckboxGroupComponent } from "../checkbox-group/checkbox-group.component"; import { CheckboxComponent } from "../checkbox/checkbox.component"; diff --git a/libs/angular-components/community/components/form/checkbox/checkbox-group/checkbox-group.component.html b/community/components/form/checkbox/checkbox-group/checkbox-group.component.html similarity index 100% rename from libs/angular-components/community/components/form/checkbox/checkbox-group/checkbox-group.component.html rename to community/components/form/checkbox/checkbox-group/checkbox-group.component.html diff --git a/libs/angular-components/community/components/form/checkbox/checkbox-group/checkbox-group.component.scss b/community/components/form/checkbox/checkbox-group/checkbox-group.component.scss similarity index 100% rename from libs/angular-components/community/components/form/checkbox/checkbox-group/checkbox-group.component.scss rename to community/components/form/checkbox/checkbox-group/checkbox-group.component.scss diff --git a/libs/angular-components/community/components/form/checkbox/checkbox-group/checkbox-group.component.ts b/community/components/form/checkbox/checkbox-group/checkbox-group.component.ts similarity index 93% rename from libs/angular-components/community/components/form/checkbox/checkbox-group/checkbox-group.component.ts rename to community/components/form/checkbox/checkbox-group/checkbox-group.component.ts index c7f70b332..cd95588e7 100644 --- a/libs/angular-components/community/components/form/checkbox/checkbox-group/checkbox-group.component.ts +++ b/community/components/form/checkbox/checkbox-group/checkbox-group.component.ts @@ -10,8 +10,8 @@ import { ComponentInputs, LabelComponent, FeedbackTextComponent, -} from "@tehik-ee/tedi-angular/tedi"; -import { generateUUID } from "@tehik-ee/tedi-angular/tedi"; +} from "@tedi-design-system/angular/tedi"; +import { generateUUID } from "@tedi-design-system/angular/tedi"; @Component({ standalone: true, diff --git a/libs/angular-components/community/components/form/checkbox/checkbox.stories.ts b/community/components/form/checkbox/checkbox.stories.ts similarity index 100% rename from libs/angular-components/community/components/form/checkbox/checkbox.stories.ts rename to community/components/form/checkbox/checkbox.stories.ts diff --git a/libs/angular-components/community/components/form/checkbox/checkbox/checkbox.component.html b/community/components/form/checkbox/checkbox/checkbox.component.html similarity index 100% rename from libs/angular-components/community/components/form/checkbox/checkbox/checkbox.component.html rename to community/components/form/checkbox/checkbox/checkbox.component.html diff --git a/libs/angular-components/community/components/form/checkbox/checkbox/checkbox.component.scss b/community/components/form/checkbox/checkbox/checkbox.component.scss similarity index 98% rename from libs/angular-components/community/components/form/checkbox/checkbox/checkbox.component.scss rename to community/components/form/checkbox/checkbox/checkbox.component.scss index 4de3255ab..72a38b196 100644 --- a/libs/angular-components/community/components/form/checkbox/checkbox/checkbox.component.scss +++ b/community/components/form/checkbox/checkbox/checkbox.component.scss @@ -1,4 +1,4 @@ -@use "@tehik-ee/tedi-core/mixins"; +@use "@tedi-design-system/core/mixins"; .tedi-checkbox { --_checkbox-bg: var(--form-checkbox-radio-default-background-default); diff --git a/libs/angular-components/community/components/form/checkbox/checkbox/checkbox.component.ts b/community/components/form/checkbox/checkbox/checkbox.component.ts similarity index 97% rename from libs/angular-components/community/components/form/checkbox/checkbox/checkbox.component.ts rename to community/components/form/checkbox/checkbox/checkbox.component.ts index bdcd26ffa..21947b3dd 100644 --- a/libs/angular-components/community/components/form/checkbox/checkbox/checkbox.component.ts +++ b/community/components/form/checkbox/checkbox/checkbox.component.ts @@ -15,11 +15,11 @@ import { ComponentInputs, IconComponent, FeedbackTextComponent, -} from "@tehik-ee/tedi-angular/tedi"; +} from "@tedi-design-system/angular/tedi"; import { CheckboxCardGroupComponent } from "../checkbox-card-group/checkbox-card-group.component"; import { ControlValueAccessor, NG_VALUE_ACCESSOR } from "@angular/forms"; import { CheckboxGroupComponent } from "../checkbox-group/checkbox-group.component"; -import { generateUUID } from "@tehik-ee/tedi-angular/tedi"; +import { generateUUID } from "@tedi-design-system/angular/tedi"; export type CheckboxSize = "default" | "large"; diff --git a/libs/angular-components/community/components/form/checkbox/index.ts b/community/components/form/checkbox/index.ts similarity index 100% rename from libs/angular-components/community/components/form/checkbox/index.ts rename to community/components/form/checkbox/index.ts diff --git a/libs/angular-components/community/components/form/choicegroup/_choicegroup-card-mixins.scss b/community/components/form/choicegroup/_choicegroup-card-mixins.scss similarity index 98% rename from libs/angular-components/community/components/form/choicegroup/_choicegroup-card-mixins.scss rename to community/components/form/choicegroup/_choicegroup-card-mixins.scss index ab23e81fa..32ebccfa1 100644 --- a/libs/angular-components/community/components/form/choicegroup/_choicegroup-card-mixins.scss +++ b/community/components/form/choicegroup/_choicegroup-card-mixins.scss @@ -1,4 +1,4 @@ -@use "@tehik-ee/tedi-core/mixins"; +@use "@tedi-design-system/core/mixins"; @mixin padding-variables { --_choicegroup-card-padding-top: var(--_choicegroup-card-padding-vertical); diff --git a/libs/angular-components/community/components/form/choicegroup/choicegroup.directive.ts b/community/components/form/choicegroup/choicegroup.directive.ts similarity index 100% rename from libs/angular-components/community/components/form/choicegroup/choicegroup.directive.ts rename to community/components/form/choicegroup/choicegroup.directive.ts diff --git a/libs/angular-components/community/components/form/choicegroup/choicegroup.styles.scss b/community/components/form/choicegroup/choicegroup.styles.scss similarity index 98% rename from libs/angular-components/community/components/form/choicegroup/choicegroup.styles.scss rename to community/components/form/choicegroup/choicegroup.styles.scss index fb60e719a..66cb24c4b 100644 --- a/libs/angular-components/community/components/form/choicegroup/choicegroup.styles.scss +++ b/community/components/form/choicegroup/choicegroup.styles.scss @@ -1,4 +1,4 @@ -@use "@tehik-ee/tedi-core/mixins"; +@use "@tedi-design-system/core/mixins"; @use "./choicegroup-card-mixins"; @mixin choicegroup-card-error-variables { diff --git a/community/components/form/file-dropzone/constants.ts b/community/components/form/file-dropzone/constants.ts new file mode 100644 index 000000000..cc7709a90 --- /dev/null +++ b/community/components/form/file-dropzone/constants.ts @@ -0,0 +1,20 @@ +const IECkiloBytes = 1024; +const SIkiloBytes = 1000; + +const IECMegabytes = IECkiloBytes * IECkiloBytes; +const SIMegabytes = SIkiloBytes * SIkiloBytes; + +export enum SIFileSize { + kB = SIkiloBytes, + MB = SIMegabytes, +} + +export enum IECFileSize { + kB = IECkiloBytes, + MB = IECMegabytes, +} + +export const FileSizeStandards = { + SI: SIFileSize, + IEC: IECFileSize, +}; diff --git a/libs/angular-components/community/components/form/file-dropzone/file-dropzone.component.html b/community/components/form/file-dropzone/file-dropzone.component.html similarity index 98% rename from libs/angular-components/community/components/form/file-dropzone/file-dropzone.component.html rename to community/components/form/file-dropzone/file-dropzone.component.html index 6107debb8..e34278a21 100644 --- a/libs/angular-components/community/components/form/file-dropzone/file-dropzone.component.html +++ b/community/components/form/file-dropzone/file-dropzone.component.html @@ -2,12 +2,14 @@ #fileInput class="tedi-file-dropzone__input" type="file" + [disabled]="disabled()" [multiple]="multiple()" [id]="inputId()" [attr.webkitdirectory]="uploadFolder() ? '' : null" [accept]="accept()" (change)="selectionChange($event)" /> + - - - - - - - - - Adaptable button (Default) - -

Button width adjusts to the content.

- - - - - - - - - - - - - - - - - - Full width of container - -

- Button width can be adjusted to the width of the container it’s placed in. It’s not a very common usage. -

-
- - - -); - -const Accessibility = () => ( - - Accessibility -

- Focus: -

-

-

- Keyboard navigation: -

-

-

Does not apply for disabled and loading buttons, these buttons are skipped.

-
-); - -const ButtonType = ({ text, button }: ExampleButtonProps) => ( - - - - - - - - - - - - - - - - - - - - - {button?.color} - {text} - - - -); - -const ButtonState = ({ heading, text, button }: ExampleButtonProps) => ( - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - {heading} - - {text && {text}} - - - -); - -const LoadingButton = ({ heading, text, button }: ExampleButtonProps) => ( - - - - - - - - - - - - - - - - - {heading} - -

{text}

-
-
- -); - -export default ButtonsDocumentation; diff --git a/libs/react-components/src/community/docs/buttons/buttons.mdx b/libs/react-components/src/community/docs/buttons/buttons.mdx deleted file mode 100644 index 55c6850f0..000000000 --- a/libs/react-components/src/community/docs/buttons/buttons.mdx +++ /dev/null @@ -1,9 +0,0 @@ -import { Meta, Unstyled } from '@storybook/blocks'; - -import ButtonsDocumentation from './buttons-documentation.tsx'; - - - - - - diff --git a/libs/react-components/src/community/docs/colors/guidelines.tsx b/libs/react-components/src/community/docs/colors/guidelines.tsx deleted file mode 100644 index a8668059d..000000000 --- a/libs/react-components/src/community/docs/colors/guidelines.tsx +++ /dev/null @@ -1,120 +0,0 @@ -import { Col, Row } from '../../../tedi/components/layout/grid'; -import { VerticalSpacing, VerticalSpacingItem } from '../../../tedi/components/layout/vertical-spacing'; -import { Heading } from '../../index'; - -const ColorRows = ({ inverted }: { inverted?: boolean }) => ( - - - text (fourgrounds) examples - - - backgrounds examples - - -); - -const ColorExamples = ({ - children, - title, - inverted, -}: { - children: React.ReactNode; - title: string; - inverted?: boolean; -}) => ( - - - {title} - - {children} - {typeof inverted !== 'undefined' && } - -); - -const Neutrals = () => ( - - - Neutrals - -

- - Neutrals - {' '} - are divided into 3 main groups: -

- -
-); - -const GuideLinesDescription = () => ( - - - - Color Guidelines - - -

- The color naming convention is optimised for fast navigation and clear overview of all variables. To optimise - design hand-offs the same design tokens are used (naming convention) for{' '} - - Figma - {' '} - and development. -

-
-); - -const GuideLines = () => ( - - - - -

- In order to keep the color palette simple, brand and functional colors are not divided into the previous - categories. Nevertheless, these colors can be used as bg, text and borders in a variety of ways to cover all the - design needs. -

-

- Keep in mind that all colors have to be used in accordance to the WCAG level AA contrast standards. In general - overlaying “text” color tokens on “background” color tokens will guarantee at least AA level contrast. -

-
- -

- Inverted colors are used for inverted sections like footers, accent cards and so on. Dark mode is not available - yet. -

-
- -

- - text-disabled - {' '} - and{' '} - - bg-disabled - {' '} - are designed to express disabled states. Keep in mind that disabled elements should be used with case and only - if absolutely necessary. It’s good practice to consider other design patterns if possible. -

- disabled examples -
-
-); - -export default GuideLines; diff --git a/libs/react-components/src/community/docs/scale-layout/breaking-points.tsx b/libs/react-components/src/community/docs/scale-layout/breaking-points.tsx deleted file mode 100644 index 6df2ef774..000000000 --- a/libs/react-components/src/community/docs/scale-layout/breaking-points.tsx +++ /dev/null @@ -1,125 +0,0 @@ -import { ColumnDef, createColumnHelper } from '@tanstack/react-table'; - -import { Breakpoint, CustomizeTableCell, getBackgroundColorClass, Table, Text } from '../../index'; - -interface BreakpointRow { - type: 'mobile' | 'tablet' | 'desktop'; - breakpoint: Breakpoint; - width: string; - layout: string; - behavior: string; - padding: string; - aligment?: 'center'; - maxWidth?: string; - figmaStyle?: 'layout/mobile' | 'layout/tablet' | 'layout/desktop'; -} - -const breakpoints: BreakpointRow[] = [ - { - type: 'mobile', - breakpoint: 'xs', - width: '0-575px', - layout: '12col / design 2col', - behavior: 'stretch', - padding: '8px/0.5rem', - figmaStyle: 'layout/mobile', - }, - { - type: 'mobile', - breakpoint: 'sm', - width: '576-767px', - layout: '12col / design 2col', - behavior: 'stretch', - padding: '8px/0.5rem', - }, - { - type: 'tablet', - breakpoint: 'md', - width: '768-991px', - layout: '12col / design 6col', - behavior: 'stretch', - padding: '24px/1.5rem', - figmaStyle: 'layout/tablet', - }, - { - type: 'desktop', - breakpoint: 'lg', - width: '992-1199px', - layout: 'side navigation + 12col', - behavior: 'stretch', - padding: '40px/2.5rem', - }, - { - type: 'desktop', - breakpoint: 'xl', - width: '1200-1399px', - layout: 'side navigation + 12col', - behavior: 'stretch', - padding: '40px/2.5rem', - }, - { - type: 'desktop', - breakpoint: 'xxl', - width: '>= 1400px', - layout: 'side navigation + 12col', - behavior: 'stretch', - aligment: 'center', - maxWidth: '1440px/90rem', - padding: 'min: 40px/2.5rem', - figmaStyle: 'layout/desktop', - }, -]; - -const BreakingpointsTable = () => { - const columnHelper = createColumnHelper(); - - // eslint-disable-next-line - const columns: ColumnDef[] = [ - columnHelper.accessor('breakpoint', { - header: () => 'Breakpoints', - cell: ({ row: { original } }) => ( - - {original.type} - {`break-${original.breakpoint}`} - - ), - }), - columnHelper.accessor('width', { - header: () => 'Width', - }), - columnHelper.accessor('layout', { - header: () => 'Layout & columns', - }), - columnHelper.accessor('behavior', { - header: () => 'Grid behavior', - cell: ({ row: { original } }) => ( - <> - {`${original.behavior}`} - {original.maxWidth && {`max-content width:${original.maxWidth}`}} - {original.aligment && {`content alignment: ${original.aligment}`}} - - ), - }), - columnHelper.accessor('padding', { - header: () => 'Page padding', - }), - columnHelper.accessor('figmaStyle', { - header: () => 'Figma grid style', - }), - ]; - - return ( - - id="spacing-table" - className="sb-unstyled" - hidePagination={true} - hideCardBorder={true} - data={breakpoints} - columns={columns} - enableSorting={false} - caption="Spacing Values" - /> - ); -}; - -export default BreakingpointsTable; diff --git a/libs/react-components/src/community/docs/scale-layout/development.mdx b/libs/react-components/src/community/docs/scale-layout/development.mdx deleted file mode 100644 index a413dfb5c..000000000 --- a/libs/react-components/src/community/docs/scale-layout/development.mdx +++ /dev/null @@ -1,12 +0,0 @@ -import { Meta, Title, Subtitle, Unstyled } from '@storybook/blocks'; - - - -# Grid in development - -Every layout in design is makeable with `` and `` components.
-All examples of Grid usage are under [Grid docs](/docs/components-grid--docs) - -To create vertical spacing between elements use `` [component](/docs/components-vertical-spacing--docs). - -{/* TODO: Need more information how to use Row, Col, VerticalSpacing and Card correctly */} diff --git a/libs/react-components/src/community/docs/scale-layout/grid.mdx b/libs/react-components/src/community/docs/scale-layout/grid.mdx deleted file mode 100644 index 907fc622f..000000000 --- a/libs/react-components/src/community/docs/scale-layout/grid.mdx +++ /dev/null @@ -1,8 +0,0 @@ -import { Meta, Title, Subtitle, Unstyled } from '@storybook/blocks'; -import Grid from './grid.tsx'; - - - - - - diff --git a/libs/react-components/src/community/docs/scale-layout/grid.tsx b/libs/react-components/src/community/docs/scale-layout/grid.tsx deleted file mode 100644 index c1ee8d812..000000000 --- a/libs/react-components/src/community/docs/scale-layout/grid.tsx +++ /dev/null @@ -1,91 +0,0 @@ -import { Title } from '@storybook/blocks'; - -import { Col, Row } from '../../../tedi/components/layout/grid'; -import { VerticalSpacing } from '../../../tedi/components/layout/vertical-spacing'; -import { Separator } from '../../../tedi/components/misc/separator/separator'; -import { Heading } from '../../index'; -import BreakingpointsTable from './breaking-points'; - -const Grid = () => { - return ( - - - Grid system -

- Grids are fundamental to creating visually consistent, organized, and responsive designs. Grids help maintain - order and structure across different pages, components, and devices.{' '} -

- - - - Visual example of 12-column grid - Columns -

- Columns are vertical divisions within a container that provide a framework for organising and aligning - content. -

-

- We use a 12-column grid. For design purposes we can use 6col, 4col, 3col and 2 col grids, since they are - easily translated to 12 column grid in development. Widths of the columns are fluid, meaning it is - determined by the container size where the grid is placed (plus fixed width gutter). -

-
- - - - Visual example of gutters in grid - Gutter -

- Gutters are the gaps between the columns, created by horizontal padding. Gutters can be responsively - adjusted. -

-

Gutter width is fixed. Gutter values that can be used: 24px (default) , 16px, 12px, 8px, 0px.

-

- Note that gutter size helps us utilise rule of proximity, meaning elements that are close together are - perceived connected. -

-
- - - - Visual example of layout variatsions - Layout variations -
- 12 columns grid is extremely adaptable and provides a lot of flexible combinations. For example content - can be: -
    -
  • utilising full container width;
  • -
  • divided into 2, 3, 4 and 6 columns;
  • -
  • divided into 2 columns of different width and create 1/3 layouts.
  • -
-
-
- - - - Visual example of column offset - Column offset -

- Column offset functionality is available for use in development. Use offsets very intentionally, as - it's generally a good practice to align elements on the left. Left alignment supports the natural - reading flow of left-to-right languages. -

-
- -
-
- - - Layout grid & breaking points -

- Layout grids play a crucial role in creating cohesive, organised, and accessible designs. They provide a solid - foundation for building scalable and responsive designs, streamline the design process, and promote - collaboration among team members. -

- -
-
- ); -}; - -export default Grid; diff --git a/libs/react-components/src/community/docs/scale-layout/spacing.mdx b/libs/react-components/src/community/docs/scale-layout/spacing.mdx deleted file mode 100644 index 547603667..000000000 --- a/libs/react-components/src/community/docs/scale-layout/spacing.mdx +++ /dev/null @@ -1,9 +0,0 @@ -import { Meta, Unstyled } from '@storybook/blocks'; - -import Spacing from './spacing.tsx'; - - - - - - diff --git a/libs/react-components/src/community/docs/scale-layout/spacing.tsx b/libs/react-components/src/community/docs/scale-layout/spacing.tsx deleted file mode 100644 index dc5e5564e..000000000 --- a/libs/react-components/src/community/docs/scale-layout/spacing.tsx +++ /dev/null @@ -1,96 +0,0 @@ -import { Title } from '@storybook/blocks'; -import { ColumnDef, createColumnHelper } from '@tanstack/react-table'; - -import { Col, Row } from '../../../tedi/components/layout/grid'; -import { VerticalSpacing } from '../../../tedi/components/layout/vertical-spacing'; -import { Separator } from '../../../tedi/components/misc/separator/separator'; -import { Heading, Table } from '../../index'; - -const Spacing = () => { - return ( - - - Spacing - - - - Soft 8px grid - Soft 8px grid -

To create harmonious and visually pleasing designs, a soft 8px grid system is used.

-
- - - - Example how spacing is used between elements - Use in the elements -

- Spacing is measured from the edges of the element boxes. All user interface elements are designed with - this system in mind. For example, the line-height of typography and the height of the buttons are also - set in increments of 8px. This means elements also fall into a grid, but they don't always have to - conform to predefined spacing values. -

-
- -
-
- - - Spacing values - - -
- ); -}; - -export default Spacing; - -const exampleSpacings = [2, 4, 8, 12, 16, 24, 32, 40, 48, 64, 80, 96, 160]; - -interface SpacingRow { - px: number; - rem: number; -} - -const spacings: SpacingRow[] = exampleSpacings.map((px) => ({ px, rem: px / 16 })); - -const SpacingsTable = () => { - const columnHelper = createColumnHelper(); - - // eslint-disable-next-line - const columns: ColumnDef[] = [ - columnHelper.accessor('px', { - header: () => 'Spacing', - cell: (info) => info.renderValue() + 'px', - }), - columnHelper.accessor('rem', { - header: () => 'rem', - cell: (info) => info.renderValue() + 'rem', - }), - columnHelper.accessor((row) => `${row.px}-example`, { - id: 'example', - header: () => 'Example', - cell: ({ row: { original } }) => ( -
- ), - }), - ]; - - return ( - - id="spacing-table" - className="sb-unstyled" - hidePagination={true} - hideCardBorder={true} - data={spacings} - columns={columns} - enableSorting={false} - caption="Spacing Values" - /> - ); -}; diff --git a/libs/react-components/src/community/helpers/background-colors/background-colors.module.scss b/libs/react-components/src/community/helpers/background-colors/background-colors.module.scss deleted file mode 100644 index ad7943214..000000000 --- a/libs/react-components/src/community/helpers/background-colors/background-colors.module.scss +++ /dev/null @@ -1,45 +0,0 @@ -$colors: ( - // Primary - 'primary-main', - 'primary-active', - 'primary-active-subtle', - 'primary-highlight', - 'primary-highlight-subtle', - // Accent - 'accent-main', - 'accent-highlight', - // Foreground - 'bg-default', - 'bg-muted', - 'bg-subtle', - 'bg-disabled', - 'bg-inverted', - 'bg-inverted-contrast', - // Shades - 'black', - 'white', - // Functional colors - 'positive-main', - 'positive-active', - 'positive-highlight', - 'important-main', - 'important-active', - 'important-highlight', - 'info-main', - 'info-active', - 'info-highlight', - 'warning-main', - 'warning-highlight', - // Transparent - 'transparent' -); - -@each $color in $colors { - .#{$color} { - background-color: var(--color-#{$color}); - - @media print { - background-color: var(--color-bg-default); - } - } -} diff --git a/libs/react-components/src/community/helpers/background-colors/background-colors.stories.tsx b/libs/react-components/src/community/helpers/background-colors/background-colors.stories.tsx deleted file mode 100644 index f318b95e9..000000000 --- a/libs/react-components/src/community/helpers/background-colors/background-colors.stories.tsx +++ /dev/null @@ -1,72 +0,0 @@ -import { Meta, StoryFn, StoryObj } from '@storybook/react'; - -import { Card } from '../../components/card'; -import CardContent from '../../components/card/card-content/card-content'; -import { TColorsBackground } from '../../components/commonTypes'; - -const meta: Meta = { - component: Card, - parameters: { - layout: 'fullscreen', - }, -}; - -export default meta; -type Story = StoryObj; - -const colors: TColorsBackground[] = [ - // Primary - 'primary-main', - 'primary-active', - 'primary-active-subtle', - 'primary-highlight', - 'primary-highlight-subtle', - // Accent - 'accent-main', - 'accent-highlight', - // Background - 'bg-default', - 'bg-muted', - 'bg-subtle', - 'bg-disabled', - 'bg-inverted', - 'bg-inverted-contrast', - // Shades - 'black', - 'white', - // Functional colors - Positive - 'positive-main', - 'positive-active', - 'positive-highlight', - 'important-main', - 'important-active', - 'important-highlight', - 'info-main', - 'info-active', - 'info-highlight', - 'warning-main', - 'warning-highlight', -]; - -interface TemplateProps { - colors: TColorsBackground[]; -} -const Template: StoryFn = () => { - return ( - - {colors.map((i, index) => ( - -

{i}

-
- ))} -
- ); -}; - -export const BackgroundColors: Story = { - render: Template, - - args: { - colors, - }, -}; diff --git a/libs/react-components/src/community/helpers/background-colors/background-colors.tsx b/libs/react-components/src/community/helpers/background-colors/background-colors.tsx deleted file mode 100644 index f5f657e98..000000000 --- a/libs/react-components/src/community/helpers/background-colors/background-colors.tsx +++ /dev/null @@ -1,10 +0,0 @@ -import { TColorsBackground } from '../../components/commonTypes'; -import styles from './background-colors.module.scss'; - -export const getBackgroundColorClass = (type?: TColorsBackground): string => { - if (type) { - return styles[type]; - } - - return ''; -}; diff --git a/libs/react-components/src/community/helpers/hooks/use-breakpoint-props.mdx b/libs/react-components/src/community/helpers/hooks/use-breakpoint-props.mdx deleted file mode 100644 index 742c524e9..000000000 --- a/libs/react-components/src/community/helpers/hooks/use-breakpoint-props.mdx +++ /dev/null @@ -1,63 +0,0 @@ -import { Meta } from '@storybook/blocks'; - - - -# Props depending on current breakpoint - -## Overview - -useBreakpointProps exports function `getCurrentBreakpointProps` . It return merged object of properties from activeBreakPoints and defaultValues. - -Same hook should be used inside Row/Col components (already has custom logic to achive the same) and in other components where needed. - -## Usage - -Type components props as two interfaces/types: - -1. ComponentBreakpointProps -\> All the properties that can change on different breakpoint -2. ComponentProps -\> All the other properties + extending BreakPointSupport interace - -Use props as: - -```tsx -interface ComponentBreakpointProps { - background?: TColorsBackground; -} - -interface ComponentProps extends BreakpointSupport { - children: React.ReactNode; -} - -const Component = (props: ComponentProps) => { - const { getCurrentBreakpointProps } = useBreakpointProps(props.defaultServerBreakpoint); - const { prop1, prop2, ...rest } = getCurrentBreakpointProps(props, defaultPropsObject); - - return

content

; -}; -``` - -And when using as Component: - -```tsx - -

Content

-
-``` - -## defaultServerBreakpoint with SSR - -When using SSR, you can pass the defaultServerBreakpoint prop to the component. -This prop should be set to the breakpoint that is wanted to render on the server side. -This is important when wanting to avoid large layout shifts with SSR. -Defaults to 'xs' if not set. - -```tsx - -

Content

-
-``` diff --git a/libs/react-components/src/community/helpers/hooks/use-breakpoint-props.ts b/libs/react-components/src/community/helpers/hooks/use-breakpoint-props.ts deleted file mode 100644 index 6a259ea7a..000000000 --- a/libs/react-components/src/community/helpers/hooks/use-breakpoint-props.ts +++ /dev/null @@ -1,53 +0,0 @@ -import { omit, pickBy } from 'lodash-es'; -import React from 'react'; - -import useBreakpoint, { Breakpoint, breakpoints } from './use-breakpoint'; - -export const useBreakpointProps = (defaultServerBreakpoint: Breakpoint = 'xs') => { - const currentBreakpoint = useBreakpoint(defaultServerBreakpoint); - const activeBreakpoints: Breakpoint[] = React.useMemo( - () => (currentBreakpoint ? breakpoints.slice(0, breakpoints.indexOf(currentBreakpoint) + 1) : []), - [currentBreakpoint] - ); - - /** - * Return currently active breakpoints properties as merged - * when property is used directly not as breakpoint prop (ex. `color: 'red'` not `md: { color: 'red' }`) - * it is counted then as xs breakpoint (mobile-first approach). - */ - const getCurrentBreakpointProps = React.useCallback( - ( - props: BreakpointSupport, - defaultValues: Partial = {} - ): Omit, Exclude | 'defaultServerBreakpoint'> => { - // eslint-disable-next-line unused-imports/no-unused-vars - const { sm, md, lg, xl, xxl, ...xs } = props; - const propArray = [ - ...activeBreakpoints.map((bp) => pickBy(bp === 'xs' ? xs : props[bp], (value) => value !== undefined)), // filter out props that have undefined as value, so they don't override lower breakpoint values or default values - ].filter(Boolean); - - // Add propArray to defaultValues - // and remove defaultServerBreakpoint from defaultValues - to avoided passing to HTML element with rest props - return Object.assign(omit(defaultValues, 'defaultServerBreakpoint'), ...propArray); - }, - [activeBreakpoints] - ); - - return { getCurrentBreakpointProps }; -}; - -/** - * BreakpointSupport is a utility type that allows you to define props for different breakpoints. - * It extends the given type T and adds optional properties for each breakpoint except 'xs'. - * This is useful for creating responsive components that can have different props based on the current breakpoint. - * Also defaultServerBreakpoint is added to the type, so you can set a default value for the component, when it's rendered on the server-side. - * Because in SSR we don't have access to the window object and can't know the user's screen size. - */ -export type BreakpointSupport = T & { - /** - * Default breakpoint for SSR, the component is rendered with this breakpoint props on the server-side. - */ - defaultServerBreakpoint?: Breakpoint; -} & { - [key in Exclude]?: Partial; -}; diff --git a/libs/react-components/src/community/helpers/hooks/use-breakpoint.ts b/libs/react-components/src/community/helpers/hooks/use-breakpoint.ts deleted file mode 100644 index 9b342331d..000000000 --- a/libs/react-components/src/community/helpers/hooks/use-breakpoint.ts +++ /dev/null @@ -1,48 +0,0 @@ -import { debounce } from 'lodash-es'; -import { useLayoutEffect, useState } from 'react'; - -export type Breakpoint = 'xs' | 'sm' | 'md' | 'lg' | 'xl' | 'xxl'; -export const breakpoints: Breakpoint[] = ['xs', 'sm', 'md', 'lg', 'xl', 'xxl']; - -/** - * @param defaultServerBreakpoint - Default breakpoint for SSR, the hook returns this breakpoint in the SSR. - * @returns User's current breakpoint - */ -export const useBreakpoint = (defaultServerBreakpoint: Breakpoint = 'xs'): Breakpoint => { - const [breakpoint, setBreakpoint] = useState(defaultServerBreakpoint); - - useLayoutEffect(() => { - const getBreakpoint = (): Breakpoint => { - if (window.matchMedia('(min-width: 1400px)').matches) { - return 'xxl'; - } else if (window.matchMedia('(min-width: 1200px)').matches) { - return 'xl'; - } else if (window.matchMedia('(min-width: 992px)').matches) { - return 'lg'; - } else if (window.matchMedia('(min-width: 768px)').matches) { - return 'md'; - } else if (window.matchMedia('(min-width: 576px)').matches) { - return 'sm'; - } else { - return 'xs'; - } - }; - - const resizeEvent = debounce((): void => { - setBreakpoint(getBreakpoint()); - }, 20); - - // Set the initial breakpoint on the client - setBreakpoint(getBreakpoint()); - - window.addEventListener('resize', resizeEvent); - return () => { - resizeEvent.cancel(); - window.removeEventListener('resize', resizeEvent); - }; - }, []); - - return breakpoint; -}; - -export default useBreakpoint; diff --git a/libs/react-components/src/community/helpers/hooks/use-first-render.ts b/libs/react-components/src/community/helpers/hooks/use-first-render.ts deleted file mode 100644 index 41754f7ca..000000000 --- a/libs/react-components/src/community/helpers/hooks/use-first-render.ts +++ /dev/null @@ -1,11 +0,0 @@ -import React from 'react'; - -export const useFirstRender = () => { - const firstRender = React.useRef(true); - - React.useEffect(() => { - firstRender.current = false; - }, []); - - return firstRender.current; -}; diff --git a/libs/react-components/src/community/helpers/hooks/use-layout.ts b/libs/react-components/src/community/helpers/hooks/use-layout.ts deleted file mode 100644 index 88764c397..000000000 --- a/libs/react-components/src/community/helpers/hooks/use-layout.ts +++ /dev/null @@ -1,29 +0,0 @@ -import useBreakpoint, { Breakpoint } from './use-breakpoint'; - -export const mobileBreakpoints: Breakpoint[] = ['xs', 'sm']; -export const tabletBreakpoints: Breakpoint[] = ['md']; -export const desktopBreakpoints: Breakpoint[] = ['lg', 'xl', 'xxl']; - -export type ScreenLayout = 'mobile' | 'tablet' | 'desktop'; -export type Layouts = Array; - -export const useLayout = (layouts: Layouts): boolean => { - const breakpoint = useBreakpoint(); - - const breakpoints = layouts?.reduce((accumulator, current) => { - switch (current) { - case 'desktop': - return [...accumulator, ...desktopBreakpoints]; - case 'tablet': - return [...accumulator, ...tabletBreakpoints]; - case 'mobile': - return [...accumulator, ...mobileBreakpoints]; - default: - return [...accumulator, current]; - } - }, []); - - return breakpoint ? breakpoints.includes(breakpoint) : false; -}; - -export default useLayout; diff --git a/libs/react-components/src/community/helpers/index.ts b/libs/react-components/src/community/helpers/index.ts deleted file mode 100644 index 0e21574fb..000000000 --- a/libs/react-components/src/community/helpers/index.ts +++ /dev/null @@ -1,5 +0,0 @@ -export * from './hooks/use-first-render'; -export * from './hooks/use-breakpoint'; -export * from './hooks/use-breakpoint-props'; -export * from './hooks/use-layout'; -export * from './background-colors/background-colors'; diff --git a/libs/react-components/src/community/helpers/polymorphic/types.ts b/libs/react-components/src/community/helpers/polymorphic/types.ts deleted file mode 100644 index eabe366fa..000000000 --- a/libs/react-components/src/community/helpers/polymorphic/types.ts +++ /dev/null @@ -1,29 +0,0 @@ -/** - * This file should contain only helpers that are used for developing components. They don't get exported separately from component library - */ -import React from 'react'; - -/** - * Types for polymorphic components - */ -export type AllowedHTMLTags = C extends V ? C : never; -type AsProp = { - /** - * Render as custom component - */ - as?: C; -}; -type PropsToOmit = keyof (AsProp & P); -// This is the type for the "ref" only -export type PolymorphicRef = React.ComponentPropsWithRef['ref']; - -export type PolymorphicComponentPropWithoutRef = Props & - AsProp & - Omit, PropsToOmit>; - -export type PolymorphicComponentPropWithRef< - C extends React.ElementType, - Props = unknown -> = PolymorphicComponentPropWithoutRef & { - ref?: PolymorphicRef; -}; diff --git a/libs/react-components/src/community/index.ts b/libs/react-components/src/community/index.ts deleted file mode 100644 index 583b4831a..000000000 --- a/libs/react-components/src/community/index.ts +++ /dev/null @@ -1,65 +0,0 @@ -export * from './components/commonTypes'; -export * from './components/typography/heading/heading'; -export * from './components/typography/text/text'; -export * from './components/button/button'; -export * from './components/collapse/collapse'; -export * from './components/anchor/anchor'; -export * from './components/layout'; -export * from './components/ellipsis/ellipsis'; -export * from './components/dropdown/dropdown'; -export * from './components/stepper'; -export * from './components/card'; -export * from './components/icon/icon'; -export * from './components/feedback/feedback'; -export * from './components/form/hidden-field/hidden-field'; -export * from './components/form/choice-group'; -export * from './components/form/check/check'; -export * from './components/form/radio/radio'; -export * from './components/form/pickers'; -export * from './components/form/file-upload'; -export * from './components/form/select/select'; -export * from './components/form/text-editor/text-editor'; -export * from './components/form/toggle/toggle'; -export * from './components/table'; -export * from './components/table-of-contents'; -export * from './components/tabs'; -export * from './components/tag/tag'; -export * from './components/tooltip'; -export * from './components/status/status'; -export * from './components/vertical-progress'; -export * from './components/accordion'; -export * from './components/toggle-open/toggle-open'; -export * from './components/modal'; -export * from './components/placeholder/placeholder'; -export * from './components/vertical-stepper/vertical-stepper'; -export * from './components/vertical-stepper/step-item/step-item'; -export * from './components/vertical-stepper/sub-item/sub-item'; - -export * from './helpers'; - -export * from './providers/style-provider/style-provider'; - -export * from './types/index'; - -// Map components -export * from './components/map-components/base-map-selection/base-map-selection'; -export * from './components/map-components/button-group/button-group'; -export * from './components/map-components/carousel/carousel'; -export * from './components/map-components/color-and-icon-picker/color-and-icon-picker'; -export * from './components/map-components/comparison/comparison'; -export * from './components/map-components/directions'; -export * from './components/map-components/edit-actions/editing-actions'; -export * from './components/map-components/left-panel/left-panel'; -export * from './components/map-components/legend/legend'; -export * from './components/map-components/map-accordion/map-accordion'; -export * from './components/map-components/map-button/map-button'; -export * from './components/map-components/map-dropdown/map-dropdown'; -export * from './components/map-components/map-info/map-info'; -export * from './components/map-components/map-layer/map-layer'; -export * from './components/map-components/map-preview/map-preview'; -export * from './components/map-components/resizer/resizer'; -export * from './components/map-components/right-panel/right-panel'; -export * from './components/map-components/scale-bar/scale-bar'; -export * from './components/map-components/map-select/map-select'; -export * from './components/map-components/sheet/sheet'; -export * from './components/map-components/timeline/timeline'; diff --git a/libs/react-components/src/community/providers/style-provider/style-provider.tsx b/libs/react-components/src/community/providers/style-provider/style-provider.tsx deleted file mode 100644 index f327987ff..000000000 --- a/libs/react-components/src/community/providers/style-provider/style-provider.tsx +++ /dev/null @@ -1,12 +0,0 @@ -import React from 'react'; - -import '../../styles/index.scss'; - -export interface StyleProviderProps { - children: React.ReactNode; -} - -export const StyleProvider = (props: StyleProviderProps): JSX.Element => { - const { children } = props; - return <>{children}; -}; diff --git a/libs/react-components/src/community/styles/_base.scss b/libs/react-components/src/community/styles/_base.scss deleted file mode 100644 index bd580bf3b..000000000 --- a/libs/react-components/src/community/styles/_base.scss +++ /dev/null @@ -1,76 +0,0 @@ -/* stylelint-disable selector-id-pattern, selector-max-id, selector-no-qualifying-type */ -@use '@tehik-ee/tedi-core/mixins'; -@use '@tehik-ee/tedi-core/bootstrap-utility/breakpoints'; - -html { - box-sizing: border-box; - font-family: var(--font-family); - line-height: var(--font-line-height-base); - color: var(--general-text-primary); - scroll-padding-top: var(--header-bottom-height, 0); - scroll-behavior: smooth; -} - -html, -body { - height: 100%; - background-color: var(--color-bg-subtle); -} - -*, -*::before, -*::after { - box-sizing: inherit; -} - -/** - * To overwrite behavior of focus outline - * Set '--global-outline-color' and '--global-outline-offset' inside component scope - */ -*:where(:focus) { - @include mixins.focus-element; -} - -body, -h1, -h2, -h3, -h4, -h5, -h6, -p, -ol, -dl, -dd, -ul { - padding: 0; - margin: 0; - font-weight: normal; -} - -ol, -ul { - margin: 0.25rem 0; - list-style-position: inside; -} - -ol.public-DraftStyleDefault-ul, -ul.public-DraftStyleDefault-ul { - list-style-position: outside; -} - -button { - line-height: 1.5; -} - -img { - max-width: 100%; - height: auto; -} - -.scroll-disabled { - @include breakpoints.media-breakpoint-down(lg) { - height: 100%; - overflow-y: hidden; - } -} diff --git a/libs/react-components/src/community/styles/_fonts.scss b/libs/react-components/src/community/styles/_fonts.scss deleted file mode 100644 index 141f17ead..000000000 --- a/libs/react-components/src/community/styles/_fonts.scss +++ /dev/null @@ -1,253 +0,0 @@ -// check src/public/fonts/README.md for explanation on font paths - -/* cyrillic-ext */ -@font-face { - font-family: Roboto; - font-style: italic; - font-weight: 300; - src: url('/fonts/roboto-v30-cyrillic-ext-300-italic.woff2') format('woff2'); - font-display: swap; - unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; -} - -/* cyrillic */ -@font-face { - font-family: Roboto; - font-style: italic; - font-weight: 300; - src: url('/fonts/roboto-v30-cyrillic-300-italic.woff2') format('woff2'); - font-display: swap; - unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; -} - -/* latin-ext */ -@font-face { - font-family: Roboto; - font-style: italic; - font-weight: 300; - src: url('/fonts/roboto-v30-latin-ext-300-italic.woff2') format('woff2'); - font-display: swap; - unicode-range: U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, - U+2C60-2C7F, U+A720-A7FF; -} - -/* latin */ -@font-face { - font-family: Roboto; - font-style: italic; - font-weight: 300; - src: url('/fonts/roboto-v30-latin-300-italic.woff2') format('woff2'); - font-display: swap; - unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, - U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; -} - -/* cyrillic-ext */ -@font-face { - font-family: Roboto; - font-style: italic; - font-weight: 400; - src: url('/fonts/roboto-v30-cyrillic-ext-400-italic.woff2') format('woff2'); - font-display: swap; - unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; -} - -/* cyrillic */ -@font-face { - font-family: Roboto; - font-style: italic; - font-weight: 400; - src: url('/fonts/roboto-v30-cyrillic-400-italic.woff2') format('woff2'); - font-display: swap; - unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; -} - -/* latin-ext */ -@font-face { - font-family: Roboto; - font-style: italic; - font-weight: 400; - src: url('/fonts/roboto-v30-latin-ext-400-italic.woff2') format('woff2'); - font-display: swap; - unicode-range: U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, - U+2C60-2C7F, U+A720-A7FF; -} - -/* latin */ -@font-face { - font-family: Roboto; - font-style: italic; - font-weight: 400; - src: url('/fonts/roboto-v30-latin-400-italic.woff2') format('woff2'); - font-display: swap; - unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, - U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; -} - -/* cyrillic-ext */ -@font-face { - font-family: Roboto; - font-style: italic; - font-weight: 700; - src: url('/fonts/roboto-v30-cyrillic-ext-700-italic.woff2') format('woff2'); - font-display: swap; - unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; -} - -/* cyrillic */ -@font-face { - font-family: Roboto; - font-style: italic; - font-weight: 700; - src: url('/fonts/roboto-v30-cyrillic-700-italic.woff2') format('woff2'); - font-display: swap; - unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; -} - -/* latin-ext */ -@font-face { - font-family: Roboto; - font-style: italic; - font-weight: 700; - src: url('/fonts/roboto-v30-latin-ext-700-italic.woff2') format('woff2'); - font-display: swap; - unicode-range: U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, - U+2C60-2C7F, U+A720-A7FF; -} - -/* latin */ -@font-face { - font-family: Roboto; - font-style: italic; - font-weight: 700; - src: url('/fonts/roboto-v30-latin-700-italic.woff2') format('woff2'); - font-display: swap; - unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, - U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; -} - -/* cyrillic-ext */ -@font-face { - font-family: Roboto; - font-style: normal; - font-weight: 300; - src: url('/fonts/roboto-v30-cyrillic-ext-300.woff2') format('woff2'); - font-display: swap; - unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; -} - -/* cyrillic */ -@font-face { - font-family: Roboto; - font-style: normal; - font-weight: 300; - src: url('/fonts/roboto-v30-cyrillic-300.woff2') format('woff2'); - font-display: swap; - unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; -} - -/* latin-ext */ -@font-face { - font-family: Roboto; - font-style: normal; - font-weight: 300; - src: url('/fonts/roboto-v30-latin-ext-300.woff2') format('woff2'); - font-display: swap; - unicode-range: U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, - U+2C60-2C7F, U+A720-A7FF; -} - -/* latin */ -@font-face { - font-family: Roboto; - font-style: normal; - font-weight: 300; - src: url('/fonts/roboto-v30-latin-300.woff2') format('woff2'); - font-display: swap; - unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, - U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; -} - -/* cyrillic-ext */ -@font-face { - font-family: Roboto; - font-style: normal; - font-weight: 400; - src: url('/fonts/roboto-v30-cyrillic-ext-400.woff2') format('woff2'); - font-display: swap; - unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; -} - -/* cyrillic */ -@font-face { - font-family: Roboto; - font-style: normal; - font-weight: 400; - src: url('/fonts/roboto-v30-cyrillic-400.woff2') format('woff2'); - font-display: swap; - unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; -} - -/* latin-ext */ -@font-face { - font-family: Roboto; - font-style: normal; - font-weight: 400; - src: url('/fonts/roboto-v30-latin-ext-400.woff2') format('woff2'); - font-display: swap; - unicode-range: U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, - U+2C60-2C7F, U+A720-A7FF; -} - -/* latin */ -@font-face { - font-family: Roboto; - font-style: normal; - font-weight: 400; - src: url('/fonts/roboto-v30-latin-400.woff2') format('woff2'); - font-display: swap; - unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, - U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; -} - -/* cyrillic-ext */ -@font-face { - font-family: Roboto; - font-style: normal; - font-weight: 700; - src: url('/fonts/roboto-v30-cyrillic-ext-700.woff2') format('woff2'); - font-display: swap; - unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; -} - -/* cyrillic */ -@font-face { - font-family: Roboto; - font-style: normal; - font-weight: 700; - src: url('/fonts/roboto-v30-cyrillic-700.woff2') format('woff2'); - font-display: swap; - unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; -} - -/* latin-ext */ -@font-face { - font-family: Roboto; - font-style: normal; - font-weight: 700; - src: url('/fonts/roboto-v30-latin-ext-700.woff2') format('woff2'); - font-display: swap; - unicode-range: U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, - U+2C60-2C7F, U+A720-A7FF; -} - -/* latin */ -@font-face { - font-family: Roboto; - font-style: normal; - font-weight: 700; - src: url('/fonts/roboto-v30-latin-700.woff2') format('woff2'); - font-display: swap; - unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, - U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; -} diff --git a/libs/react-components/src/community/styles/_helpers.scss b/libs/react-components/src/community/styles/_helpers.scss deleted file mode 100644 index 6b3b0315f..000000000 --- a/libs/react-components/src/community/styles/_helpers.scss +++ /dev/null @@ -1,44 +0,0 @@ -@use './mixins'; - -// https://github.com/h5bp/main.css/blob/main/src/_helpers.css - -.hidden, -[hidden] { - display: none !important; -} - -.block { - display: block !important; -} - -.inline-block { - display: inline-block !important; -} - -.flex { - display: flex !important; -} - -.visually-hidden, -.sr-only { - position: absolute; - width: 1px; - height: 1px; - padding: 0; - margin: -1px; - overflow: hidden; - clip: rect(0, 0, 0, 0); - white-space: nowrap; - border: 0; - - &.focusable:active, - &.focusable:focus { - position: static; - width: auto; - height: auto; - margin: 0; - overflow: visible; - clip: auto; - white-space: inherit; - } -} diff --git a/libs/react-components/src/community/styles/_map-component.variables.scss b/libs/react-components/src/community/styles/_map-component.variables.scss deleted file mode 100644 index 2a7ef8a1d..000000000 --- a/libs/react-components/src/community/styles/_map-component.variables.scss +++ /dev/null @@ -1,401 +0,0 @@ -:root { - /** color variables **/ - --button-expand-background-default: var(--alpha-white-80); - --button-map-base-background-default: var(--general-surface-primary); - --button-map-base-background-hover: var(--general-surface-primary); - --button-map-base-background-selected: var(--general-surface-primary); - --button-map-base-border-default: var(--button-primary-border-default); - --button-map-base-border-hover: var(--button-primary-border-hover); - --button-map-base-border-selected: var(--general-border-brand); - --button-map-base-text-background-default: var(--alpha-white-80); - --button-map-base-text-background-hover: var(--alpha-white-80); - --button-map-base-text-background-selected: var(--general-surface-brand-primary); - --button-map-info-background-default: var(--alpha-white-80); - --button-map-info-background-hover: var(--alpha-white-80); - --button-map-info-background-open: var(--button-primary-background-active); - --button-map-info-text-active: var(--primary-800); - --button-map-info-text-default: var(--link-primary-default); - --button-map-info-text-focus: var(--link-primary-default); - --button-map-info-text-hover: var(--primary-700); - --button-map-info-text-open: var(--primary-800); - --button-neutral-inverted-background-active: var(--alpha-white-10); - --button-neutral-inverted-background-default: var(--alpha-01); - --button-neutral-inverted-background-focus: var(--alpha-01); - --button-neutral-inverted-background-hover: var(--alpha-white-10); - --button-neutral-inverted-text-active: var(--neutral-100); - --button-neutral-inverted-text-default: var(--neutral-100); - --button-neutral-inverted-text-focus: var(--neutral-100); - --button-neutral-inverted-text-hover: var(--primary-200); - --button-neutral-background-active: var(--general-surface-brand-tertiary); - --button-neutral-background-default: var(--alpha-01); - --button-neutral-background-focus: var(--alpha-01); - --button-neutral-background-hover: var(--general-surface-hover); - --button-neutral-border-default: var(--alpha-01); - --button-neutral-text-active: var(--primary-800); - --button-neutral-text-default: var(--primary-600); - --button-neutral-text-focus: var(--primary-600); - --button-neutral-text-hover: var(--primary-700); - --button-primary-background-active: var(--general-surface-brand-tertiary); - --button-primary-background-default: var(--neutral-100); - --button-primary-background-focus: var(--neutral-100); - --button-primary-background-hover: var(--general-surface-hover); - --button-primary-border-active: var(--primary-700); - --button-primary-border-default: var(--general-border-primary); - --button-primary-border-focus: var(--primary-600); - --button-primary-border-hover: var(--primary-600); - --button-primary-text-active: var(--primary-800); - --button-primary-text-default: var(--primary-600); - --button-primary-text-focus: var(--primary-600); - --button-primary-text-hover: var(--primary-700); - --drag-indicator-accent: var(--general-border-accent); - --drag-indicator-light: var(--general-border-primary); - --drag-indicator-button-background: var(--primary-200); - --drag-indicator-button-icon: var(--general-icon-brand); - --form-icon-selection-active: var(--general-icon-brand); - --form-icon-selection-inactive: var(--general-icon-secondary); - --logo-primary: var(--neutral-100); - --logo-secondary-blue: #003087; - --logo-secondary-blue-dark: #263b80; - --map-application-link: var(--link-primary-default); - --map-historical-background-default: var(--general-surface-primary); - --map-historical-background-hover: var(--general-surface-primary); - --map-historical-background-selected: var(--general-surface-primary); - --map-historical-border-default: var(--button-primary-border-default); - --map-historical-border-hover: var(--button-primary-border-hover); - --map-historical-border-selected: var(--general-border-brand); - --map-historical-text-default: var(--general-text-secondary); - --map-historical-text-hover: var(--general-text-brand); - --map-historical-text-selected: var(--general-text-white); - --map-historical-text-background-default: var(--alpha-white-80); - --map-historical-text-background-hover: var(--alpha-white-80); - --map-historical-text-background-selected: var(--general-surface-brand-primary); - --measurement-tooltip: var(--alpha-80); - --mirror-indicator: var(--drag-indicator-accent); - --shape-default: var(--accent-600); - --shape-hover: var(--accent-500); - --shape-disabled-background: var(--alpha-white-50); - --shape-disabled-border: var(--button-card-disabled-border); - --shape-edit-background-default: #ff800099; - --shape-edit-background-hover: #ff8000cc; - --shape-edit-border-default: var(--accent-600); - --shape-edit-border-hover: var(--accent-600); - --shape-read-background-default: #e7f0f699; - --shape-read-background-hover: #e7f0f6cc; - --shape-read-border-default: var(--general-border-brand); - --shape-read-border-hover: var(--general-border-brand); - --sheet-body-default: var(--general-surface-primary); - --sheet-body-secondary: var(--general-surface-tertiary); - --sheet-footer-background: var(--sheet-header-background-default); - --sheet-header-border: var(--general-border-primary); - --sheet-header-background-brand: var(--general-surface-brand-primary); - --sheet-header-background-default: var(--general-surface-primary); - --sidepanel-background: var(--general-surface-primary); - --sidepanel-footer-background: var(--general-surface-tertiary); - --sidepanel-header-background-default: var(--side-navigation-background); - --sidepanel-header-background-open: var(--sidepanel-item-brand-background-open); - --sidepanel-item-brand-background-default: var(--general-surface-brand-primary); - --sidepanel-item-brand-background-open: var(--general-surface-brand-secondary); - --sidepanel-item-brand-border-bottom: var(--primary-800); - --sidepanel-item-white-background-default: var(--general-surface-primary); - --sidepanel-item-white-background-hover: var(--general-surface-hover); - --sidepanel-tree-default: var(--general-border-primary); - --sidepanel-tree-hover: var(--primary-200); - --timeline-indicator-border: var(--primary-600); - --timeline-indicator-background-default: var(--neutral-300); - --timeline-indicator-background-hover: var(--general-surface-primary); - --timeline-indicator-background-selected: var(--timeline-indicator-background-hover); - --timeline-indicator-icon-default: var(--general-icon-brand); - --timeline-indicator-icon-hover: var(--general-icon-brand); - --timeline-indicator-icon-selected: var(--general-icon-brand-dark); - --timeline-line-large: var(--general-border-brand); - --timeline-line-small: var(--general-border-secondary); - --form-checkbox-radio-card-primary-selected-background: var(--primary-600); - --button-group-primary-selected-background: var(--form-checkbox-radio-card-primary-selected-background); - - /** dimensional variables **/ - --button-size-default-desktop: var(--dimensions-19); - --button-size-default-tablet: var(--dimensions-19); - --button-size-default-mobile: var(--dimensions-19); - --sidepanel-item-padding-x-desktop: var(--dimensions-05); - --sidepanel-item-padding-x-tablet: var(--dimensions-05); - --sidepanel-item-padding-x-mobile: var(--dimensions-05); - --sidepanel-layers-subitem-padding-left-desktop: var(--dimensions-03); - --sidepanel-layers-subitem-padding-left-tablet: var(--dimensions-03); - --sidepanel-layers-subitem-padding-left-mobile: var(--dimensions-03); - --button-size-sm-desktop: var(--dimensions-16); - --button-size-sm-tablet: var(--dimensions-16); - --button-size-sm-mobile: var(--dimensions-16); - --sidepanel-min-width-desktop: 316px; - --sidepanel-min-width-rem-desktop: 19.75rem; - --sidepanel-min-width-tablet: 316px; - --sidepanel-min-width-rem-tablet: 19.75rem; - --sidepanel-min-width-mobile: 316px; - --sidepanel-min-width-rem-mobile: 19.75rem; - --sidepanel-layers-drag-indicator-width-desktop: var(--dimensions-03); - --sidepanel-layers-drag-indicator-width-tablet: var(--dimensions-03); - --sidepanel-layers-drag-indicator-width-mobile: var(--dimensions-03); - --timeline-line-small-desktop: 8px; - --timeline-line-small-rem-desktop: 0.5rem; - --timeline-line-small-tablet: 0px; - --timeline-line-small-rem-tablet: 0rem; - --timeline-line-small-mobile: 8px; - --timeline-line-small-rem-mobile: 0.5rem; - --timeline-line-medium-desktop: 12px; - --timeline-line-medium-rem-desktop: 0.75rem; - --timeline-line-medium-tablet: 0px; - --timeline-line-medium-rem-tablet: 0rem; - --timeline-line-medium-mobile: 12px; - --timeline-line-medium-rem-mobile: 0.75rem; - --timeline-line-large-desktop: 16px; - --timeline-line-large-rem-desktop: 1rem; - --timeline-line-large-tablet: 0px; - --timeline-line-large-rem-tablet: 0rem; - --timeline-line-large-mobile: 16px; - --timeline-line-large-rem-mobile: 1rem; - --timeline-layout-gutter-desktop: var(--dimensions-07); - --timeline-layout-gutter-tablet: 0px; - --timeline-layout-gutter-rem-tablet: 0rem; - --timeline-layout-gutter-mobile: var(--dimensions-07); - --timeline-layout-padding-left-desktop: var(--timeline-layout-gutter-mobile); - --timeline-layout-padding-left-tablet: 0px; - --timeline-layout-padding-left-rem-tablet: 0rem; - --timeline-layout-padding-left-mobile: var(--timeline-layout-gutter-mobile); - --timeline-layout-padding-top-desktop: var(--dimensions-13); - --timeline-layout-padding-top-tablet: 0px; - --timeline-layout-padding-top-rem-tablet: 0rem; - --timeline-layout-padding-top-mobile: var(--dimensions-06); - --measure-line-width-desktop: var(--borders-03); - --measure-line-width-tablet: var(--borders-03); - --measure-line-width-mobile: var(--borders-03); - --sheet-header-radius-desktop: 0px; - --sheet-header-radius-rem-desktop: 0rem; - --sheet-header-radius-tablet: 0px; - --sheet-header-radius-rem-tablet: 0rem; - --sheet-header-radius-mobile: 24px; - --sheet-header-radius-rem-mobile: 1.5rem; - --sheet-header-padding-y-desktop: 0px; - --sheet-header-padding-y-rem-desktop: 0rem; - --sheet-header-padding-y-tablet: 0px; - --sheet-header-padding-y-rem-tablet: 0rem; - --sheet-header-padding-y-mobile: var(--dimensions-05); - --sheet-header-padding-x-desktop: 0px; - --sheet-header-padding-x-rem-desktop: 0rem; - --sheet-header-padding-x-tablet: 0px; - --sheet-header-padding-x-rem-tablet: 0rem; - --sheet-header-padding-x-mobile: var(--dimensions-10); - --sheet-body-padding-x-desktop: 0px; - --sheet-body-padding-x-rem-desktop: 0rem; - --sheet-body-padding-x-tablet: 0px; - --sheet-body-padding-x-rem-tablet: 0rem; - --sheet-body-padding-x-mobile: var(--sheet-header-padding-x-mobile); - --sheet-footer-padding-y-desktop: 0px; - --sheet-footer-padding-y-rem-desktop: 0rem; - --sheet-footer-padding-y-tablet: 0px; - --sheet-footer-padding-y-rem-tablet: 0rem; - --sheet-footer-padding-y-mobile: var(--sheet-header-padding-y-mobile); - --sheet-footer-padding-x-desktop: 0px; - --sheet-footer-padding-x-rem-desktop: 0rem; - --sheet-footer-padding-x-tablet: 0px; - --sheet-footer-padding-x-rem-tablet: 0rem; - --sheet-footer-padding-x-mobile: var(--sheet-header-padding-x-mobile); - --sheet-body-padding-y-desktop: 0px; - --sheet-body-padding-y-rem-desktop: 0rem; - --sheet-body-padding-y-tablet: 0px; - --sheet-body-padding-y-rem-tablet: 0rem; - --sheet-body-padding-y-mobile: var(--sheet-header-padding-y-mobile); - --drag-indicator-radius-desktop: var(--radius-08); - --drag-indicator-radius-tablet: var(--radius-08); - --drag-indicator-radius-mobile: var(--radius-08); - --sidepanel-right-radius-desktop: var(--card-radius-rounded-desktop); - --sidepanel-right-radius-tablet: var(--card-radius-rounded-tablet); - --sidepanel-right-radius-mobile: var(--card-radius-rounded-mobile); - --sidepanel-item-height-desktop: var(--dimensions-16); - --sidepanel-item-height-tablet: var(--dimensions-16); - --sidepanel-item-height-mobile: var(--dimensions-17); - --tree-width-desktop: var(--dimensions-13); - --tree-width-tablet: var(--dimensions-13); - --tree-width-mobile: var(--dimensions-13); - --tree-width-sm-desktop: var(--dimensions-11); - --tree-width-sm-tablet: var(--dimensions-11); - --tree-width-sm-mobile: var(--dimensions-11); - --sidepanel-header-padding-top-desktop: var(--dimensions-10); - --sidepanel-header-padding-top-tablet: var(--dimensions-10); - --sidepanel-header-padding-top-mobile: var(--dimensions-10); - --sidepanel-header-padding-y-desktop: var(--dimensions-05); - --sidepanel-header-padding-y-tablet: var(--dimensions-05); - --sidepanel-header-padding-y-mobile: var(--dimensions-05); - --sidepanel-footer-padding-y-desktop: var(--dimensions-05); - --sidepanel-footer-padding-y-tablet: var(--dimensions-05); - --sidepanel-footer-padding-y-mobile: var(--dimensions-05); - --drag-indicator-vertical-width-desktop: var(--dimensions-03); - --drag-indicator-vertical-width-tablet: var(--dimensions-03); - --drag-indicator-vertical-width-mobile: var(--dimensions-03); - --sidepanel-item-padding-y-desktop: var(--dimensions-03); - --sidepanel-item-padding-y-tablet: var(--dimensions-03); - --sidepanel-item-padding-y-mobile: var(--dimensions-03); - --mirror-border-desktop: var(--dimensions-03); - --mirror-border-tablet: var(--dimensions-03); - --mirror-border-mobile: var(--dimensions-03); - --sheet-header-height-desktop: 0px; - --sheet-header-height-rem-desktop: 0rem; - --sheet-header-height-tablet: 0px; - --sheet-header-height-rem-tablet: 0rem; - --sheet-header-height-mobile: 60px; - --sheet-header-height-rem-mobile: 3.75rem; - --sidepanel-header-radius-bottom-desktop: var(--radius-03); - --sidepanel-header-radius-bottom-tablet: var(--radius-03); - --sidepanel-header-radius-bottom-mobile: var(--radius-03); - --logo-fund-horizontal-min-width-desktop: 165px; - --logo-fund-horizontal-min-width-rem-desktop: 10.3125rem; - --logo-fund-horizontal-min-width-tablet: 165px; - --logo-fund-horizontal-min-width-rem-tablet: 10.3125rem; - --logo-fund-horizontal-min-width-mobile: 165px; - --logo-fund-horizontal-min-width-rem-mobile: 10.3125rem; - --logo-fund-vertical-min-width-desktop: 96px; - --logo-fund-vertical-min-width-rem-desktop: 6rem; - --logo-fund-vertical-min-width-tablet: 96px; - --logo-fund-vertical-min-width-rem-tablet: 6rem; - --logo-fund-vertical-min-width-mobile: 96px; - --logo-fund-vertical-min-width-rem-mobile: 6rem; - --logo-fund-horizontal-min-height-desktop: 96px; - --logo-fund-horizontal-min-height-rem-desktop: 6rem; - --logo-fund-horizontal-min-height-tablet: 96px; - --logo-fund-horizontal-min-height-rem-tablet: 6rem; - --logo-fund-horizontal-min-height-mobile: 96px; - --logo-fund-horizontal-min-height-rem-mobile: 6rem; - --logo-fund-vertical-min-height-desktop: 182px; - --logo-fund-vertical-min-height-rem-desktop: 11.375rem; - --logo-fund-vertical-min-height-tablet: 182px; - --logo-fund-vertical-min-height-rem-tablet: 11.375rem; - --logo-fund-vertical-min-height-mobile: 182px; - --logo-fund-vertical-min-height-rem-mobile: 11.375rem; - --logo-riigiamet-min-height-desktop: 46px; - --logo-riigiamet-min-height-rem-desktop: 2.875rem; - --logo-riigiamet-min-height-tablet: 46px; - --logo-riigiamet-min-height-rem-tablet: 2.875rem; - --logo-riigiamet-min-height-mobile: 46px; - --logo-riigiamet-min-height-rem-mobile: 2.875rem; - --form-radio-selection-size-desktop: var(--dimensions-13); - --form-radio-selection-size-tablet: var(--dimensions-13); - --form-radio-selection-size-mobile: var(--dimensions-13); - --sidepanel-padding-y-desktop: var(--dimensions-05); - --sidepanel-padding-y-tablet: var(--dimensions-05); - --sidepanel-padding-y-mobile: var(--dimensions-05); - --button-meta-info-padding-desktop: var(--dimensions-03); - --button-meta-info-padding-tablet: var(--dimensions-03); - --button-meta-info-padding-mobile: var(--dimensions-03); -} - -:root.dark-mode { - --button-expand-background-default: var(--alpha-80); - --button-map-base-background-default: var(--neutral-850); - --button-map-base-background-hover: var(--neutral-850); - --button-map-base-background-selected: var(--neutral-850); - --button-map-base-border-default: var(--neutral-750); - --button-map-base-border-hover: var(--button-primary-border-hover); - --button-map-base-border-selected: var(--general-border-brand); - --button-map-base-text-background-default: var(--alpha-white-80); - --button-map-base-text-background-hover: var(--alpha-white-80); - --button-map-base-text-background-selected: var(--general-surface-brand-primary); - --button-map-info-background-default: var(--alpha-80); - --button-map-info-background-hover: var(--alpha-80); - --button-map-info-background-open: var(--button-primary-background-active); - --button-map-info-text-active: var(--link-inverted-default); - --button-map-info-text-default: var(--link-inverted-default); - --button-map-info-text-focus: var(--link-inverted-default); - --button-map-info-text-hover: var(--link-inverted-default); - --button-map-info-text-open: var(--link-inverted-default); - --button-neutral-inverted-background-active: var(--alpha-white-10); - --button-neutral-inverted-background-default: var(--alpha-01); - --button-neutral-inverted-background-focus: var(--alpha-01); - --button-neutral-inverted-background-hover: var(--alpha-white-10); - --button-neutral-inverted-text-active: var(--primary-400); - --button-neutral-inverted-text-default: var(--neutral-100); - --button-neutral-inverted-text-focus: var(--neutral-100); - --button-neutral-inverted-text-hover: var(--primary-200); - --button-neutral-background-active: var(--primary-700); - --button-neutral-background-default: var(--alpha-01); - --button-neutral-background-focus: var(--alpha-01); - --button-neutral-background-hover: var(--primary-800); - --button-neutral-border-default: var(--alpha-01); - --button-neutral-text-active: var(--primary-300); - --button-neutral-text-default: var(--neutral-100); - --button-neutral-text-focus: var(--neutral-100); - --button-neutral-text-hover: var(--primary-200); - --button-primary-background-active: var(--neutral-850); - --button-primary-background-default: var(--neutral-850); - --button-primary-background-focus: var(--neutral-850); - --button-primary-background-hover: var(--neutral-800); - --button-primary-border-active: var(--neutral-100); - --button-primary-border-default: var(--neutral-750); - --button-primary-border-focus: var(--neutral-750); - --button-primary-border-hover: var(--neutral-800); - --button-primary-text-active: var(--neutral-100); - --button-primary-text-default: var(--neutral-350); - --button-primary-text-focus: var(--neutral-100); - --button-primary-text-hover: var(--neutral-100); - --drag-indicator-accent: var(--general-border-accent); - --drag-indicator-light: var(--alpha-white-50); - --drag-indicator-button-background: var(--alpha-80); - --drag-indicator-button-icon: var(--general-icon-white); - --form-icon-selection-active: var(--general-icon-white); - --form-icon-selection-inactive: var(--general-icon-tertiary); - --logo-primary: var(--neutral-100); - --logo-secondary-blue: var(--neutral-100); - --logo-secondary-blue-dark: var(--neutral-100); - --map-application-link: var(--link-inverted-default); - --map-historical-background-default: var(--neutral-850); - --map-historical-background-hover: var(--neutral-850); - --map-historical-background-selected: var(--neutral-850); - --map-historical-border-default: var(--neutral-750); - --map-historical-border-hover: var(--button-primary-border-hover); - --map-historical-border-selected: var(--general-border-brand); - --map-historical-text-default: var(--neutral-300); - --map-historical-text-hover: var(--general-text-brand); - --map-historical-text-selected: var(--general-text-white); - --map-historical-text-background-default: var(--alpha-80); - --map-historical-text-background-hover: var(--alpha-80); - --map-historical-text-background-selected: var(--general-surface-brand-primary); - --measurement-tooltip: var(--alpha-80); - --mirror-indicator: var(--drag-indicator-accent); - --shape-default: var(--accent-600); - --shape-hover: var(--accent-500); - --shape-disabled-background: var(--alpha-white-50); - --shape-disabled-border: var(--button-card-disabled-border); - --shape-edit-background-default: #ff800099; - --shape-edit-background-hover: #ff8000cc; - --shape-edit-border-default: var(--accent-600); - --shape-edit-border-hover: var(--accent-600); - --shape-read-background-default: #e7f0f699; - --shape-read-background-hover: #e7f0f6cc; - --shape-read-border-default: var(--general-border-brand); - --shape-read-border-hover: var(--general-border-brand); - --sheet-body-default: var(--neutral-850); - --sheet-body-secondary: var(--neutral-750); - --sheet-footer-background: var(--sheet-header-background-default); - --sheet-header-border: var(--sheet-header-background-default); - --sheet-header-background-brand: var(--neutral-800); - --sheet-header-background-default: var(--neutral-800); - --sidepanel-background: var(--neutral-850); - --sidepanel-footer-background: var(--neutral-850); - --sidepanel-header-background-default: var(--neutral-800); - --sidepanel-header-background-open: var(--sidepanel-item-brand-background-open); - --sidepanel-item-brand-background-default: var(--neutral-750); - --sidepanel-item-brand-background-open: var(--neutral-800); - --sidepanel-item-brand-border-bottom: var(--neutral-750); - --sidepanel-item-white-background-default: var(--neutral-850); - --sidepanel-item-white-background-hover: var(--neutral-800); - --sidepanel-tree-default: var(--general-border-primary); - --sidepanel-tree-hover: var(--neutral-750); - --timeline-indicator-border: var(--drag-indicator-accent); - --timeline-indicator-background-default: var(--neutral-750); - --timeline-indicator-background-hover: var(--neutral-850); - --timeline-indicator-background-selected: var(--timeline-indicator-background-hover); - --timeline-indicator-icon-default: var(--general-icon-white); - --timeline-indicator-icon-hover: var(--general-icon-white); - --timeline-indicator-icon-selected: var(--general-icon-white); - --timeline-line-large: #fff; - --timeline-line-small: #fff; - --form-checkbox-radio-card-primary-selected-background: #99bdda; - --button-group-primary-selected-background: var(--form-checkbox-radio-card-primary-selected-background); -} diff --git a/libs/react-components/src/community/styles/_mixins.scss b/libs/react-components/src/community/styles/_mixins.scss deleted file mode 100644 index 034fcb35a..000000000 --- a/libs/react-components/src/community/styles/_mixins.scss +++ /dev/null @@ -1 +0,0 @@ -@forward '../../../design-tokens/mixins'; diff --git a/libs/react-components/src/community/styles/_typography.scss b/libs/react-components/src/community/styles/_typography.scss deleted file mode 100644 index 3e55e0206..000000000 --- a/libs/react-components/src/community/styles/_typography.scss +++ /dev/null @@ -1,64 +0,0 @@ -@use './mixins'; - -h1, -.h1, -.text-h1 { - font-size: var(--font-size-h1); - font-weight: var(--font-weight-h1); - line-height: var(--font-line-height-h1); -} - -h2, -.h2, -.text-h2 { - font-size: var(--font-size-h2); - font-weight: var(--font-weight-h2); - line-height: var(--font-line-height-h2); -} - -h3, -.h3, -.text-h3 { - font-size: var(--font-size-h3); - font-weight: var(--font-weight-h3); - line-height: var(--font-line-height-h3); -} - -h4, -.h4, -.text-h4 { - font-size: var(--font-size-h4); - font-weight: var(--font-weight-h4); - line-height: var(--font-line-height-h4); -} - -h5, -.h5, -.text-h5 { - font-size: var(--font-size-h5); - font-weight: var(--font-weight-h5); - line-height: var(--font-line-height-h5); -} - -h6, -.h6, -.text-h6 { - font-size: var(--font-size-h6); - font-weight: var(--font-weight-h6); - line-height: var(--font-line-height-h6); - color: var(--general-text-secondary); -} - -.text-normal { - font-family: var(--font-family); - font-size: var(--font-size-base); - font-weight: 400; - line-height: var(--font-line-height-base); - color: var(--general-text-primary); -} - -small, -.text-small { - font-size: var(--font-size-small); - line-height: var(--font-line-height-small); -} diff --git a/libs/react-components/src/community/styles/components/_calendar.scss b/libs/react-components/src/community/styles/components/_calendar.scss deleted file mode 100644 index 813505d1e..000000000 --- a/libs/react-components/src/community/styles/components/_calendar.scss +++ /dev/null @@ -1,29 +0,0 @@ -@use '../mixins'; - -/* Mui calendar picker */ - -div.MuiCalendarPicker-root { - margin: initial; - border-radius: var(--border-radius-default); - box-shadow: 0 2px 4px rgb(0 0 0 / 8%); - - @include mixins.print-grayscale; - - div.MuiPickersFadeTransitionGroup-root { - .MuiPickersCalendarHeader-label { - text-transform: capitalize; - } - } - - button.MuiPickersDay-root.Mui-highlighted { - font-weight: 700; - color: var(--color-primary-main); - background-color: var(--color-primary-highlight-subtle); - } - - button.MuiPickersDay-root.Mui-selected { - font-weight: 700; - color: var(--color-white); - background-color: var(--color-primary-main); - } -} diff --git a/libs/react-components/src/community/styles/components/_datepicker.scss b/libs/react-components/src/community/styles/components/_datepicker.scss deleted file mode 100644 index cfd99a7f3..000000000 --- a/libs/react-components/src/community/styles/components/_datepicker.scss +++ /dev/null @@ -1,22 +0,0 @@ -@use '../mixins'; - -/* Mui datepicker */ -div.MuiClockPointer-root { - @include mixins.print-grayscale; -} - -div.MuiClockPointer-root, -div.MuiClock-pin, -div.PrivatePickersYear-root button.Mui-selected, -div.MuiDayPicker-weekContainer button.Mui-selected { - background-color: var(--color-primary-main); -} - -div.MuiClockPointer-thumb { - border-color: var(--color-primary-main); -} - -div.PrivatePickersYear-root button.Mui-selected:hover, -div.MuiDayPicker-weekContainer button.Mui-selected:hover { - background-color: var(--color-primary-active); -} diff --git a/libs/react-components/src/community/styles/components/_text-editor.scss b/libs/react-components/src/community/styles/components/_text-editor.scss deleted file mode 100644 index cdf104ca1..000000000 --- a/libs/react-components/src/community/styles/components/_text-editor.scss +++ /dev/null @@ -1,3 +0,0 @@ -.public-DraftEditor-content { - min-height: 6.25rem; // 100px -} diff --git a/libs/react-components/src/community/styles/components/_toast.scss b/libs/react-components/src/community/styles/components/_toast.scss deleted file mode 100644 index 856f5e572..000000000 --- a/libs/react-components/src/community/styles/components/_toast.scss +++ /dev/null @@ -1,12 +0,0 @@ -/* Toast overrides */ -.Toastify .Toastify__toast { - min-height: unset; - padding: 0; - margin-bottom: 1rem; - font-family: inherit; - color: var(--color-text-default); -} - -.Toastify .Toastify__toast-body { - padding: 0; -} diff --git a/libs/react-components/src/community/styles/index.scss b/libs/react-components/src/community/styles/index.scss deleted file mode 100644 index 27dea71d5..000000000 --- a/libs/react-components/src/community/styles/index.scss +++ /dev/null @@ -1,8 +0,0 @@ -@forward '../../../design-tokens/variables'; -@use 'map-component.variables'; -@use 'components/datepicker'; -@use 'components/calendar'; -@use 'components/text-editor'; -@use 'components/toast'; -@use '@tehik-ee/tedi-core/helpers'; -@use '@tehik-ee/tedi-core/print'; diff --git a/libs/react-components/src/community/styles/storybook.scss b/libs/react-components/src/community/styles/storybook.scss deleted file mode 100644 index 70acc7beb..000000000 --- a/libs/react-components/src/community/styles/storybook.scss +++ /dev/null @@ -1,114 +0,0 @@ -@use 'mixins'; - -/* Helper classes for storybook examples */ -body { - height: auto; -} - -.example-box { - padding-top: 0.75rem; - padding-bottom: 0.75rem; - background-color: rgb(39 41 43 / 3%); - border: 1px solid rgb(39 41 43 / 10%); -} - -.example-row + .example-row { - margin-top: 0.75rem; -} - -/* text-editor-example **/ -.text-editor-example li { - margin-left: 1.5rem; // to match with draft.js implementation -} - -pre { - padding: 1rem; - white-space: pre-line; - border: 1px solid var(--color-border-contrast); - border-radius: 3px; -} - -.function { - white-space: break-spaces; -} - -code { - padding: 4px; - color: var(--neutral-100); - white-space: nowrap; - background-color: var(--red-600); - border-radius: 4px; -} - -a { - color: var(--color-primary-main); - text-decoration: none; - - &:where(:hover), - &:where(:focus-visible) { - text-decoration: underline; - } -} - -iframe { - display: block; -} - -.not-found { - background: linear-gradient(137.5deg, rgb(255 248 233 / 0%) 33.36%, #fff1d3 88.42%); -} - -.not-found__image { - display: block; - align-self: flex-end; - max-height: 50rem; - object-fit: cover; -} - -li > ul, -li > ol { - padding-left: 1.5rem; -} - -/* Color examples */ - -.color { - width: 200px; -} - -.color__example { - width: 100%; - height: 120px; - border-radius: 0.25rem; - transition: transform 0.2s ease-in; - - &:hover { - box-shadow: 0 4px 4px rgb(0 0 0 / 25%); - transform: scale(1.025); - } - - @include mixins.button-reset; -} - -.color__text { - @include mixins.button-reset; - - text-align: left; - transition: transform 0.2s ease-in; - - &:hover { - color: var(--color-black); - transform: scale(1.05); - } -} - -.material-symbols-outlined { - // Storybook has new styles that overwrite this on "un-styled" elements - font-family: 'Material Symbols Outlined', sans-serif !important; -} - -// temporary fix to display Select menu portal correctly in storybooks docs file -.innerZoomElementWrapper div.select__menu-portal { - top: auto; - left: auto; -} diff --git a/libs/react-components/src/community/types/index.ts b/libs/react-components/src/community/types/index.ts deleted file mode 100644 index c56672429..000000000 --- a/libs/react-components/src/community/types/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -// eslint-disable-next-line @typescript-eslint/no-explicit-any -export type IntentionalAny = any; diff --git a/libs/react-components/src/tedi/components/base/icon/icon.module.scss b/libs/react-components/src/tedi/components/base/icon/icon.module.scss deleted file mode 100644 index ad2563635..000000000 --- a/libs/react-components/src/tedi/components/base/icon/icon.module.scss +++ /dev/null @@ -1,100 +0,0 @@ -@use '@tehik-ee/tedi-core/mixins'; - -$icon-colors: ( - 'primary': 'general-icon-primary', - 'secondary': 'general-icon-secondary', - 'tertiary': 'general-icon-tertiary', - 'brand': 'general-icon-brand', - 'brand-dark': 'general-icon-brand-dark', - 'success': 'general-icon-success', - 'warning': 'general-icon-warning', - 'warning-dark': 'general-icon-warning-dark', - 'danger': 'general-icon-danger', - 'white': 'general-icon-white', -); -$icon-backgrounds: ( - 'primary': 'general-icon-background-primary', - 'secondary': 'general-icon-background-secondary', - 'brand-primary': 'general-icon-background-brand-primary', - 'brand-secondary': 'general-icon-background-brand-secondary', -); -$icon-sizes: ( - 8: icon-00, - 12: icon-01, - 16: icon-02, - 18: icon-03, - 24: icon-05, - 36: icon-06, - 48: icon-07, -); - -.tedi-icon__wrapper { - flex-shrink: 0; - - &--block { - &.tedi-icon__wrapper--bg { - display: flex; - } - } - - &--inline { - &.tedi-icon__wrapper--bg { - display: inline-flex; - } - } - - &.tedi-icon__wrapper--bg { - align-items: center; - justify-content: center; - border-radius: 100%; - - @each $name, $var in $icon-backgrounds { - &-#{$name} { - background-color: var(--#{$var}); - } - } - - @each $size, $vars in $icon-sizes { - &.tedi-icon__wrapper--size-#{$size} { - width: calc(var(--_width) * 2); - height: calc(var(--_height) * 2); - - @include mixins.responsive-styles(--_width, $vars); - @include mixins.responsive-styles(--_height, $vars); - } - } - } -} - -.tedi-icon { - flex-shrink: 0; - max-width: 1em; - overflow: hidden; - - &.tedi-icon--block { - display: block; - } - - &.tedi-icon--inline { - display: inline; - font-size: inherit; - vertical-align: -11.5%; - } - - @each $name, $var in $icon-colors { - &--color-#{$name} { - color: var(--#{$var}); - } - } - - @each $size, $vars in $icon-sizes { - &--size-#{$size} { - @include mixins.responsive-styles(font-size, $vars); - } - } - - &--filled { - font-variation-settings: 'FILL' 1; - font-optical-sizing: auto; - } -} diff --git a/libs/react-components/src/tedi/components/base/icon/icon.spec.tsx b/libs/react-components/src/tedi/components/base/icon/icon.spec.tsx deleted file mode 100644 index 46dee126a..000000000 --- a/libs/react-components/src/tedi/components/base/icon/icon.spec.tsx +++ /dev/null @@ -1,99 +0,0 @@ -import { render } from '@testing-library/react'; - -import { Icon } from './icon'; - -import '@testing-library/jest-dom'; - -describe('Icon component', () => { - it('renders with default props', () => { - const { container } = render(); - const iconElement = container.querySelector('span[data-name="icon"]'); - expect(iconElement).toBeInTheDocument(); - expect(iconElement).toHaveClass('notranslate'); - expect(iconElement).toHaveClass('material-symbols--outlined'); - expect(iconElement).toHaveClass('tedi-icon'); - expect(iconElement).toHaveClass('tedi-icon--size-24'); - expect(iconElement).toHaveClass('tedi-icon--block'); - }); - - it('applies custom className to the icon when no background is set', () => { - const { container } = render(); - const iconElement = container.querySelector('span[data-name="icon"]'); - expect(iconElement).toBeInTheDocument(); - expect(iconElement).toHaveClass('custom-class'); - }); - - it('applies custom className to the wrapper when background is set', () => { - const { container } = render(); - const wrapperElement = container.querySelector('div.tedi-icon__wrapper'); - const iconElement = container.querySelector('span[data-name="icon"]'); - - expect(wrapperElement).toBeInTheDocument(); - expect(wrapperElement).toHaveClass('custom-class'); - expect(wrapperElement).toHaveClass('tedi-icon__wrapper--bg'); - expect(wrapperElement).toHaveClass('tedi-icon__wrapper--bg-primary'); - expect(iconElement).toBeInTheDocument(); - expect(iconElement).not.toHaveClass('custom-class'); - }); - - it('renders with custom size', () => { - const { container } = render(); - const iconElement = container.querySelector('span[data-name="icon"]'); - expect(iconElement).toBeInTheDocument(); - expect(iconElement).toHaveClass('tedi-icon--size-36'); - }); - - it('renders with custom type', () => { - const { container } = render(); - const iconElement = container.querySelector('span[data-name="icon"]'); - expect(iconElement).toBeInTheDocument(); - expect(iconElement).toHaveClass('material-symbols--sharp'); - }); - - it('renders with custom color', () => { - const { container } = render(); - const iconElement = container.querySelector('span[data-name="icon"]'); - expect(iconElement).toBeInTheDocument(); - expect(iconElement).toHaveClass('tedi-icon--color-success'); - }); - - it('renders with custom display', () => { - const { container } = render(); - const iconElement = container.querySelector('span[data-name="icon"]'); - expect(iconElement).toBeInTheDocument(); - expect(iconElement).toHaveClass('tedi-icon--inline'); - }); - - it('renders filled variant', () => { - const { container } = render(); - const iconElement = container.querySelector('span[data-name="icon"]'); - expect(iconElement).toBeInTheDocument(); - expect(iconElement).toHaveClass('tedi-icon--filled'); - }); - - it('renders with background color', () => { - const { container } = render(); - const wrapperElement = container.querySelector('div.tedi-icon__wrapper'); - const iconElement = container.querySelector('span[data-name="icon"]'); - - expect(wrapperElement).toBeInTheDocument(); - expect(wrapperElement).toHaveClass('tedi-icon__wrapper--bg'); - expect(wrapperElement).toHaveClass('tedi-icon__wrapper--bg-primary'); - expect(iconElement).toBeInTheDocument(); - }); - - it('renders with custom wrapper size', () => { - const { container } = render(); - const wrapperElement = container.querySelector('div.tedi-icon__wrapper'); - expect(wrapperElement).toHaveClass('tedi-icon__wrapper--size-48'); - }); - - it('renders with background and assigns ref to wrapper', () => { - const ref = { current: null }; - const { container } = render(); - - const wrapperElement = container.querySelector('div.tedi-icon__wrapper'); - expect(wrapperElement).toBeInTheDocument(); - expect(ref.current).toBe(wrapperElement); - }); -}); diff --git a/libs/react-components/src/tedi/components/base/icon/icon.stories.tsx b/libs/react-components/src/tedi/components/base/icon/icon.stories.tsx deleted file mode 100644 index b61dff7c7..000000000 --- a/libs/react-components/src/tedi/components/base/icon/icon.stories.tsx +++ /dev/null @@ -1,286 +0,0 @@ -import { Meta, StoryFn, StoryObj } from '@storybook/react'; -import classNames from 'classnames'; - -import { Col, Row } from '../../layout/grid'; -import { VerticalSpacing } from '../../layout/vertical-spacing'; -import { Heading } from '../typography/heading/heading'; -import { Icon, IconProps } from './icon'; - -/** - * Figma ↗
- * Zeroheight ↗
- * Official Google Material Icons homepage icons ↗
- * Material Icons Figma ↗
- * Figma Material Symbols plugin ↗ - */ -const meta: Meta = { - title: 'Tedi-Ready/Base/Icon', - component: Icon, - parameters: { - design: { - type: 'figma', - url: 'https://www.figma.com/file/jWiRIXhHRxwVdMSimKX2FF/TEDI-Design-System-(draft)?type=design&node-id=45-30752&mode=dev', - }, - }, -}; - -export default meta; -type Story = StoryObj; - -const sizeArray: IconProps['size'][] = [8, 12, 16, 18, 24, 36, 48]; -const colorArray: IconProps['color'][] = [ - 'primary', - 'secondary', - 'tertiary', - 'brand', - 'brand-dark', - 'success', - 'warning', - 'warning-dark', - 'danger', - 'white', -]; - -type IconPropsType = IconProps['size'] | IconProps['color'] | IconProps['type'] | IconProps['background']; - -interface MultipleProps { - array: Type[]; - property: keyof IconProps; - items: { - name: string; - property: string; - color: IconProps['color']; - background: IconProps['background']; - size: IconProps['size']; - }[]; -} - -type TemplateMultipleProps = MultipleProps & IconProps; - -const TemplateRow: StoryFn = (args) => { - const { array, property, ...iconProps } = args; - - return ( - <> - - Outlined - - - {array.map((value, key) => ( - -
- -
- - ))} -
- - Filled - - - {array.map((value, key) => ( - -
- -
- - ))} -
- - ); -}; - -const TemplateColumn: StoryFn = (args) => { - const { array, property, ...iconProps } = args; - - return ( -
- {array.map((value, key) => ( - - - {value?.toString()} {value === 24 && default} - - - -   - - - - ))} -
- ); -}; - -const TemplateColumnWithMultipleVariants: StoryFn = (args) => { - const { items } = args; - - return ( -
- {items.map((item, key) => ( - - - {item.size?.toString()}  - {item.size === 24 && default} - - - -   - - - - ))} -
- ); -}; - -const TemplateColumnWithBackgroundCircleVarians: StoryFn = () => { - return ( - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ); -}; - -const Template: StoryFn = (args) => ; - -export const Default: Story = { - render: Template, - - args: { - name: 'account_circle', - }, -}; - -export const Sizes: Story = { - name: 'Icon Size', - render: TemplateColumn, - - args: { - name: 'account_circle', - property: 'size', - color: 'primary', - array: sizeArray, - }, -}; - -export const SizesWithBackground: Story = { - name: 'Icon size inside background', - render: TemplateColumnWithMultipleVariants, - args: { - items: [ - { - name: 'info', - property: 'size', - color: 'brand', - background: 'brand-secondary', - size: 16, - }, - { - name: 'vaccines', - property: 'size', - color: 'brand', - background: 'brand-secondary', - size: 24, - }, - ], - }, -}; - -export const Colors: Story = { - render: TemplateRow, - name: 'Icon colors', - - args: { - name: 'account_circle', - property: 'color', - array: colorArray, - size: 48, - }, -}; - -export const Backgrounds: Story = { - render: TemplateColumnWithBackgroundCircleVarians, - name: 'Icon background colors', -}; - -export const UsedInsideText: Story = { - render: (args: { name: IconProps['name'] }) => { - return ( - - - - This is level 1 heading with inline icon - - - - This is level 2 heading with inline icon - - - - This is level 3 heading with inline icon - - - - This is level 4 heading with inline icon - - - - This is level 5 heading with inline icon - - - - This is level 6 heading with inline icon - -

- - This is paragraph text with inline icon -

- - - This is small text with inline icon - -
- ); - }, - args: { - name: 'account_circle', - }, -}; diff --git a/libs/react-components/src/tedi/components/base/icon/icon.tsx b/libs/react-components/src/tedi/components/base/icon/icon.tsx deleted file mode 100644 index ddb41751a..000000000 --- a/libs/react-components/src/tedi/components/base/icon/icon.tsx +++ /dev/null @@ -1,143 +0,0 @@ -import cn from 'classnames'; -import { forwardRef } from 'react'; - -import styles from './icon.module.scss'; - -export type IconSize = 8 | 12 | 16 | 18 | 24 | 36 | 48; -export type IconType = 'outlined' | 'sharp' | 'rounded'; -export type IconColor = - | 'primary' - | 'secondary' - | 'tertiary' - | 'brand' - | 'brand-dark' - | 'success' - | 'warning' - | 'warning-dark' - | 'danger' - | 'white'; -export type IconBackgroundColor = 'primary' | 'secondary' | 'brand-primary' | 'brand-secondary'; - -export interface IconSharedProps { - /** - * Name of material icon - * https://fonts.google.com/icons - */ - name: string; - /** - * Additional classes to style the icon or its wrapper. - * - If `background` is provided, the `className` will be applied to the wrapper element. - * - If `background` is not provided, the `className` will be applied directly to the icon element. - */ - className?: string; - /** - * Type of icon - * It is recommended to only use one type throughout your app - * @default outlined - */ - type?: IconType; - /** - * Size of the icon - * @default 24 - */ - size?: IconSize; - /** - * Render a filled variant of the icon - * @default false - */ - filled?: boolean; - /** - * Which color Icon should be - * Use 'positive', 'important' or 'warning' with caution, usually they should not be in application UI - * @default primary - */ - color?: IconColor; - /** - * Icons label for screen-readers. - * If omitted then the icon is hidden for screen-readers. - */ - label?: string; -} - -export interface IconWithBackgroundProps extends IconSharedProps { - /** - * Add round background - */ - background: IconBackgroundColor; - display?: 'block'; -} - -export interface IconWithoutBackgroundProps extends IconSharedProps { - background?: undefined; - /** - * Type of display - * @default block - */ - display?: 'block' | 'inline'; -} - -export type IconProps = IconSharedProps & (IconWithBackgroundProps | IconWithoutBackgroundProps); - -export const Icon = forwardRef((props: IconProps, ref): JSX.Element => { - const { - className, - name, - type = 'outlined', - size = 24, - filled, - display = 'block', - color = 'primary', - background, - label, - ...rest - } = props; - - const wrapperBEM = cn( - styles['tedi-icon__wrapper'], - { - [styles['tedi-icon__wrapper--bg']]: background, - [styles[`tedi-icon__wrapper--bg-${background}`]]: background, - [styles[`tedi-icon__wrapper--size-${size}`]]: size, - [styles['tedi-icon__wrapper--block']]: background, - }, - background && className - ); - - const iconBEM = cn( - 'notranslate', - 'material-symbols', - type && [`material-symbols--${type}`], - styles['tedi-icon'], - color && styles[`tedi-icon--color-${color}`], - size && styles[`tedi-icon--size-${size}`], - display && styles[`tedi-icon--${display}`], - filled && styles['tedi-icon--filled'], - !background && className - ); - - const iconElement = ( - - {name} - - ); - - if (background) { - return ( -
- {iconElement} -
- ); - } - - return iconElement; -}); - -Icon.displayName = 'Icon'; diff --git a/libs/react-components/src/tedi/components/base/typography/heading/heading.spec.tsx b/libs/react-components/src/tedi/components/base/typography/heading/heading.spec.tsx deleted file mode 100644 index 2e1ace161..000000000 --- a/libs/react-components/src/tedi/components/base/typography/heading/heading.spec.tsx +++ /dev/null @@ -1,10 +0,0 @@ -import { render } from '@testing-library/react'; - -import { Heading } from './heading'; - -describe('Heading', () => { - it('should render successfully', () => { - const { baseElement } = render(Title); - expect(baseElement).toBeTruthy(); - }); -}); diff --git a/libs/react-components/src/tedi/components/base/typography/heading/heading.stories.tsx b/libs/react-components/src/tedi/components/base/typography/heading/heading.stories.tsx deleted file mode 100644 index cef4e46c3..000000000 --- a/libs/react-components/src/tedi/components/base/typography/heading/heading.stories.tsx +++ /dev/null @@ -1,141 +0,0 @@ -import { Meta, StoryFn, StoryObj } from '@storybook/react'; - -import { TextRow } from '../../../../providers/storybook-provider/storybook-provider'; -import { Col, Row } from '../../../layout/grid'; -import { HeadingModifiers, Text } from '../text/text'; -import { Heading } from './heading'; - -/** - * Figma ↗
- * Zeroheight ↗ - */ - -const meta: Meta = { - title: 'Tedi-Ready/Base/Typography/Heading', - component: Heading, - parameters: { - design: { - type: 'figma', - url: 'https://www.figma.com/design/jWiRIXhHRxwVdMSimKX2FF/TEDI-Design-System-(draft)?node-id=115-11630&m=dev', - }, - }, -}; - -export default meta; -type Story = StoryObj; - -const headings: HeadingModifiers[] = ['h1', 'h2', 'h3', 'h4', 'h5', 'h6']; - -const TemplateHeadings: StoryFn = () => ( -
- - - - Desktop - - - - - Mobile - - - - {headings.map((heading, key) => ( - - Heading {heading.toUpperCase()} - - } - mobileText={ - - Heading {heading.toUpperCase()} - - } - className={key !== headings.length - 1 ? 'border-bottom' : ''} - /> - ))} -
-); - -export const Default: Story = { - args: { - children: 'Heading', - }, -}; - -export const Headings: Story = { - render: TemplateHeadings, -}; - -export const CustomModifier: Story = { - render: () => ( - <> - - H4 heading with H1 styles and warning color - - - H2 heading with normal bold text and brand color - - - H1 element with normal text styles - - - ), -}; - -export const SemanticHeadings: Story = { - render: () => ( - <> - - -

Heading 1

- - - Heading 1 - -
- - -

Heading 2

- - - Heading 2 - -
- - -

Heading 3

- - - Heading 3 - -
- - -

Heading 4

- - - Heading 4 - -
- - -
Heading 5
- - - Heading 5 - -
- - -
Heading 6
- - - Heading 6 - -
- - ), -}; diff --git a/libs/react-components/src/tedi/components/base/typography/heading/heading.tsx b/libs/react-components/src/tedi/components/base/typography/heading/heading.tsx deleted file mode 100644 index 39f924cae..000000000 --- a/libs/react-components/src/tedi/components/base/typography/heading/heading.tsx +++ /dev/null @@ -1,22 +0,0 @@ -import { HeadingModifiers, Text, TextProps } from '../text/text'; - -export type HeadingLevel = 1 | 2 | 3 | 4 | 5 | 6; - -export type HeadingProps = Omit & { - /** - * Semantic heading tag - * h1-h6 are allowed values - * @default h1 - */ - element?: Extract; -}; - -export const Heading = (props: HeadingProps) => { - const { children, element = 'h1', ...rest } = props; - - return ( - - {children} - - ); -}; diff --git a/libs/react-components/src/tedi/components/base/typography/text/text.spec.tsx b/libs/react-components/src/tedi/components/base/typography/text/text.spec.tsx deleted file mode 100644 index 1ef3d6c13..000000000 --- a/libs/react-components/src/tedi/components/base/typography/text/text.spec.tsx +++ /dev/null @@ -1,52 +0,0 @@ -import { render } from '@testing-library/react'; - -import { Text } from './text'; - -import '@testing-library/jest-dom'; - -describe('Text Component', () => { - it('renders text correctly with default props', () => { - const { getByTestId } = render(Text test); - const textElement = getByTestId('text'); - expect(textElement).toBeInTheDocument(); - expect(textElement.tagName).toBe('P'); - expect(textElement).toHaveTextContent('Text test'); - }); - - it('renders text with custom element correctly', () => { - const { getByTestId } = render( - - Text test - - ); - const textElement = getByTestId('text'); - expect(textElement).toBeInTheDocument(); - expect(textElement.tagName).toBe('DIV'); - expect(textElement).toHaveTextContent('Text test'); - }); - - it('renders text with custom modifiers correctly', () => { - const { getByTestId } = render( - - Text test - - ); - const textElement = getByTestId('text'); - expect(textElement).toBeInTheDocument(); - expect(textElement).toHaveClass('text-bold'); - expect(textElement).toHaveClass('text-italic'); - expect(textElement).toHaveTextContent('Text test'); - }); - - it('renders text with custom color correctly', () => { - const { getByTestId } = render( - - Text test - - ); - const textElement = getByTestId('text'); - expect(textElement).toBeInTheDocument(); - expect(textElement).toHaveClass('tedi-text--primary'); - expect(textElement).toHaveTextContent('Text test'); - }); -}); diff --git a/libs/react-components/src/tedi/components/base/typography/text/text.stories.tsx b/libs/react-components/src/tedi/components/base/typography/text/text.stories.tsx deleted file mode 100644 index ade0fb410..000000000 --- a/libs/react-components/src/tedi/components/base/typography/text/text.stories.tsx +++ /dev/null @@ -1,250 +0,0 @@ -import { Meta, StoryFn, StoryObj } from '@storybook/react'; - -import { TextRow } from '../../../../providers/storybook-provider/storybook-provider'; -import { Col, Row } from '../../../layout/grid'; -import { VerticalSpacing } from '../../../layout/vertical-spacing'; -import { Text } from './text'; - -/** - * Figma ↗
- * Zeroheight ↗ - */ - -const meta: Meta = { - component: Text, - title: 'Tedi-Ready/Base/Typography/Text', - parameters: { - design: { - type: 'figma', - url: 'https://www.figma.com/design/jWiRIXhHRxwVdMSimKX2FF/TEDI-Design-System-(draft)?node-id=115-11630&m=dev', - }, - }, -}; - -export default meta; -type Story = StoryObj; - -const TemplateSubtitles: StoryFn = () => ( -
- - - - Desktop - - - - - Mobile - - - - - Subtitle - - } - mobileText={ - - Subtitle - - } - className="border-bottom" - /> - - Subtitle Small - - } - mobileText={ - - Subtitle Small - - } - className="border-bottom" - /> - - Label - - } - mobileText={ - - Label - - } - className="border-bottom" - /> - - Label bold - - } - mobileText={ - - Label bold - - } - /> -
-); - -const TemplateBodyText: StoryFn = () => ( -
- - - - Desktop - - - - - Mobile - - - - - Body bold} - mobileText={Body bold} - className="border-bottom" - /> - Body italic} - mobileText={Body italic} - className="border-bottom" - /> - Small} - mobileText={Small} - className="border-bottom" - /> - Small bold} - mobileText={Small bold} - className="border-bottom" - /> - Small italic} - mobileText={Small italic} - /> -
-); - -const TemplateGeneralText: StoryFn = () => ( - - - - - Rebane on väikese koera suurune ja pika koheva sabaga. Joostes hoiab ta saba horisontaalselt. Tema selja - karvad on oranžid. Eestis eelistab ta elupaigana metsatukkasid. - - - - - - - Rebane on väikese koera suurune ja pika koheva sabaga. Joostes hoiab ta saba horisontaalselt. Tema selja - karvad on oranžid. Eestis eelistab ta elupaigana metsatukkasid. - - - - - - - Rebane on väikese koera suurune ja pika koheva sabaga. Joostes hoiab ta saba horisontaalselt. Tema selja - karvad on oranžid. Eestis eelistab ta elupaigana metsatukkasid. - - - - - - - Rebane on väikese koera suurune ja pika koheva sabaga. Joostes hoiab ta saba horisontaalselt. Tema selja - karvad on oranžid. Eestis eelistab ta elupaigana metsatukkasid. - - - - - - - Rebane on väikese koera suurune ja pika koheva sabaga. Joostes hoiab ta saba horisontaalselt. Tema selja - karvad on oranžid. Eestis eelistab ta elupaigana metsatukkasid. - - - - -); - -const TemplateStatusText: StoryFn = () => ( - - - - - Rebane on väikese koera suurune ja pika koheva sabaga. Joostes hoiab ta saba horisontaalselt. Tema selja - karvad on oranžid. Eestis eelistab ta elupaigana metsatukkasid. - - - - - - - Rebane on väikese koera suurune ja pika koheva sabaga. Joostes hoiab ta saba horisontaalselt. Tema selja - karvad on oranžid. Eestis eelistab ta elupaigana metsatukkasid. - - - - - - - Rebane on väikese koera suurune ja pika koheva sabaga. Joostes hoiab ta saba horisontaalselt. Tema selja - karvad on oranžid. Eestis eelistab ta elupaigana metsatukkasid. - - - - - - - Rebane on väikese koera suurune ja pika koheva sabaga. Joostes hoiab ta saba horisontaalselt. Tema selja - karvad on oranžid. Eestis eelistab ta elupaigana metsatukkasid. - - - - - - - Rebane on väikese koera suurune ja pika koheva sabaga. Joostes hoiab ta saba horisontaalselt. Tema selja - karvad on oranžid. Eestis eelistab ta elupaigana metsatukkasid. - - - - -); - -export const Default: Story = { - args: { - children: 'Text', - }, -}; - -export const BodyText: Story = { - render: TemplateBodyText, - name: 'Body', -}; - -export const Subtitles: Story = { - render: TemplateSubtitles, -}; - -export const GeneralText: Story = { - render: TemplateGeneralText, - name: 'General text colors', -}; - -export const StatusText: Story = { - render: TemplateStatusText, - name: 'Status text colors', -}; diff --git a/libs/react-components/src/tedi/components/base/typography/text/text.tsx b/libs/react-components/src/tedi/components/base/typography/text/text.tsx deleted file mode 100644 index fc90766de..000000000 --- a/libs/react-components/src/tedi/components/base/typography/text/text.tsx +++ /dev/null @@ -1,113 +0,0 @@ -import cn from 'classnames'; - -import { BreakpointSupport, useBreakpointProps } from '../../../../helpers'; -import type { HeadingLevel } from '../heading/heading'; - -export type HeadingModifiers = `h${HeadingLevel}`; - -export type TextModifiers = - | HeadingModifiers - | 'normal' - | 'small' - | 'bold' - | 'thin' - | 'italic' - | 'center' - | 'left' - | 'right' - | 'nowrap' - | 'break-all' - | 'break-word' - | 'break-spaces' - | 'uppercase' - | 'lowercase' - | 'capitalize' - | 'capitalize-first' - | 'inline-block' - | 'inline' - | 'line-normal' - | 'line-condensed' - | 'subtitle'; - -export type TextColor = - | 'primary' - | 'secondary' - | 'tertiary' - | 'white' - | 'disabled' - | 'brand' - | 'success' - | 'warning' - | 'danger' - | 'info' - | 'neutral'; - -export type TextElement = 'div' | 'p' | 'span' | 'li' | 'label' | HeadingModifiers; - -type TextBreakpointProps = { - /** - * Additional class - */ - className?: string; - /** - * Base element - * @default p - */ - element?: TextElement; - /** - * Single or multiple modifiers to change the text behavior - */ - modifiers?: TextModifiers[] | TextModifiers; - /** - * Color of the text - * Use 'success', 'important' or 'warning' with caution, usually they should not be in application UI - * @default primary - */ - color?: TextColor; -}; - -export interface TextProps extends BreakpointSupport { - /** - * Children of the text - */ - children: React.ReactNode; - /** - * ID attribute - */ - id?: string; - /** - * Allows to focus the element - */ - tabIndex?: number; -} - -const isHeadingModifier = (modifier: TextModifiers): modifier is HeadingModifiers => { - return /^h[1-6]$/.test(modifier); -}; - -export const Text = (props: TextProps): JSX.Element => { - const { getCurrentBreakpointProps } = useBreakpointProps(props.defaultServerBreakpoint); - const { - children, - className, - tabIndex = props.id ? -1 : undefined, - element: Element = 'p', - modifiers, - color, - ...rest - } = getCurrentBreakpointProps(props); - - const modifiersArray = typeof modifiers === 'string' ? [modifiers] : modifiers; - - const BEM = cn( - className, - modifiersArray?.map((modifier) => (isHeadingModifier(modifier) ? `tedi-text--${modifier}` : `text-${modifier}`)), - { [`tedi-text--${color}`]: color } - ); - - return ( - - {children} - - ); -}; diff --git a/libs/react-components/src/tedi/components/base/typography/typography.mdx b/libs/react-components/src/tedi/components/base/typography/typography.mdx deleted file mode 100644 index 8f25afc8c..000000000 --- a/libs/react-components/src/tedi/components/base/typography/typography.mdx +++ /dev/null @@ -1,75 +0,0 @@ -import { Meta, Unstyled } from '@storybook/blocks'; - - - -[Figma ↗]()
-[Zeroheight ↗](https://zeroheight.com/1ee8444b7/p/4651ec-typography)
- -### Text - -Text is a helper component used to apply different colors and modifiers to text. -Modifiers prop accepts array of modifiers or single modifier. It helps you to combine modifiers if needed. -Every text modifier and color has its own global CSS class, but using the Text component is recommended, as it allows your IDE to suggest possible values and helps reduce mistakes. - -Basic usage: - -```jsx - - Some text - -``` - -Multiple modifiers: - -```jsx - - Some text - -``` - -You can also use: - -```jsx -

Some text

-``` - -You can also use TEDI classes, such as: - -```jsx -

Some text

-``` - -### Heading - -This component is a wrapper around the `` component. It should only be used when we want to semantically render h1-h6 tags. -The same result can be achieved by using ``, but using this component provides a better indicator in code that we are using semantic headings. - -NB! Headings have dynamic font styles, which means that they have different font-size/font-weight/line-height -values for desktop/mobile. - -Basic usage: - -```jsx -Some heading -``` - -### Breakpoint support - -You can use breakpoint props to render modifiers, colors, or even elements differently based on different breakpoints. Simply pass an object as a prop -corresponding to the desired breakpoint. Each object can contain any combination of `modifiers` and `color` to adjust the text styling. - -Breakpoints available: `sm` `md` `lg` `xl` `xxl` - -Basic usage: - -```jsx - - Responsive Text - -``` - -```jsx - - Responsive Heading - -``` diff --git a/libs/react-components/src/tedi/components/buttons/button-content/README.md b/libs/react-components/src/tedi/components/buttons/button-content/README.md deleted file mode 100644 index 621f60a47..000000000 --- a/libs/react-components/src/tedi/components/buttons/button-content/README.md +++ /dev/null @@ -1,3 +0,0 @@ -ButtonContent component is only used internally by @tedi-design-system and is not exported. -It contains rendering visuals of links and buttons, so Anchor can be rendered visually as Button and vice versa. -But it forces component users to always use semantically correct component. diff --git a/libs/react-components/src/tedi/components/buttons/button-content/button-content.module.scss b/libs/react-components/src/tedi/components/buttons/button-content/button-content.module.scss deleted file mode 100644 index 8defff535..000000000 --- a/libs/react-components/src/tedi/components/buttons/button-content/button-content.module.scss +++ /dev/null @@ -1,537 +0,0 @@ -@use '@tehik-ee/tedi-core/mixins'; -@use '@tehik-ee/tedi-core/bootstrap-utility/breakpoints'; - -$btn-height: 2.5rem; -$btn-height-small: 2rem; -$btn-height-large: 3.5rem; -$btn-width-large: 3.72rem; - -@mixin button-variant( - $background, - $border, - $color, - $background-hover, - $border-hover, - $color-hover, - $border-focus, - $background-focus, - $color-active, - $border-active, - $background-active, - $background-disabled: var(--button-main-disabled-general-background), - $border-disabled: var(--button-main-disabled-general-border), - $color-disabled: var(--button-main-disabled-general-text) -) { - color: $color; - background-color: $background; - border-color: $border; - - &.tedi-btn--is-active:not(:disabled, [aria-disabled='true']), - &:active:not(:disabled, [aria-disabled='true']) { - color: $color-active; - background-color: $background-active; - border-color: $border-active; - } - - &.tedi-btn--is-hovered:not(:disabled, [aria-disabled='true']), - &:hover:not(:disabled, [aria-disabled='true']) { - color: $color-hover; - background-color: $background-hover; - border-color: $border-hover; - } - - &:focus-visible:not(:disabled, [aria-disabled='true']) { - background-color: $background-focus; - border-color: $border; - outline: 2px solid $border-focus; - outline-offset: 1px; - } - - &:disabled { - color: $color-disabled; - background-color: $background-disabled; - border-color: $border-disabled; - } -} - -@mixin button-secondary-variant( - $border, - $color, - $background-hover, - $border-hover, - $color-hover, - $border-focus, - $background-focus, - $color-active, - $border-active, - $background-active, - $background-disabled: var(--button-main-disabled-general-background), - $border-disabled: var(--button-main-disabled-general-border), - $color-disabled: var(--button-main-disabled-general-text) -) { - @include button-variant( - var(--button-main-secondary-background-default), - $border, - $color, - $background-hover, - $border-hover, - $color-hover, - $border-focus, - $background-focus, - $color-active, - $border-active, - $background-active, - $background-disabled, - $border-disabled, - $color-disabled - ); -} - -@mixin link-variant($color, $color-hover, $color-active, $color-focus) { - color: $color; - - &.tedi-btn--is-hovered:not(:disabled, [aria-disabled='true']), - &:hover:not(:disabled, [aria-disabled='true']) { - color: $color-hover; - } - - &.tedi-btn--is-active:not(:disabled, [aria-disabled='true']), - &:active:not(:disabled, [aria-disabled='true']) { - color: $color-active; - } - - &:focus-visible:not(:disabled) { - color: $color-focus; - border-radius: 0; - outline: 2px solid $color-focus; - outline-offset: 1px; - - .tedi-btn__text { - text-decoration: underline; - } - } -} - -.tedi-btn { - --general-icon-primary: currentcolor; - - position: relative; - display: inline-flex; - align-items: center; - justify-content: center; - max-width: 100%; - min-height: $btn-height; - padding-top: calc(var(--_padding-top) - 1px); - padding-bottom: calc(var(--_padding-bottom) - 1px); - margin: 0; - text-align: center; - text-decoration: none; - cursor: pointer; - background-image: none; - border-style: solid; - border-width: 1px; - transition: 150ms ease; - transition-property: background-color, border-color; - - @include mixins.print-grayscale; - @include mixins.responsive-styles(font-family, family-primary, $exclude: tablet); - @include mixins.responsive-styles(font-size, button-text-size-default); - - // subtract border height - @include mixins.responsive-styles(--_padding-top, button-md-padding-y); - @include mixins.responsive-styles(--_padding-bottom, button-md-padding-y); - @include mixins.responsive-styles(padding-left, button-md-padding-x); - @include mixins.responsive-styles(padding-right, button-md-padding-x); - @include mixins.responsive-styles(border-radius, button-radius-default); -} - -.tedi-btn:disabled, -.tedi-btn[aria-disabled='true'] { - cursor: auto; -} - -.tedi-btn--small { - min-height: $btn-height-small; - padding-top: calc(var(--_padding-top) - 1px); - padding-bottom: calc(var(--_padding-bottom) - 1px); - - @include mixins.responsive-styles(font-size, button-text-size-sm); - @include mixins.responsive-styles(--_padding-top, button-sm-padding-y); - @include mixins.responsive-styles(--_padding-bottom, button-sm-padding-y); - @include mixins.responsive-styles(padding-left, button-sm-padding-x); - @include mixins.responsive-styles(padding-right, button-sm-padding-x); - - @include breakpoints.media-breakpoint-down(md) { - height: $btn-height; - } - - &.tedi-btn--icon-only { - width: $btn-height-small; - - @include breakpoints.media-breakpoint-down(md) { - width: $btn-height; - } - } -} - -.tedi-btn--large { - height: $btn-height-large; - padding-top: calc(var(--_padding-top) - 1px); - padding-bottom: calc(var(--_padding-bottom) - 1px); - - @include mixins.responsive-styles(--_padding-top, button-md-padding-y); - @include mixins.responsive-styles(--_padding-bottom, button-md-padding-y); - @include mixins.responsive-styles(padding-left, button-md-padding-x); - @include mixins.responsive-styles(padding-right, button-md-padding-x); - - &.tedi-btn--icon-only { - width: $btn-width-large; - } -} - -.tedi-btn--neutral { - padding: 0; - padding-top: calc(var(--_padding-top) - 1px); - padding-bottom: calc(var(--_padding-bottom) - 1px); - background-clip: padding-box; - - @include mixins.responsive-styles(--_padding-top, button-md-neutral-padding-y); - @include mixins.responsive-styles(--_padding-bottom, button-md-neutral-padding-y); - - &.tedi-btn--default.tedi-btn--icon-only { - &:hover:not(:disabled) { - background-color: var(--button-main-neutral-icon-only-background-hover); - } - - &:active:not(:disabled) { - background-color: var(--button-main-neutral-icon-only-background-active); - } - } - - &.tedi-btn--danger.tedi-btn--icon-only { - &:hover:not(:disabled) { - background-color: var(--button-main-danger-neutral-icon-only-background-hover); - } - - &:active:not(:disabled) { - background-color: var(--button-main-danger-neutral-icon-only-background-active); - } - } -} - -.tedi-btn--icon-only { - width: $btn-height; - padding: 0; - - .tedi-btn__text { - position: absolute !important; - width: 1px !important; - height: 1px !important; - padding: 0 !important; - overflow: hidden; - clip: rect(1px, 1px, 1px, 1px); - border: 0 !important; - - @include mixins.visually-hidden; - } -} - -.tedi-btn--default { - &.tedi-btn--primary { - @include button-variant( - var(--button-main-primary-background-default), - var(--button-main-primary-border-default), - var(--button-main-primary-text-default), - var(--button-main-primary-background-hover), - var(--button-main-primary-border-hover), - var(--button-main-primary-text-default), - var(--button-main-primary-border-focus), - var(--button-main-primary-background-focus), - var(--button-main-primary-text-default), - var(--button-main-primary-border-active), - var(--button-main-primary-background-active) - ); - } - - &.tedi-btn--secondary { - @include button-secondary-variant( - var(--button-main-secondary-border-default), - var(--button-main-secondary-text-default), - var(--button-main-secondary-background-hover), - var(--button-main-secondary-border-hover), - var(--button-main-secondary-text-default), - var(--button-main-secondary-border-focus), - var(--button-main-secondary-background-focus), - var(--button-main-secondary-text-default), - var(--button-main-secondary-border-active), - var(--button-main-secondary-background-active) - ); - } - - &.tedi-btn--neutral { - @include button-variant( - var(--button-main-neutral-background-default), - var(--button-main-neutral-border-default), - var(--button-main-neutral-text-default), - var(--button-main-neutral-background-hover), - var(--button-main-neutral-border-hover), - var(--button-main-neutral-text-hover), - var(--button-main-primary-border-focus), - var(--button-main-neutral-background-focus), - var(--button-main-neutral-text-active), - var(--button-main-neutral-border-active), - var(--button-main-neutral-background-active), - var(--button-main-neutral-background-default), - var(--button-main-neutral-background-default) - ); - } -} - -.tedi-btn--danger { - &.tedi-btn--primary { - @include button-variant( - var(--button-main-danger-background-default), - var(--button-main-danger-border-default), - var(--button-main-danger-text-default), - var(--button-main-danger-background-hover), - var(--button-main-danger-border-hover), - var(--button-main-danger-text-default), - var(--button-main-primary-border-focus), - var(--button-main-danger-background-focus), - var(--button-main-danger-text-default), - var(--button-main-danger-border-active), - var(--button-main-danger-background-active) - ); - } - - &.tedi-btn--neutral { - @include button-variant( - var(--button-main-danger-neutral-background-default), - var(--button-main-danger-neutral-border-default), - var(--button-main-danger-neutral-text-default), - var(--button-main-danger-neutral-background-hover), - var(--button-main-danger-neutral-border-hover), - var(--button-main-danger-neutral-text-hover), - var(--button-main-primary-border-focus), - var(--button-main-danger-neutral-background-focus), - var(--button-main-danger-neutral-text-active), - var(--button-main-danger-neutral-border-active), - var(--button-main-danger-neutral-background-active), - var(--button-main-neutral-background-default), - var(--button-main-neutral-background-default) - ); - } -} - -.tedi-btn--success { - &.tedi-btn--primary { - @include button-variant( - var(--button-main-success-background-default), - var(--button-main-success-border-default), - var(--button-main-success-text-default), - var(--button-main-success-background-hover), - var(--button-main-success-border-hover), - var(--button-main-success-text-default), - var(--button-main-primary-border-focus), - var(--button-main-success-background-focus), - var(--button-main-success-text-default), - var(--button-main-success-border-active), - var(--button-main-success-background-active) - ); - } -} - -.tedi-btn--inverted { - &.tedi-btn--primary { - @include button-variant( - var(--button-main-primary-inverted-background-default), - var(--button-main-primary-inverted-border-default), - var(--button-main-primary-inverted-text-default), - var(--button-main-primary-inverted-background-hover), - var(--button-main-primary-inverted-border-hover), - var(--button-main-primary-inverted-text-hover), - var(--button-main-primary-inverted-border-focus), - var(--button-main-primary-inverted-background-focus), - var(--button-main-primary-inverted-text-default), - var(--button-main-primary-inverted-border-active), - var(--button-main-primary-inverted-background-active) - ); - } - - &.tedi-btn--secondary { - @include button-variant( - var(--button-main-secondary-inverted-background-default), - var(--button-main-secondary-inverted-border-default), - var(--button-main-secondary-inverted-text-default), - var(--button-main-secondary-inverted-background-hover), - var(--button-main-secondary-inverted-border-hover), - var(--button-main-secondary-inverted-text-default), - var(--button-main-secondary-inverted-border-focus), - var(--button-main-secondary-inverted-background-focus), - var(--button-main-secondary-inverted-text-default), - var(--button-main-secondary-inverted-border-active), - var(--button-main-secondary-inverted-background-active) - ); - } - - &.tedi-btn--neutral { - @include button-variant( - var(--button-main-neutral-inverted-background-default), - var(--button-main-neutral-inverted-border-default), - var(--button-main-neutral-inverted-text-default), - var(--button-main-neutral-inverted-background-hover), - var(--button-main-neutral-inverted-border-hover), - var(--button-main-neutral-inverted-text-hover), - var(--button-main-neutral-inverted-border-focus), - var(--button-main-neutral-inverted-background-focus), - var(--button-main-neutral-inverted-text-default), - var(--button-main-neutral-inverted-border-active), - var(--button-main-neutral-inverted-background-active), - var(--button-main-neutral-inverted-background-default), - var(--button-main-neutral-inverted-background-default) - ); - } - - &.tedi-btn--link { - @include link-variant( - var(--link-inverted-default), - var(--link-inverted-hover), - var(--link-inverted-active), - var(--link-inverted-focus) - ); - } -} - -.tedi-btn--text-color { - &.tedi-btn--link { - @include link-variant( - inherit, - var(--button-main-neutral-text-hover), - var(--button-main-disabled-general-text), - inherit - ); - } -} - -.tedi-btn--link { - display: inline-flex; - width: auto; - height: auto; - min-height: auto; - padding: 0; - text-align: left; - text-decoration: none; - background: none; - border: none; - - @include link-variant( - var(--link-primary-default), - var(--link-primary-hover), - var(--link-primary-active), - var(--link-primary-focus) - ); - - .tedi-btn__icon--left { - @include mixins.responsive-styles(margin-right, button-sm-inner-spacing); - } - - .tedi-btn__icon--right { - @include mixins.responsive-styles(margin-left, button-sm-inner-spacing); - } -} - -.tedi-btn__text { - @include mixins.responsive-styles(padding-left, button-md-inner-spacing); - @include mixins.responsive-styles(padding-right, button-md-inner-spacing); - - .tedi-btn--link.tedi-btn--is-hovered:not(:disabled) &, - .tedi-btn--link.tedi-btn--is-active:not(:disabled) &, - .tedi-btn--link:hover:not(:disabled) &, - .tedi-btn--link:active:not(:disabled) & { - text-decoration: underline; - } - - .tedi-btn--is-loading:not(.tedi-btn--icon) & { - color: var(--alpha-01); - } - - .tedi-btn--small & { - @include mixins.responsive-styles(padding-left, button-sm-inner-spacing); - @include mixins.responsive-styles(padding-right, button-sm-inner-spacing); - } - - .tedi-btn--link & { - padding: 0; - } - - &:not(.tedi-btn--link .tedi-btn__text) { - text-wrap: nowrap; - - @include breakpoints.media-breakpoint-up(md) { - display: -webkit-box; - overflow: hidden; - text-overflow: ellipsis; - text-wrap: wrap; - word-break: break-word; - -webkit-line-clamp: 2; - line-clamp: 2; - -webkit-box-orient: vertical; - } - } -} - -.tedi-btn__icon--left, -.tedi-btn__icon--right { - display: inline-flex; - flex-shrink: 0; - padding: 0; -} - -.tedi-btn__icon { - display: flex; - align-items: center; - justify-content: center; - - .tedi-btn--link & { - vertical-align: text-bottom; - } -} - -.tedi-btn__inner { - display: flex; - align-items: center; - justify-content: center; - min-width: 1.5rem; - min-height: 1.5rem; - - .tedi-btn--link.tedi-btn__icon-standalone--link:not(.tedi-btn--is-loading) & { - display: inline-flex; - align-items: flex-start; - - .tedi-btn__icon { - @include mixins.responsive-styles(line-height, body-regular-line-height); - } - } - - .tedi-btn--link:not(.tedi-btn--is-loading) & { - display: inline; - } -} - -.tedi-btn__spinner { - stroke: currentcolor; -} - -.tedi-btn--underline { - .tedi-btn__text { - text-decoration: underline; - } -} - -.tedi-btn--no-style { - @include mixins.button-reset; -} - -.tedi-btn--full-width { - width: 100%; -} diff --git a/libs/react-components/src/tedi/components/buttons/button-content/button-content.spec.tsx b/libs/react-components/src/tedi/components/buttons/button-content/button-content.spec.tsx deleted file mode 100644 index 6d10c836e..000000000 --- a/libs/react-components/src/tedi/components/buttons/button-content/button-content.spec.tsx +++ /dev/null @@ -1,81 +0,0 @@ -import { fireEvent, render, screen } from '@testing-library/react'; - -import ButtonContent, { ButtonContentProps } from './button-content'; - -import '@testing-library/jest-dom'; - -describe('ButtonContent component', () => { - // eslint-disable-next-line @typescript-eslint/ban-types - const defaultProps: ButtonContentProps<'button', {}, {}> = { - children: 'Click Me', - }; - - it('renders button with default styles and children', () => { - render(); - const button = screen.getByRole('button', { name: /click me/i }); - expect(button).toBeInTheDocument(); - expect(button).toHaveClass('tedi-btn tedi-btn--primary tedi-btn--default'); - }); - - it('applies custom class names', () => { - render(); - const button = screen.getByRole('button'); - expect(button).toHaveClass('custom-class'); - }); - - it('renders with icon only', () => { - render(); - const icon = screen.getByRole('img', { hidden: true }); - expect(icon).toBeInTheDocument(); - expect(icon).toHaveClass('tedi-btn__icon tedi-btn__icon--center'); - }); - - it('renders with left icon', () => { - render(); - const leftIcon = screen.getByRole('img', { hidden: true }); - expect(leftIcon).toBeInTheDocument(); - expect(leftIcon).toHaveClass('tedi-btn__icon tedi-btn__icon--left'); - }); - - it('renders with right icon', () => { - render(); - const rightIcon = screen.getByRole('img', { hidden: true }); - expect(rightIcon).toBeInTheDocument(); - expect(rightIcon).toHaveClass('tedi-btn__icon tedi-btn__icon--right'); - }); - - it('renders underline when underline prop is true', () => { - render(); - const button = screen.getByRole('button'); - expect(button).toHaveClass('tedi-btn--underline'); - }); - - it('renders in loading state with spinner', () => { - render(); - const spinner = screen.getByRole('status'); - expect(spinner).toBeInTheDocument(); - expect(spinner).toHaveClass('tedi-btn__spinner'); - }); - - it('renders with full width when fullWidth is true', () => { - render(); - const button = screen.getByRole('button'); - expect(button).toHaveClass('tedi-btn--full-width'); - }); - - it('does not trigger onClick when isLoading is true', () => { - const handleClick = jest.fn(); - render(); - const button = screen.getByRole('button'); - fireEvent.click(button); - expect(handleClick).not.toHaveBeenCalled(); - }); - - it('triggers onClick when clicked and not loading', () => { - const handleClick = jest.fn(); - render(); - const button = screen.getByRole('button'); - fireEvent.click(button); - expect(handleClick).toHaveBeenCalledTimes(1); - }); -}); diff --git a/libs/react-components/src/tedi/components/buttons/button-content/button-content.tsx b/libs/react-components/src/tedi/components/buttons/button-content/button-content.tsx deleted file mode 100644 index 7a6fa1db1..000000000 --- a/libs/react-components/src/tedi/components/buttons/button-content/button-content.tsx +++ /dev/null @@ -1,209 +0,0 @@ -import cn from 'classnames'; -import React, { forwardRef } from 'react'; - -import { AllowedHTMLTags, PolymorphicComponentPropWithRef, PolymorphicRef } from '../../../helpers/polymorphic/types'; -import { UnknownType } from '../../../types/commonTypes'; -import { Icon, IconWithoutBackgroundProps } from '../../base/icon/icon'; -import { Spinner } from '../../loaders/spinner/spinner'; -import { Print } from '../../misc/print/print'; -import { ButtonColor, ButtonType } from '../button/button'; -import styles from './button-content.module.scss'; - -export type ButtonContentProps< - C extends React.ElementType, - P extends Record, - A -> = PolymorphicComponentPropWithRef< - AllowedHTMLTags, - { - /** - * Button children - */ - children: React.ReactNode; - /** - * Additional custom class name. - */ - className?: string; - /** - * Button visual type - * @default primary - */ - visualType?: ButtonType; - /** - * If button should take all the space it has - */ - fullWidth?: boolean; - /** - * Color schema for button. PS text-color works only with link type links. - * @default default - */ - color?: ButtonColor; - /** - * Button size - */ - size?: 'default' | 'small' | 'large'; - /** - * Name of the icon when button only has an icon in it. - */ - icon?: string | IconWithoutBackgroundProps; - /** - * Name of the icon we want to show on the left. - */ - iconLeft?: string | IconWithoutBackgroundProps; - /** - * Name of the icon we want to show on the right. - */ - iconRight?: string | IconWithoutBackgroundProps; - /** - * Underline the button text - */ - underline?: boolean; - /** - * If button is active and should keep its hover state. - */ - isHovered?: boolean; - /** - * If button is active and should keep it's active state. - */ - isActive?: boolean; - /** - * If button is in loading state and should show spinner. - * When isLoading is true, button does not trigger onClick event. - * @default false - */ - isLoading?: boolean; - /** - * Skip applying button/link styles - * Useful when you just want to use Button or Link logic without the styles - * In this case icon, iconLeft and iconRight are ignored - */ - noStyle?: boolean; - /** - * - */ - renderWrapperElement?: unknown; - } & P ->; - -export type ButtonContentComponent = , A>( - props: ButtonContentProps -) => React.ReactNode; - -const InternalButtonContent = forwardRef( - , A>( - { - children, - as, - text, - className, - visualType = 'primary', - color = 'default', - size, - icon, - iconLeft, - iconRight, - underline = false, - isHovered, - isActive, - isLoading = false, - noStyle, - renderWrapperElement, - fullWidth, - onClick, - ...rest - }: ButtonContentProps, - ref?: PolymorphicRef - ) => { - const Component = as || 'button'; - const hasIcon = icon || iconLeft || iconRight; - - const BEM = !noStyle - ? cn( - styles['tedi-btn'], - styles[`tedi-btn--${visualType}`], - styles[`tedi-btn--${color}`], - { [styles[`tedi-btn--${size}`]]: size }, - { [styles['tedi-btn--underline']]: underline }, - { [styles['tedi-btn--is-hovered']]: isHovered }, - { [styles['tedi-btn--is-active']]: isActive }, - { [styles['tedi-btn--is-loading']]: isLoading }, - { [styles['tedi-btn--icon-only']]: icon }, - { [styles['tedi-btn--icon']]: hasIcon }, - { [styles['tedi-btn--full-width']]: fullWidth }, - className - ) - : cn(styles['tedi-btn--no-style'], { [styles['tedi-btn--full-width']]: fullWidth }, className); - - const getIcon = (location: string, icon: string | IconWithoutBackgroundProps): JSX.Element => { - const iconBEM = cn(styles['tedi-btn__icon'], styles[`tedi-btn__icon--${location}`], { - [styles['tedi-btn__spinner']]: isLoading, - }); - - const isLink = visualType === 'link'; - - const defaultIconProps: Partial = { - size: size === 'large' ? 24 : 18, - className: iconBEM, - ...(isLink ? { display: 'inline' } : {}), - }; - - const iconProps: IconWithoutBackgroundProps = - typeof icon === 'string' - ? { ...defaultIconProps, name: icon } - : { ...defaultIconProps, ...icon, className: cn(defaultIconProps.className, icon?.className) }; - - return isLoading ? : ; - }; - - const renderContent = (): JSX.Element => { - const leftContent = isLoading ? ( - - ) : iconLeft ? ( - getIcon('left', iconLeft) - ) : icon ? ( - getIcon('center', icon) - ) : null; - - return ( - - {leftContent} - {children} - {!isLoading && iconRight && getIcon('right', iconRight)} - - ); - }; - - const onClickHandler = (event: React.MouseEvent): void => { - if (onClick && !isLoading) onClick(event); - }; - - return ( - - - {!noStyle ? renderContent() : children} - - - ); - } -); - -InternalButtonContent.displayName = 'ButtonContent'; - -/** - * Shares the rendering logic between `Link` and `Button`. We don't export it from the component library. - */ -export const ButtonContent: ButtonContentComponent = InternalButtonContent; - -export default ButtonContent; diff --git a/libs/react-components/src/tedi/components/buttons/button-group/button-group.module.scss b/libs/react-components/src/tedi/components/buttons/button-group/button-group.module.scss deleted file mode 100644 index 9317067c5..000000000 --- a/libs/react-components/src/tedi/components/buttons/button-group/button-group.module.scss +++ /dev/null @@ -1,111 +0,0 @@ -@use '@tehik-ee/tedi-core/mixins'; -@use '@tehik-ee/tedi-core/bootstrap-utility/breakpoints'; - -.tedi-button-group { - display: flex; - gap: 0; - - &--primary, - &--secondary { - .tedi-button-group__item { - display: flex; - align-items: center; - justify-content: center; - height: auto; - min-height: 2.5rem; - padding-top: calc(var(--_padding-top) - 1px); - padding-bottom: calc(var(--_padding-bottom) - 1px); - text-align: center; - cursor: pointer; - border-radius: 0; - - @include mixins.responsive-styles(--_padding-top, button-md-padding-y); - @include mixins.responsive-styles(--_padding-bottom, button-md-padding-y); - @include mixins.responsive-styles(padding-left, button-md-padding-x); - @include mixins.responsive-styles(padding-right, button-md-padding-x); - - &:first-child { - @include mixins.responsive-styles(border-top-left-radius, button-radius-sm); - @include mixins.responsive-styles(border-bottom-left-radius, button-radius-sm); - } - - &:not(:first-child) { - margin-left: -1px; - } - - &:last-child { - @include mixins.responsive-styles(border-top-right-radius, button-radius-sm); - @include mixins.responsive-styles(border-bottom-right-radius, button-radius-sm); - } - - &:focus-visible { - z-index: 1; - } - - @include breakpoints.media-breakpoint-down(md) { - flex: 1; - } - } - } - - &--primary { - .tedi-button-group__item { - color: var(--button-group-primary-inactive-text); - background-color: var(--button-group-primary-inactive-background); - border: 1px solid var(--button-group-primary-inactive-border); - - &--disabled:disabled { - color: var(--button-group-primary-disabled-text); - background-color: var(--button-group-primary-disabled-background); - border: 1px solid var(--button-group-primary-disabled-border); - } - - &:hover:not(:disabled) { - color: var(--button-group-primary-hover-text); - background-color: var(--button-group-primary-hover-background); - border-color: var(--button-group-primary-hover-border); - } - - &.tedi-button-group__item--active:not(:disabled) { - z-index: 1; - color: var(--button-group-primary-selected-text); - background-color: var(--button-group-primary-selected-background); - border-color: var(--button-group-primary-selected-border); - } - } - } - - &--secondary { - .tedi-button-group__item { - color: var(--button-group-secondary-inactive-text); - background-color: var(--button-group-secondary-inactive-background); - border: 1px solid var(--button-group-secondary-inactive-border); - - &--disabled:disabled { - color: var(--button-group-secondary-disabled-text); - background-color: var(--button-group-secondary-disabled-background); - border: 1px solid var(--button-group-secondary-disabled-border); - } - - &:hover:not(:disabled) { - color: var(--button-group-secondary-hover-text); - background-color: var(--button-group-secondary-hover-background); - border-color: var(--button-group-secondary-hover-border); - } - - &.tedi-button-group__item--active:not(:disabled) { - z-index: 1; - color: var(--button-group-secondary-selected-text); - background-color: var(--button-group-secondary-selected-background); - outline: 2px solid var(--button-group-secondary-selected-border); - outline-offset: -2px; - } - } - } - - &--stretch { - .tedi-button-group__item { - flex: 1; - } - } -} diff --git a/libs/react-components/src/tedi/components/buttons/button-group/button-group.spec.tsx b/libs/react-components/src/tedi/components/buttons/button-group/button-group.spec.tsx deleted file mode 100644 index 592173b60..000000000 --- a/libs/react-components/src/tedi/components/buttons/button-group/button-group.spec.tsx +++ /dev/null @@ -1,138 +0,0 @@ -import { fireEvent, render, screen } from '@testing-library/react'; -import React from 'react'; - -import Button from '../button/button'; -import { ButtonGroup } from './button-group'; - -describe('ButtonGroup Component', () => { - it('renders child buttons correctly', () => { - render( - - - - - ); - const buttons = screen.getAllByRole('button'); - expect(buttons).toHaveLength(2); - expect(buttons[0]).toHaveTextContent('Button 1'); - expect(buttons[1]).toHaveTextContent('Button 2'); - }); - - it('applies the stretch class when the stretch prop is true', () => { - const { container } = render( - - - - ); - expect(container.firstChild).toHaveClass('tedi-button-group--stretch'); - }); - - it('does not apply the stretch class when the stretch prop is false', () => { - const { container } = render( - - - - ); - expect(container.firstChild).not.toHaveClass('tedi-button-group--stretch'); - }); - - it('triggers onSelectionChange when a button is clicked', () => { - const onSelectionChange = jest.fn(); - render( - - - - - ); - const button1 = screen.getByText('Button 1'); - fireEvent.click(button1); - expect(onSelectionChange).toHaveBeenCalledWith('button1'); - }); - - it('does not trigger onSelectionChange for a disabled button', () => { - const onSelectionChange = jest.fn(); - render( - - - - - ); - const button1 = screen.getByText('Button 1'); - fireEvent.click(button1); - expect(onSelectionChange).not.toHaveBeenCalled(); - }); - - it('applies custom className correctly', () => { - const { container } = render( - - - - ); - expect(container.firstChild).toHaveClass('custom-class'); - }); - - it('renders with the correct type class based on the type prop', () => { - const { container } = render( - - - - ); - expect(container.firstChild).toHaveClass('tedi-button-group--secondary'); - }); - - it('renders with the correct aria-label when provided', () => { - render( - - - - ); - const group = screen.getByRole('group'); - expect(group).toHaveAttribute('aria-label', 'Test Button Group'); - }); - - it('renders active class on a button when isActive is true', () => { - const { container } = render( - - - - - ); - const activeButton = container.querySelector('.tedi-button-group__item--active'); - expect(activeButton).toBeInTheDocument(); - expect(activeButton).toHaveTextContent('Button 1'); - }); - - it('updates state correctly when a button is clicked', () => { - const TestComponent = () => { - const [selected, setSelected] = React.useState(null); - - return ( - - - - - ); - }; - - render(); - - const button1 = screen.getByTestId('button1'); - const button2 = screen.getByTestId('button2'); - - fireEvent.click(button1); - expect(button1).toHaveClass('tedi-button-group__item--active'); - expect(button2).not.toHaveClass('tedi-button-group__item--active'); - - fireEvent.click(button2); - expect(button2).toHaveClass('tedi-button-group__item--active'); - expect(button1).not.toHaveClass('tedi-button-group__item--active'); - }); -}); diff --git a/libs/react-components/src/tedi/components/buttons/button-group/button-group.stories.tsx b/libs/react-components/src/tedi/components/buttons/button-group/button-group.stories.tsx deleted file mode 100644 index 921f320dd..000000000 --- a/libs/react-components/src/tedi/components/buttons/button-group/button-group.stories.tsx +++ /dev/null @@ -1,184 +0,0 @@ -import { Meta, StoryFn, StoryObj } from '@storybook/react'; -import { useState } from 'react'; - -import { Col, Row } from '../../layout/grid'; -import { VerticalSpacing } from '../../layout/vertical-spacing'; -import { Button } from '../button/button'; -import ButtonGroup, { ButtonGroupProps } from './button-group'; - -/** - * Figma ↗
- * ZeroHeight ↗ - */ - -const meta: Meta = { - title: 'TEDI-Ready/Components/Buttons/ButtonGroup', - component: ButtonGroup, - parameters: { - status: { - type: 'partiallyTediReady', - }, - }, -}; - -export default meta; -type Story = StoryObj; - -const buttonStates = ['Default', 'Hover', 'Active', 'Disabled']; - -const Template: StoryFn = (args) => ( - - - - - -); - -const TemplateTypes: StoryFn = (args) => { - return ( - - - - - - - - - - - - - ); -}; - -export const Default: Story = { - render: Template, - args: { - type: 'primary', - stretch: false, - }, -}; - -export const Types: StoryObj = { - render: TemplateTypes, -}; - -export const WithIcon: StoryObj = { - render: TemplateTypes, - args: { iconLeft: 'table' }, -}; - -export const IconOnly: StoryObj = { - render: TemplateTypes, - args: { icon: 'table' }, -}; - -const TemplateColumn: StoryFn<{ states: string[]; type: 'primary' | 'secondary' }> = (args) => { - const [selectedId, setSelectedId] = useState(null); - - return ( - - {args.states.map((state, index) => ( - - - {state} - - - - - - - - - - ))} - - ); -}; - -export const Primary: StoryObj<{ states: string[] }> = { - render: (args) => , - args: { - states: buttonStates, - }, - parameters: { - pseudo: { - hover: ['#Hover-primary'], - active: ['#Active-primary'], - focus: ['#Focus-primary'], - }, - }, -}; - -export const Secondary: StoryObj<{ states: string[] }> = { - render: (args) => , - args: { - states: buttonStates, - }, - parameters: { - pseudo: { - hover: ['#Hover-secondary'], - active: ['#Active-secondary'], - focus: ['#Focus-secondary'], - }, - }, -}; - -export const DifferentWidthButtons: Story = { - render: (args) => { - const [selectedId, setSelectedId] = useState(null); - - return ( - - - - - - - - - - ); - }, - args: { - type: 'primary', - }, -}; - -export const Stretched: Story = { - render: Template, - args: { - stretch: true, - }, -}; diff --git a/libs/react-components/src/tedi/components/buttons/button-group/button-group.tsx b/libs/react-components/src/tedi/components/buttons/button-group/button-group.tsx deleted file mode 100644 index b2e0ecbaa..000000000 --- a/libs/react-components/src/tedi/components/buttons/button-group/button-group.tsx +++ /dev/null @@ -1,79 +0,0 @@ -import cn from 'classnames'; -import { Children, cloneElement, isValidElement, ReactNode } from 'react'; - -import Button, { ButtonProps } from '../button/button'; -import styles from './button-group.module.scss'; - -export type ButtonGroupProps = { - /** - * The child components to render inside the ButtonGroup. - * Typically, these should be ` - - - - - - - - - - - - ))} - - - ); -}; - -export const Primary: StoryObj = { - render: TemplateColumn, - args: { - array: buttonStateArray, - visualType: 'primary', - }, - parameters: { - pseudo: { - hover: '#Hover', - active: '#Active', - focus: '#Focus', - }, - }, -}; - -export const PrimaryInverted: StoryObj = { - render: TemplateColumn, - args: { - array: buttonStateArray, - visualType: 'primary', - color: 'inverted', - titleColor: 'white', - }, - parameters: { - pseudo: { - hover: '#Hover', - active: '#Active', - focus: '#Focus', - }, - backgrounds: { default: 'brand' }, - }, -}; - -export const Secondary: StoryObj = { - render: TemplateColumn, - args: { - array: buttonStateArray, - visualType: 'secondary', - }, - parameters: { - pseudo: { - hover: '#Hover', - active: '#Active', - focus: '#Focus', - }, - }, -}; - -export const SecondaryInverted: StoryObj = { - render: TemplateColumn, - args: { - array: buttonStateArray, - visualType: 'secondary', - color: 'inverted', - titleColor: 'white', - }, - parameters: { - pseudo: { - hover: '#Hover', - active: '#Active', - focus: '#Focus', - }, - backgrounds: { default: 'brand' }, - }, -}; - -export const Neutral: StoryObj = { - render: TemplateColumn, - args: { - array: buttonStateArray, - visualType: 'neutral', - }, - parameters: { - pseudo: { - hover: '#Hover', - active: '#Active', - focus: '#Focus', - }, - }, -}; - -export const NeutralInverted: StoryObj = { - render: TemplateColumn, - args: { - array: buttonStateArray, - visualType: 'neutral', - color: 'inverted', - titleColor: 'white', - }, - parameters: { - pseudo: { - hover: '#Hover', - active: '#Active', - focus: '#Focus', - }, - backgrounds: { default: 'brand' }, - }, -}; - -export const Success: StoryObj = { - render: TemplateColumn, - args: { - array: buttonStateArray, - color: 'success', - }, - parameters: { - pseudo: { - hover: '#Hover', - active: '#Active', - focus: '#Focus', - }, - }, -}; - -export const Danger: StoryObj = { - render: TemplateColumn, - args: { - array: buttonStateArray, - color: 'danger', - }, - parameters: { - pseudo: { - hover: '#Hover', - active: '#Active', - focus: '#Focus', - }, - }, -}; - -export const DangerNeutral: StoryObj = { - render: TemplateColumn, - args: { - array: buttonStateArray, - color: 'danger', - visualType: 'neutral', - }, - parameters: { - pseudo: { - hover: '#Hover', - active: '#Active', - focus: '#Focus', - }, - }, -}; - -export const NoStyleTemplate: StoryFn = (args) => { - return ( - - ); -}; - -export const FullWidth: Story = { - args: { - fullWidth: true, - children: 'Button', - }, -}; - -const ResponsiveTemplate: StoryFn> = (args) => ( - - - - - - - - -); - -export const ResponsiveButton: Story = { - render: ResponsiveTemplate, - args: { - children: 'Responsive Button', - }, - parameters: { - viewport: { - defaultViewport: 'responsive', - }, - }, -}; - -export const LongTextButtonThatWrapsIntoMultipleLines: Story = { - args: { - children: 'This is a very long button text that should wrap into multiple lines', - }, - - render: (args) => ( - - - - - Please avoid long text. This is fallback for emergencies — use only with caution. - - - - - - - - - - - - - - - - ), -}; diff --git a/libs/react-components/src/tedi/components/buttons/button/button.tsx b/libs/react-components/src/tedi/components/buttons/button/button.tsx deleted file mode 100644 index 116667e93..000000000 --- a/libs/react-components/src/tedi/components/buttons/button/button.tsx +++ /dev/null @@ -1,65 +0,0 @@ -import React, { forwardRef } from 'react'; - -import { BreakpointSupport, useBreakpointProps } from '../../../helpers'; -import { PolymorphicRef } from '../../../helpers/polymorphic/types'; -import { UnknownType } from '../../../types/commonTypes'; -import ButtonContent, { ButtonContentProps } from '../button-content/button-content'; - -export type ButtonType = 'primary' | 'secondary' | 'neutral' | 'link'; -export type ButtonColor = 'default' | 'danger' | 'success' | 'inverted' | 'text'; - -interface IInternalButtonProps { - /** - * Button type - * @default button - */ - type?: 'submit' | 'button' | 'reset'; - /** - * Skips form's browser validation - * @default true when type="submit" - */ - formNoValidate?: boolean; -} - -type AllowedTags = 'button'; - -export type ButtonProps = BreakpointSupport< - ButtonContentProps ->; - -const ButtonComponent = forwardRef( - (props: ButtonProps, ref?: PolymorphicRef) => { - const { getCurrentBreakpointProps } = useBreakpointProps(props.defaultServerBreakpoint); - - const { - children, - as, - type = 'button', - formNoValidate = type === 'submit' ? true : undefined, - ...rest - } = getCurrentBreakpointProps>(props); - - const ComponentAs = as || 'button'; - - return ( - - {children} - - ); - } -); - -ButtonComponent.displayName = 'Button'; - -export const Button = ButtonComponent as ( - props: ButtonProps -) => React.ReactElement | null; - -export default Button; diff --git a/libs/react-components/src/tedi/components/buttons/closing-button/closing-button.module.scss b/libs/react-components/src/tedi/components/buttons/closing-button/closing-button.module.scss deleted file mode 100644 index 28c6d8217..000000000 --- a/libs/react-components/src/tedi/components/buttons/closing-button/closing-button.module.scss +++ /dev/null @@ -1,39 +0,0 @@ -@use '@tehik-ee/tedi-core/mixins'; - -.tedi-closing-button { - --general-icon-primary: var(--button-close-text-default); - - display: flex; - align-items: center; - justify-content: center; - padding: 0; - cursor: pointer; - background-color: var(--button-close-background-default); - border: 1px solid var(--button-close-background-default); - transition: background 0.5s ease; - - @include mixins.responsive-styles(border-radius, button-radius-default); - - @each $state in hover, active { - &:#{ $state } { - --general-icon-primary: var(--button-close-text-#{$state}); - - background-color: var(--button-close-background-#{$state}); - } - } - - &:focus-visible { - outline: 2px solid var(--button-main-primary-border-focus); - outline-offset: -2px; - } - - &--medium { - @include mixins.responsive-styles(width, button-xs-icon-size); - @include mixins.responsive-styles(height, button-xs-icon-size); - } - - &--large { - @include mixins.responsive-styles(width, button-sm-icon-size); - @include mixins.responsive-styles(height, button-sm-icon-size); - } -} diff --git a/libs/react-components/src/tedi/components/buttons/closing-button/closing-button.spec.tsx b/libs/react-components/src/tedi/components/buttons/closing-button/closing-button.spec.tsx deleted file mode 100644 index 8bc45184d..000000000 --- a/libs/react-components/src/tedi/components/buttons/closing-button/closing-button.spec.tsx +++ /dev/null @@ -1,81 +0,0 @@ -import { fireEvent, render, screen } from '@testing-library/react'; - -import { ClosingButton } from './closing-button'; - -jest.mock('../../../providers/label-provider', () => ({ - useLabels: () => ({ - getLabel: jest.fn().mockReturnValue('Close'), - }), -})); - -describe('ClosingButton component', () => { - it('renders the ClosingButton with default props', () => { - render( - { - console.log('Button pressed'); - }} - /> - ); - - const button = screen.getByRole('button', { name: /close/i }); - expect(button).toBeInTheDocument(); - expect(button).toHaveClass('tedi-closing-button'); - - const icon = button.querySelector('span[data-name="icon"]'); - expect(icon).toBeInTheDocument(); - expect(icon).toHaveClass('tedi-icon--size-18'); - }); - - it('renders with the correct large size class and icon size', () => { - render( - { - console.log('Button pressed'); - }} - /> - ); - - const button = screen.getByRole('button', { name: /close/i }); - expect(button).toBeInTheDocument(); - expect(button).toHaveClass('tedi-closing-button tedi-closing-button--large'); - - const icon = button.querySelector('span[data-name="icon"]'); - expect(icon).toBeInTheDocument(); - expect(icon).toHaveClass('tedi-icon--size-24'); - }); - - it('applies custom class names', () => { - render(); - - const button = screen.getByRole('button', { name: /Close/i }); - expect(button).toHaveClass('tedi-closing-button tedi-closing-button--large custom-class'); - }); - - it('triggers onClick handler when clicked', () => { - const onClickMock = jest.fn(); - render(); - - const button = screen.getByRole('button', { name: /Close/i }); - fireEvent.click(button); - expect(onClickMock).toHaveBeenCalledTimes(1); - }); - - it('uses fallback label from label provider when title is not provided', () => { - render(); - - const button = screen.getByRole('button', { name: /Close/i }); - expect(button).toHaveAttribute('title', 'Close'); - expect(button).toHaveAttribute('aria-label', 'Close'); - }); - - it('uses custom title if provided', () => { - render(); - - const button = screen.getByRole('button', { name: /Custom Close/i }); - expect(button).toHaveAttribute('title', 'Custom Close'); - expect(button).toHaveAttribute('aria-label', 'Custom Close'); - }); -}); diff --git a/libs/react-components/src/tedi/components/buttons/closing-button/closing-button.stories.tsx b/libs/react-components/src/tedi/components/buttons/closing-button/closing-button.stories.tsx deleted file mode 100644 index b6104a7d6..000000000 --- a/libs/react-components/src/tedi/components/buttons/closing-button/closing-button.stories.tsx +++ /dev/null @@ -1,81 +0,0 @@ -import { Meta, StoryFn, StoryObj } from '@storybook/react'; - -import { Col, Row } from '../../layout/grid'; -import ClosingButton, { ClosingButtonProps } from './closing-button'; - -/** - * Figma ↗
- * Zeroheight ↗ - */ - -const meta: Meta = { - component: ClosingButton, - title: 'Tedi-Ready/Components/Buttons/ClosingButton', - parameters: { - design: { - type: 'figma', - url: 'https://www.figma.com/design/jWiRIXhHRxwVdMSimKX2FF/TEDI-READY-(work-in-progress)?node-id=4514-63815&m=dev', - }, - }, -}; - -const sizeArray: ClosingButtonProps['size'][] = ['medium', 'large']; - -export default meta; -type Story = StoryObj; - -const SizeTemplate: StoryFn = () => { - return ( -
- {sizeArray.map((size, key) => ( - - {size} - - alert(`${size} button clicked`)} /> - - - ))} -
- ); -}; - -const stateArray = ['Default', 'Hover', 'Active', 'Focus']; - -const StatesTemplate: StoryFn = () => { - return ( -
- {stateArray.map((state) => ( - - - {state} - - - - - - ))} -
- ); -}; - -export const Default: Story = { - args: { - title: 'close', - size: 'medium', - }, -}; - -export const Size: Story = { - render: SizeTemplate, -}; - -export const States: Story = { - render: StatesTemplate, - parameters: { - pseudo: { - hover: '#Hover', - active: '#Active', - focusVisible: '#Focus', - }, - }, -}; diff --git a/libs/react-components/src/tedi/components/buttons/closing-button/closing-button.tsx b/libs/react-components/src/tedi/components/buttons/closing-button/closing-button.tsx deleted file mode 100644 index b718964f6..000000000 --- a/libs/react-components/src/tedi/components/buttons/closing-button/closing-button.tsx +++ /dev/null @@ -1,61 +0,0 @@ -import cn from 'classnames'; -import { MouseEventHandler } from 'react'; -import { ButtonHTMLAttributes } from 'react'; - -import { useLabels } from '../../../providers/label-provider'; -import { Icon } from '../../base/icon/icon'; -import styles from './closing-button.module.scss'; - -type ClosingButtonSize = 'medium' | 'large'; - -export interface ClosingButtonProps extends ButtonHTMLAttributes { - /** - * Additional classes to apply custom styles to the ClosingButton. - */ - className?: string; - /** - * Size of the ClosingButton - * @default 'medium' - */ - size?: ClosingButtonSize; - /** - * Event handler for the button click event. Triggered when the user clicks on the close button. - */ - onClick?: MouseEventHandler; - /* - * Title for the button. - * Used for accessibility and as a tooltip on hover. If not provided, the label provider's 'close' label will be used as a fallback. - */ - title?: string; -} - -export const ClosingButton = (props: ClosingButtonProps): JSX.Element => { - const { getLabel } = useLabels(); - const { title = getLabel('close'), onClick, size = 'medium', className, ...rest } = props; - - const buttonClass = cn( - styles['tedi-closing-button'], - { - [styles[`tedi-closing-button--${size}`]]: size, - }, - className - ); - - const iconSize = size === 'large' ? 24 : 18; - - return ( - - ); -}; - -export default ClosingButton; diff --git a/libs/react-components/src/tedi/components/buttons/collapse/collapse.module.scss b/libs/react-components/src/tedi/components/buttons/collapse/collapse.module.scss deleted file mode 100644 index 163c1ea15..000000000 --- a/libs/react-components/src/tedi/components/buttons/collapse/collapse.module.scss +++ /dev/null @@ -1,39 +0,0 @@ -@use '@tehik-ee/tedi-core/mixins'; - -.tedi-collapse { - &__title { - width: 100%; - text-align: left; - cursor: pointer; - - @include mixins.button-reset; - } - - &__text { - text-decoration: underline; - } - - &__icon-wrapper { - display: flex; - align-items: center; - justify-content: center; - border: var(--borders-01) solid var(--button-main-secondary-border-default); - border-radius: 100%; - - @include mixins.responsive-styles(width, button-sm-icon-size); - @include mixins.responsive-styles(height, button-sm-icon-size); - } - - &__icon { - color: var(--button-main-neutral-text-default); - transition: transform 300ms ease; - - .tedi-collapse--is-open & { - transform: rotate(180deg); - } - } - - &__content { - margin-top: var(--dimensions-05); - } -} diff --git a/libs/react-components/src/tedi/components/buttons/collapse/collapse.spec.tsx b/libs/react-components/src/tedi/components/buttons/collapse/collapse.spec.tsx deleted file mode 100644 index 8f9c1cb84..000000000 --- a/libs/react-components/src/tedi/components/buttons/collapse/collapse.spec.tsx +++ /dev/null @@ -1,97 +0,0 @@ -import { fireEvent, render, screen } from '@testing-library/react'; - -import { useBreakpointProps, usePrint } from '../../../helpers'; -import { Heading } from '../../base/typography/heading/heading'; -import Collapse, { CollapseProps } from './collapse'; - -import '@testing-library/jest-dom'; - -jest.mock('../../../helpers', () => ({ - useBreakpointProps: jest.fn(), - usePrint: jest.fn(), -})); - -const getComponent = (props?: Partial) => - render( - Heading} - openText="Näita rohkem" - closeText="Näita vähem" - {...props} - > - Collapse content - - ); - -describe('Collapse component with breakpoint support', () => { - (useBreakpointProps as jest.Mock).mockImplementation(() => ({ - getCurrentBreakpointProps: jest.fn((props) => ({ - ...props, - })), - })); - - (usePrint as jest.Mock).mockReturnValue(false); - - it('renders successfully', () => { - const { baseElement } = getComponent(); - expect(baseElement).toBeTruthy(); - }); - - it('applies custom className to root', () => { - const { container } = getComponent({ className: 'test-class' }); - const root = container.querySelector('.test-class'); - expect(root).toBeInTheDocument(); - expect(root?.getAttribute('data-name')).toBe('collapse'); - }); - - it('is collapsed by default (uncontrolled)', () => { - const { getByTestId } = getComponent(); - const content = getByTestId('collapse-inner'); - expect(content).toHaveStyle('height: 0px'); - }); - - it('expands when clicked (uncontrolled)', () => { - const { getByTestId } = getComponent(); - const content = getByTestId('collapse-inner'); - const button = screen.getByRole('button', { name: /näita rohkem/i }); - fireEvent.click(button); - expect(content).toHaveStyle('height: 0px'); - }); - - it('can be controlled externally', () => { - const { getByTestId, rerender } = render( - Controlled}> - Controlled content - - ); - - const content = getByTestId('collapse-inner'); - expect(content).toHaveStyle('height: auto'); - - rerender( - Controlled}> - Controlled content - - ); - - expect(content).toHaveStyle('height: 0px'); - }); - - it('hides collapse text when hideCollapseText is true', () => { - render( - Toggle}> - Content - - ); - - const visuallyHiddenCol = screen.getByText(/open/i).parentElement; - expect(visuallyHiddenCol).toHaveClass('visually-hidden'); - }); - - it('renders secondary arrow wrapper when arrowType is "secondary"', () => { - const { container } = getComponent({ arrowType: 'secondary' }); - const wrapper = container.querySelector('.tedi-collapse__icon-wrapper'); - expect(wrapper).toBeInTheDocument(); - }); -}); diff --git a/libs/react-components/src/tedi/components/buttons/collapse/collapse.stories.tsx b/libs/react-components/src/tedi/components/buttons/collapse/collapse.stories.tsx deleted file mode 100644 index 9a14fc495..000000000 --- a/libs/react-components/src/tedi/components/buttons/collapse/collapse.stories.tsx +++ /dev/null @@ -1,77 +0,0 @@ -import { Meta, StoryObj } from '@storybook/react'; - -import { Heading } from '../../base/typography/heading/heading'; -import { Text } from '../../base/typography/text/text'; -import { VerticalSpacing } from '../../layout/vertical-spacing'; -import Collapse from './collapse'; - -/** - * Figma ↗
- * Zeroheight ↗ - */ - -const meta: Meta = { - component: Collapse, - title: 'Tedi-ready/Components/Buttons/Collapse', - parameters: { - status: { - type: [{ name: 'breakpointSupport', url: '?path=/docs/helpers-usebreakpointprops--usebreakpointprops' }], - }, - design: { - type: 'figma', - url: 'https://www.figma.com/design/jWiRIXhHRxwVdMSimKX2FF/TEDI-READY-2.0.4-(work-in-progress)?node-id=15433-138256&m=dev', - }, - controls: { - exclude: ['sm', 'md', 'lg', 'xl', 'xxl'], - }, - }, -}; - -export default meta; -type Story = StoryObj; - -export const Default: Story = {}; - -export const IconNeutralButton = { - args: { - ...Default.args, - hideCollapseText: true, - }, -}; - -export const IconSecondaryButton = { - args: { - ...Default.args, - arrowType: 'secondary', - hideCollapseText: true, - }, -}; - -export const TitleRow = { - args: { - id: 'collapse-1', - openText: 'Näita rohkem', - closeText: 'Näita vähem', - title: ( - - Juhtumi üldandmed - - ), - children: ( - -
- Laste osalus -

peretüli lapse osaluseta

-
-
- Juhtumi liigid -

peretüli (lapsega)

-
-
- Kannatanu seos vägivaldsega -

tütar

-
-
- ), - }, -}; diff --git a/libs/react-components/src/tedi/components/buttons/collapse/collapse.tsx b/libs/react-components/src/tedi/components/buttons/collapse/collapse.tsx deleted file mode 100644 index 72e136b0c..000000000 --- a/libs/react-components/src/tedi/components/buttons/collapse/collapse.tsx +++ /dev/null @@ -1,183 +0,0 @@ -import cn from 'classnames'; -import React from 'react'; -import AnimateHeight from 'react-animate-height'; - -import { BreakpointSupport, useBreakpointProps, usePrint } from '../../../helpers'; -import { useLabels } from '../../../providers/label-provider'; -import { Icon } from '../../base/icon/icon'; -import { Text } from '../../base/typography/text/text'; -import { Col, Row, RowProps } from '../../layout/grid'; -import Print from '../../misc/print/print'; -import styles from './collapse.module.scss'; - -type CollapseBreakpointProps = { - /** - * Whether the collapse should be initially open (uncontrolled mode). - * This is ignored when `open` and `onToggle` are provided. - * @default false - */ - defaultOpen?: boolean; - - /** - * Controls the open/closed state of the collapse (controlled mode). - * Should be used together with `onToggle`. - */ - open?: boolean; - /** - * Whether to visually hide the open/close text on the toggle button. - * Useful for icon-only toggles. - * @default false - */ - hideCollapseText?: boolean; - /** - * Additional props to pass to the `Row` component used in the title area. - */ - titleRowProps?: RowProps; - /** - * Custom class name for the root element. - */ - className?: string; - /* - * Display toggle arrow as default or secondary button style - */ - arrowType?: 'default' | 'secondary'; -}; - -export interface CollapseProps extends BreakpointSupport { - /** - * Unique identifier for the collapse content. - * Used for ARIA attributes like `aria-controls`. - */ - id: string; - /** - * Content to be displayed inside the collapsible area. - */ - children: React.ReactNode; - /** - * Callback triggered when the collapse is toggled. - * Use this to update the `open` prop in controlled mode. - */ - onToggle?: (open: boolean) => void; - /** - * The title/header element for the collapsible section. - * Rendered inside the toggle button. - */ - title?: JSX.Element; - /** - * Text shown on the toggle button when the content is collapsed. - * Defaults to the result of `getLabel('open')`. - */ - openText?: string; - /** - * Text shown on the toggle button when the content is expanded. - * Defaults to the result of `getLabel('close')`. - */ - closeText?: string; -} - -export const Collapse = (props: CollapseProps): JSX.Element => { - const { getCurrentBreakpointProps } = useBreakpointProps(props.defaultServerBreakpoint); - const { getLabel } = useLabels(); - const { - id, - children, - className, - openText = getLabel('open'), - closeText = getLabel('close'), - hideCollapseText = false, - title, - titleRowProps, - defaultOpen, - open, - onToggle, - arrowType = 'neutral', - ...rest - } = getCurrentBreakpointProps(props); - - const [isOpenState, setIsOpen] = React.useState(() => defaultOpen); - const isPrint = usePrint(); - - const isOpen = React.useMemo( - () => isPrint || (open !== undefined ? open : isOpenState), - [isPrint, open, isOpenState] - ); - - const CollapseBEM = React.useMemo( - () => - cn(styles['tedi-collapse'], className, { - [styles['tedi-collapse--is-open']]: isOpen, - }), - [className, isOpen] - ); - - const handleClick = () => { - const newOpenState = !isOpen; - setIsOpen(newOpenState); - onToggle?.(newOpenState); - }; - - const handleKeyDown = (e: React.KeyboardEvent) => { - if ((e.key === 'Enter' || e.key === ' ') && !e.repeat) { - e.preventDefault(); - handleClick(); - } - }; - - const renderContent = React.useMemo( - () =>
{children}
, - [children] - ); - - return ( -
- - {isPrint ? ( - renderContent - ) : ( - - {renderContent} - - )} -
- ); -}; - -export default Collapse; diff --git a/libs/react-components/src/tedi/components/buttons/floating-button/floating-button.module.scss b/libs/react-components/src/tedi/components/buttons/floating-button/floating-button.module.scss deleted file mode 100644 index d543084d5..000000000 --- a/libs/react-components/src/tedi/components/buttons/floating-button/floating-button.module.scss +++ /dev/null @@ -1,95 +0,0 @@ -@use '@tehik-ee/tedi-core/mixins'; -@use '@tehik-ee/tedi-core/bootstrap-utility/breakpoints'; -@use '../button-content/button-content.module'; - -$btn-height: 2.75rem; -$btn-height-small: 2.5rem; -$btn-height-large: 3rem; - -.tedi-floating-button { - box-sizing: border-box; - text-wrap: wrap; - box-shadow: 0 4px 10px 0 var(--alpha-14); - - &--horizontal { - @include mixins.responsive-styles(border-radius, button-radius-default); - } - - &--vertical { - min-width: max-content; - white-space: nowrap; - border-radius: 0; - transform: rotate(-90deg); - transform-origin: center; - - @include mixins.responsive-styles(border-top-right-radius, button-radius-sm); - @include mixins.responsive-styles(border-top-left-radius, button-radius-sm); - } - - &.tedi-floating-button--primary { - @include button-content.button-variant( - var(--button-floating-primary-background-default), - var(--button-floating-primary-border-default), - var(--button-floating-primary-text-default), - var(--button-floating-primary-background-hover), - var(--button-floating-primary-border-hover), - var(--button-floating-primary-text-default), - var(--button-floating-primary-border-focus), - var(--button-floating-primary-background-focus), - var(--button-floating-primary-text-default), - var(--button-floating-primary-border-active), - var(--button-floating-primary-background-active) - ); - } - - &.tedi-floating-button--secondary { - @include button-content.button-variant( - var(--button-floating-secondary-background-default), - var(--button-floating-secondary-border-default), - var(--button-floating-secondary-text-default), - var(--button-floating-secondary-background-hover), - var(--button-floating-secondary-border-hover), - var(--button-floating-secondary-text-hover), - var(--button-floating-primary-border-focus), - var(--button-floating-secondary-background-focus), - var(--button-floating-secondary-text-active), - var(--button-floating-secondary-border-active), - var(--button-floating-secondary-background-active) - ); - } - - &--medium { - height: auto; - min-height: $btn-height; - - @include breakpoints.media-breakpoint-up(md) { - min-height: $btn-height-small; - } - - @include mixins.responsive-styles(padding, button-md-padding-y button-md-padding-x); - - &.tedi-floating-button--icon-only { - @include mixins.responsive-styles(width, button-md-icon-size); - @include mixins.responsive-styles(height, button-md-icon-size); - @include mixins.responsive-styles(padding, button-md-icon-padding); - } - } - - &--large { - height: auto; - min-height: $btn-height-large; - - @include mixins.responsive-styles(padding, button-xl-padding-y button-xl-padding-x); - - &.tedi-floating-button--icon-only { - @include mixins.responsive-styles(width, button-xl-icon-size); - @include mixins.responsive-styles(height, button-xl-icon-size); - @include mixins.responsive-styles(padding, button-xl-icon-padding); - } - } - - &:not(&--icon-only) { - @include mixins.responsive-styles(min-width, button-width-min); - @include mixins.responsive-styles(max-width, button-width-max); - } -} diff --git a/libs/react-components/src/tedi/components/buttons/floating-button/floating-button.spec.tsx b/libs/react-components/src/tedi/components/buttons/floating-button/floating-button.spec.tsx deleted file mode 100644 index a828a0b8a..000000000 --- a/libs/react-components/src/tedi/components/buttons/floating-button/floating-button.spec.tsx +++ /dev/null @@ -1,85 +0,0 @@ -import { render, screen } from '@testing-library/react'; - -import FloatingButton, { FloatingButtonProps } from './floating-button'; - -import '@testing-library/jest-dom'; - -describe('FloatingButton component', () => { - const defaultProps: FloatingButtonProps = { - children: 'Click Me', - }; - - it('renders floating button with default styles and children', () => { - render(); - const button = screen.getByRole('button'); - expect(button).toHaveClass( - 'tedi-floating-button tedi-floating-button--horizontal tedi-floating-button--primary tedi-floating-button--medium' - ); - }); - - it('applies custom class names', () => { - render(); - const button = screen.getByRole('button'); - expect(button).toHaveClass('custom-class'); - }); - - it('renders with icon only', () => { - render(); - const button = screen.getByRole('button'); - expect(button).toHaveClass('tedi-floating-button--icon-only'); - }); - - it('renders vertical button', () => { - render(); - const button = screen.getByRole('button'); - expect(button).toHaveClass('tedi-floating-button--vertical'); - }); - - it('renders large button', () => { - render(); - const button = screen.getByRole('button'); - expect(button).toHaveClass('tedi-floating-button--large'); - }); - - it('renders secondary button', () => { - render(); - const button = screen.getByRole('button'); - expect(button).toHaveClass('tedi-floating-button--secondary'); - }); - - it('renders button top left', () => { - render(); - const button = screen.getByRole('button'); - expect(button).toHaveStyle({ - top: 0, - left: 0, - }); - }); - - it('renders button top right', () => { - render(); - const button = screen.getByRole('button'); - expect(button).toHaveStyle({ - top: 0, - right: 0, - }); - }); - - it('renders button bottom left', () => { - render(); - const button = screen.getByRole('button'); - expect(button).toHaveStyle({ - bottom: 0, - left: 0, - }); - }); - - it('renders button bottom right', () => { - render(); - const button = screen.getByRole('button'); - expect(button).toHaveStyle({ - bottom: 0, - right: 0, - }); - }); -}); diff --git a/libs/react-components/src/tedi/components/buttons/floating-button/floating-button.stories.tsx b/libs/react-components/src/tedi/components/buttons/floating-button/floating-button.stories.tsx deleted file mode 100644 index e3d0efef7..000000000 --- a/libs/react-components/src/tedi/components/buttons/floating-button/floating-button.stories.tsx +++ /dev/null @@ -1,164 +0,0 @@ -import { Meta, StoryFn, StoryObj } from '@storybook/react'; - -import { Text } from '../../base/typography/text/text'; -import { Col, Row } from '../../layout/grid'; -import FloatingButton, { FloatingButtonProps } from './floating-button'; - -/** - * Figma ↗
- * Zeroheight ↗ - */ - -const meta: Meta = { - component: FloatingButton, - title: 'Tedi-ready/Components/Buttons/FloatingButton', - parameters: { - design: { - type: 'figma', - url: 'https://www.figma.com/design/jWiRIXhHRxwVdMSimKX2FF/TEDI-READY-(work-in-progress)?node-id=4514-73451&m=dev', - }, - }, -}; - -export default meta; -type Story = StoryObj; - -const buttonSizeArray = ['Default', 'Large']; -const buttonStateArray = ['Default', 'Hover', 'Active', 'Focus']; - -type TemplateMultipleProps = FloatingButtonProps & { - array: Type; -}; - -const TemplateColumn: StoryFn = (args) => { - const { array, ...buttonProps } = args; - - return ( - - {buttonSizeArray.map((size) => ( - - - - {size} - - - - {array.map((value, key) => ( - - - {value} - - - - Scroll up - - - Scroll up - - - Scroll up - - {buttonProps.axis === 'horizontal' && ( - - Scroll up - - )} - - - ))} - - - ))} - - ); -}; - -export const Default: Story = { - args: { - children: 'Scroll up', - position: 'static', - }, -}; - -export const PrimaryHorizontal: StoryObj = { - render: TemplateColumn, - args: { - array: buttonStateArray, - axis: 'horizontal', - visualType: 'primary', - position: 'static', - }, - parameters: { - pseudo: { - hover: '#Hover', - active: '#Active', - focus: '#Focus', - }, - }, -}; - -export const PrimaryVertical: StoryObj = { - render: TemplateColumn, - args: { - array: buttonStateArray, - axis: 'vertical', - visualType: 'primary', - position: 'static', - }, - parameters: { - pseudo: { - hover: '#Hover', - active: '#Active', - focus: '#Focus', - }, - }, -}; - -export const SecondaryHorizontal: StoryObj = { - render: TemplateColumn, - args: { - array: buttonStateArray, - axis: 'horizontal', - visualType: 'secondary', - position: 'static', - }, - parameters: { - pseudo: { - hover: '#Hover', - active: '#Active', - focus: '#Focus', - }, - }, -}; - -export const SecondaryVertical: StoryObj = { - render: TemplateColumn, - args: { - array: buttonStateArray, - axis: 'vertical', - visualType: 'secondary', - position: 'static', - }, - parameters: { - pseudo: { - hover: '#Hover', - active: '#Active', - focus: '#Focus', - }, - }, -}; diff --git a/libs/react-components/src/tedi/components/buttons/floating-button/floating-button.tsx b/libs/react-components/src/tedi/components/buttons/floating-button/floating-button.tsx deleted file mode 100644 index 719b64eb7..000000000 --- a/libs/react-components/src/tedi/components/buttons/floating-button/floating-button.tsx +++ /dev/null @@ -1,101 +0,0 @@ -import cn from 'classnames'; -import { CSSProperties } from 'react'; - -import Button, { ButtonProps } from '../button/button'; -import styles from './floating-button.module.scss'; - -export type FloatingButtonAxis = 'horizontal' | 'vertical'; -export type FloatingButtonColor = 'primary' | 'secondary'; -export type FloatingButtonSize = 'medium' | 'large'; -export type FloatingButtonPlacement = { - vertical: 'top' | 'bottom' | 'center'; - horizontal: 'left' | 'right' | 'center'; -}; -export type FloatingButtonOffset = { - top?: number | string; - bottom?: number | string; - left?: number | string; - right?: number | string; -}; - -export interface FloatingButtonProps - extends Omit< - ButtonProps, - 'visualType' | 'fullWidth' | 'color' | 'size' | 'isLoading' | 'noStyle' | 'renderWrapperElement' - > { - /** - * Button axis - * @default horizontal - */ - axis?: FloatingButtonAxis; - /** - * Button visual type - * @default primary - */ - visualType?: FloatingButtonColor; - /** - * Button size - * @default medium - */ - size?: FloatingButtonSize; - /** - * Button position - * @default fixed - */ - position?: CSSProperties['position']; - /** - * Button placement - */ - placement?: FloatingButtonPlacement; - /** - * Button offset - */ - offset?: FloatingButtonOffset; - /** - * Button z-index - */ - zIndex?: number; -} - -export const FloatingButton = (props: FloatingButtonProps): JSX.Element => { - const { - children, - className, - axis = 'horizontal', - visualType = 'primary', - size = 'medium', - position = 'fixed', - placement, - offset, - zIndex, - ...rest - } = props; - - const placementStyles: CSSProperties = { - position, - zIndex, - ...(placement?.vertical === 'top' && { top: offset?.top ?? 0 }), - ...(placement?.vertical === 'bottom' && { bottom: offset?.bottom ?? 0 }), - ...(placement?.vertical === 'center' && { top: '50%', transform: 'translateY(-50%)' }), - ...(placement?.horizontal === 'left' && { left: offset?.left ?? 0 }), - ...(placement?.horizontal === 'right' && { right: offset?.right ?? 0 }), - ...(placement?.horizontal === 'center' && { left: '50%', transform: 'translateX(-50%)' }), - }; - - const BEM = cn( - styles['tedi-floating-button'], - styles[`tedi-floating-button--${axis}`], - styles[`tedi-floating-button--${visualType}`], - styles[`tedi-floating-button--${size}`], - { [styles['tedi-floating-button--icon-only']]: rest.icon }, - className - ); - - return ( - - ); -}; - -export default FloatingButton; diff --git a/libs/react-components/src/tedi/components/buttons/info-button/info-button.module.scss b/libs/react-components/src/tedi/components/buttons/info-button/info-button.module.scss deleted file mode 100644 index d673a731d..000000000 --- a/libs/react-components/src/tedi/components/buttons/info-button/info-button.module.scss +++ /dev/null @@ -1,10 +0,0 @@ -@use '@tehik-ee/tedi-core/mixins'; -@use '@tehik-ee/tedi-core/bootstrap-utility/breakpoints'; - -.tedi-info-button { - vertical-align: bottom; - - @include mixins.responsive-styles(width, button-xs-icon-size); - @include mixins.responsive-styles(height, button-xs-icon-size); - @include mixins.responsive-styles(min-height, button-xs-icon-size); -} diff --git a/libs/react-components/src/tedi/components/buttons/info-button/info-button.spec.tsx b/libs/react-components/src/tedi/components/buttons/info-button/info-button.spec.tsx deleted file mode 100644 index b450f9a3c..000000000 --- a/libs/react-components/src/tedi/components/buttons/info-button/info-button.spec.tsx +++ /dev/null @@ -1,69 +0,0 @@ -import { render, screen } from '@testing-library/react'; -import React from 'react'; - -import { InfoButton } from './info-button'; - -import '@testing-library/jest-dom'; - -describe('InfoButton Component', () => { - it('renders child elements correctly', () => { - render( - -
Test content
-
- ); - const childElement = screen.getByTestId('child'); - expect(childElement).toBeInTheDocument(); - expect(childElement).toHaveTextContent('Test content'); - }); - - it('applies custom className correctly', () => { - const { container } = render(Info); - const button = container.querySelector('button'); - expect(button).toHaveClass('custom-class'); - }); - - it('renders with the correct default icon size', () => { - const { container } = render(Info); - const icon = container.querySelector('span[data-name="icon"]'); - expect(icon).toBeInTheDocument(); - expect(icon).toHaveClass('tedi-icon--size-18'); - }); - - it('renders with the smaller icon when isSmall is true', () => { - const { container } = render(Info); - const icon = container.querySelector('span[data-name="icon"]'); - expect(icon).toBeInTheDocument(); - expect(icon).toHaveClass('tedi-icon--size-16'); - }); - - it('passes ref correctly to Button component', () => { - const ref = React.createRef(); - render(Info); - expect(ref.current).toBeInstanceOf(HTMLButtonElement); - }); - - it('renders children correctly within the Button component', () => { - render(Click me!); - const button = screen.getByRole('button'); - expect(button).toHaveTextContent('Click me!'); - }); - - it('applies default button props correctly', () => { - const { container } = render(Default Button); - const button = container.querySelector('button'); - expect(button).toHaveAttribute('type', 'button'); - expect(button).toHaveAttribute('data-name', 'info-button'); - }); - - it('can receive custom props like title and id', () => { - render( - - Info - - ); - const button = screen.getByRole('button'); - expect(button).toHaveAttribute('title', 'Info button'); - expect(button).toHaveAttribute('id', 'info-button-id'); - }); -}); diff --git a/libs/react-components/src/tedi/components/buttons/info-button/info-button.stories.tsx b/libs/react-components/src/tedi/components/buttons/info-button/info-button.stories.tsx deleted file mode 100644 index 3b9b98f28..000000000 --- a/libs/react-components/src/tedi/components/buttons/info-button/info-button.stories.tsx +++ /dev/null @@ -1,72 +0,0 @@ -import { Meta, StoryFn, StoryObj } from '@storybook/react'; - -import { Text } from '../../base/typography/text/text'; -import { Col, Row } from '../../layout/grid'; -import { VerticalSpacing } from '../../layout/vertical-spacing'; -import InfoButton from './info-button'; - -/** - * Figma ↗
- * Zeroheight ↗ - */ - -const meta: Meta = { - component: InfoButton, - title: 'Tedi-Ready/Components/Buttons/InfoButton', - parameters: { - design: { - type: 'figma', - url: 'https://www.figma.com/design/jWiRIXhHRxwVdMSimKX2FF/TEDI-READY-(work-in-progress)?node-id=4514-72997&m=dev', - }, - }, -}; - -export default meta; -type Story = StoryObj; - -const buttonStateArray = ['Default', 'Hover', 'Active', 'Focus']; - -type TemplateMultipleProps = { - array: typeof buttonStateArray; -}; - -const TemplateColumn: StoryFn = (args) => { - const { array, ...buttonProps } = args; - - return ( - - {array.map((state, index) => ( - - - {state} - - - - Info button - - - - ))} - - ); -}; - -export const Default: Story = { - args: { - title: 'Info button', - }, -}; - -export const InfoButtonStates: StoryObj = { - render: TemplateColumn, - args: { - array: buttonStateArray, - }, - parameters: { - pseudo: { - hover: '#Hover', - active: '#Active', - focusVisible: '#Focus', - }, - }, -}; diff --git a/libs/react-components/src/tedi/components/buttons/info-button/info-button.tsx b/libs/react-components/src/tedi/components/buttons/info-button/info-button.tsx deleted file mode 100644 index 31257b5fd..000000000 --- a/libs/react-components/src/tedi/components/buttons/info-button/info-button.tsx +++ /dev/null @@ -1,33 +0,0 @@ -import cn from 'classnames'; -import React from 'react'; - -import { Button, ButtonProps } from '../button/button'; -import styles from './info-button.module.scss'; - -export interface InfoButtonProps extends Omit { - /** - * If true, applies a small size to the InfoButton. - * @default false - */ - isSmall?: boolean; -} - -export const InfoButton = React.forwardRef( - ({ isSmall, ...props }, ref): JSX.Element => ( - - ) -); - -InfoButton.displayName = 'InfoButton'; - -export default InfoButton; diff --git a/libs/react-components/src/tedi/components/cards/card/card-content/card-content.spec.tsx b/libs/react-components/src/tedi/components/cards/card/card-content/card-content.spec.tsx deleted file mode 100644 index 7125b015f..000000000 --- a/libs/react-components/src/tedi/components/cards/card/card-content/card-content.spec.tsx +++ /dev/null @@ -1,90 +0,0 @@ -import { render, screen } from '@testing-library/react'; -import { UnknownType } from 'libs/react-components/src/tedi/types/commonTypes'; - -import { useBreakpointProps } from '../../../../helpers'; -import { CardContext } from '../card-context'; -import { CardContent, CardContentProps } from './card-content'; - -import '@testing-library/jest-dom'; - -jest.mock('../../../../helpers', () => ({ - useBreakpointProps: jest.fn(), -})); - -describe('CardContent', () => { - beforeEach(() => { - (useBreakpointProps as jest.Mock).mockReturnValue({ - getCurrentBreakpointProps: jest.fn((props) => props), - }); - }); - - const renderComponent = (props?: CardContentProps, contextValue?: UnknownType) => { - const context = contextValue || {}; - return render( - - Test Content - - ); - }; - - it('renders without crashing with default props', () => { - renderComponent(); - const content = screen.getByText('Test Content'); - expect(content).toBeInTheDocument(); - expect(content).toHaveClass('tedi-card__content'); - }); - - it('applies additional className', () => { - renderComponent({ className: 'custom-class' }); - const content = screen.getByText('Test Content'); - expect(content).toHaveClass('custom-class'); - }); - - it('applies numeric padding', () => { - renderComponent({ padding: 2 }); - const content = screen.getByText('Test Content'); - expect(content).toHaveAttribute('data-padding', '2rem'); - }); - - it('applies object-based padding', () => { - renderComponent({ padding: { vertical: 1, horizontal: 2 } }); - const content = screen.getByText('Test Content'); - - expect(content).not.toHaveAttribute('data-padding'); - - expect(content).toHaveStyle({ - '--card-content-padding-top': '1rem', - '--card-content-padding-bottom': '1rem', - '--card-content-padding-left': '2rem', - '--card-content-padding-right': '2rem', - }); - }); - - it('applies background properties', () => { - renderComponent({ - backgroundImage: 'test-image.jpg', - backgroundPosition: 'center', - backgroundSize: 'cover', - backgroundRepeat: 'no-repeat', - }); - const content = screen.getByText('Test Content'); - expect(content).toHaveStyle({ - backgroundImage: 'url(test-image.jpg)', - backgroundPosition: 'center', - backgroundSize: 'cover', - backgroundRepeat: 'no-repeat', - }); - }); - - it('renders as a button when role="button"', () => { - renderComponent({ role: 'button' }); - const content = screen.getByRole('button'); - expect(content.tagName).toBe('BUTTON'); - }); - - it('renders as a div by default', () => { - renderComponent(); - const content = screen.getByText('Test Content'); - expect(content.tagName).toBe('DIV'); - }); -}); diff --git a/libs/react-components/src/tedi/components/cards/card/card-content/card-content.tsx b/libs/react-components/src/tedi/components/cards/card/card-content/card-content.tsx deleted file mode 100644 index 9a4632dcb..000000000 --- a/libs/react-components/src/tedi/components/cards/card/card-content/card-content.tsx +++ /dev/null @@ -1,62 +0,0 @@ -import cn from 'classnames'; -import React, { CSSProperties } from 'react'; - -import { BreakpointSupport, useBreakpointProps } from '../../../../helpers'; -import { SharedCardProps } from '../card'; -import styles from '../card.module.scss'; -import { CardContext } from '../card-context'; -import { getPaddingCssVariables } from '../utility'; - -export interface CardContentProps extends BreakpointSupport { - children?: React.ReactNode; - role?: 'button'; - style?: CSSProperties; -} - -export const CardContent = (props: CardContentProps): JSX.Element => { - const { padding: rootPadding, background: rootBackground } = React.useContext(CardContext); - const { getCurrentBreakpointProps } = useBreakpointProps(props.defaultServerBreakpoint); - - const { - children, - className, - padding, - background = 'primary', - backgroundImage, - backgroundPosition, - backgroundSize, - backgroundRepeat, - hasSeparator, - role, - style, - ...rest - } = getCurrentBreakpointProps(props, { - padding: rootPadding, - background: rootBackground, - }); - - const backgroundClass = background ? styles[`tedi-card--background--${background}`] : ''; - const backgroundStyle: React.CSSProperties = { - backgroundImage: backgroundImage ? `url(${backgroundImage})` : undefined, - backgroundPosition, - backgroundSize, - backgroundRepeat, - }; - const Component = role === 'button' ? 'button' : 'div'; - - return ( - - {children} - - ); -}; - -export default CardContent; diff --git a/libs/react-components/src/tedi/components/cards/card/card-context.ts b/libs/react-components/src/tedi/components/cards/card/card-context.ts deleted file mode 100644 index 5760d16e9..000000000 --- a/libs/react-components/src/tedi/components/cards/card/card-context.ts +++ /dev/null @@ -1,7 +0,0 @@ -import React from 'react'; - -import type { CardContentProps } from './card-content/card-content'; - -type CardContext = Pick; - -export const CardContext = React.createContext({}); diff --git a/libs/react-components/src/tedi/components/cards/card/card-header/card-header.spec.tsx b/libs/react-components/src/tedi/components/cards/card/card-header/card-header.spec.tsx deleted file mode 100644 index 60f1362ff..000000000 --- a/libs/react-components/src/tedi/components/cards/card/card-header/card-header.spec.tsx +++ /dev/null @@ -1,93 +0,0 @@ -import { render, screen } from '@testing-library/react'; -import { UnknownType } from 'libs/react-components/src/tedi/types/commonTypes'; - -import { useBreakpointProps } from '../../../../helpers'; -import styles from '../card.module.scss'; -import { CardContentProps } from '../card-content/card-content'; -import { CardContext } from '../card-context'; -import CardHeader from './card-header'; - -jest.mock('../../../../helpers', () => ({ - useBreakpointProps: jest.fn(), -})); - -describe('CardHeader', () => { - beforeEach(() => { - (useBreakpointProps as jest.Mock).mockReturnValue({ - getCurrentBreakpointProps: jest.fn((props) => props), - }); - }); - - const renderComponent = (props?: CardContentProps, contextValue?: UnknownType) => { - const context = contextValue || {}; - return render( - - Test Header - - ); - }; - - it('renders without crashing with default props', () => { - renderComponent(); - const header = screen.getByText('Test Header'); - expect(header).toBeInTheDocument(); - expect(header).toHaveClass(styles['tedi-card__header']); - }); - - it('applies additional className', () => { - renderComponent({ className: 'extra-class' }); - const header = screen.getByText('Test Header'); - expect(header).toHaveClass('extra-class'); - }); - - it('applies numeric padding correctly', () => { - renderComponent({ padding: 1.5 }); - const header = screen.getByText('Test Header'); - expect(header).toHaveAttribute('data-padding', '1.5rem'); - }); - - it('applies object-based padding styles', () => { - renderComponent({ padding: { vertical: 1, horizontal: 2 } }); - const header = screen.getByText('Test Header'); - expect(header).toHaveStyle({ - '--card-content-padding-top': '1rem', - '--card-content-padding-bottom': '1rem', - '--card-content-padding-left': '2rem', - '--card-content-padding-right': '2rem', - }); - }); - - it('applies background properties correctly', () => { - renderComponent({ - backgroundImage: 'test-image.jpg', - backgroundPosition: 'center', - backgroundSize: 'cover', - backgroundRepeat: 'no-repeat', - }); - const header = screen.getByText('Test Header'); - expect(header).toHaveStyle({ - backgroundImage: 'url(test-image.jpg)', - backgroundPosition: 'center', - backgroundSize: 'cover', - backgroundRepeat: 'no-repeat', - }); - }); - - it('renders with separator class when hasSeparator is true', () => { - renderComponent({ hasSeparator: true }); - const header = screen.getByText('Test Header'); - expect(header).toHaveClass(styles['tedi-card__content--separator']); - }); - - it('renders as a button when role="button" is provided', () => { - renderComponent({ role: 'button' }); - const header = screen.getByRole('button'); - expect(header).toBeInTheDocument(); - }); - - it('renders as a div by default', () => { - renderComponent(); - const header = screen.getByText('Test Header'); - expect(header.tagName).toBe('DIV'); - }); -}); diff --git a/libs/react-components/src/tedi/components/cards/card/card-header/card-header.tsx b/libs/react-components/src/tedi/components/cards/card/card-header/card-header.tsx deleted file mode 100644 index 09b2653a8..000000000 --- a/libs/react-components/src/tedi/components/cards/card/card-header/card-header.tsx +++ /dev/null @@ -1,12 +0,0 @@ -import cn from 'classnames'; - -import styles from '../card.module.scss'; -import { CardContent, CardContentProps } from '../card-content/card-content'; - -export const CardHeader = (props: CardContentProps): JSX.Element => { - const { className, background = 'brand-primary', ...rest } = props; - - return ; -}; - -export default CardHeader; diff --git a/libs/react-components/src/tedi/components/cards/card/card-notification/card-notification.module.scss b/libs/react-components/src/tedi/components/cards/card/card-notification/card-notification.module.scss deleted file mode 100644 index 70d53b832..000000000 --- a/libs/react-components/src/tedi/components/cards/card/card-notification/card-notification.module.scss +++ /dev/null @@ -1,17 +0,0 @@ -.tedi-card__notification-content { - z-index: 1; - padding: 0; - - [data-name='card-header'] + & { - margin-top: -1px; - } - - &:last-child { - margin-bottom: -1px; - } -} - -.tedi-card__notification { - padding: var(--card-content-padding-top, 1rem) var(--card-content-padding-right, 1rem) - var(--card-content-padding-bottom, 1rem) var(--card-content-padding-left, 1rem); -} diff --git a/libs/react-components/src/tedi/components/cards/card/card-notification/card-notification.spec.tsx b/libs/react-components/src/tedi/components/cards/card/card-notification/card-notification.spec.tsx deleted file mode 100644 index 301abddce..000000000 --- a/libs/react-components/src/tedi/components/cards/card/card-notification/card-notification.spec.tsx +++ /dev/null @@ -1,69 +0,0 @@ -import { render, screen } from '@testing-library/react'; -import cn from 'classnames'; - -import { Alert } from '../../../notifications/alert/alert'; -import CardNotification, { CardNotificationProps } from './card-notification'; -import style from './card-notification.module.scss'; - -jest.mock('../../../notifications/alert/alert', () => ({ - Alert: jest.fn(({ children, className }) =>
{children}
), -})); - -jest.mock('../card-content/card-content', () => ({ - __esModule: true, - default: jest.fn(({ children, className, 'data-name': dataName }) => ( -
- {children} -
- )), -})); - -describe('CardNotification', () => { - const renderComponent = (props?: Partial) => - render( - - Test Notification - - ); - - it('renders without crashing', () => { - renderComponent(); - const notification = screen.getByText('Test Notification'); - - expect(notification).toBeInTheDocument(); - }); - - it('passes children to Alert', () => { - renderComponent(); - const alert = screen.getByText('Test Notification'); - - expect(alert).toBeInTheDocument(); - }); - - it('applies custom class names', () => { - renderComponent({ className: 'extra-class' }); - const alert = screen.getByText('Test Notification'); - - expect(alert).toHaveClass(cn(style['tedi-card__notification'], 'extra-class')); - }); - - it('applies noSideBorders prop to Alert', () => { - renderComponent(); - expect(Alert).toHaveBeenCalledWith( - expect.objectContaining({ - noSideBorders: true, - }), - {} - ); - }); - - it('passes additional props to Alert', () => { - renderComponent({ type: 'success' }); - expect(Alert).toHaveBeenCalledWith( - expect.objectContaining({ - type: 'success', - }), - {} - ); - }); -}); diff --git a/libs/react-components/src/tedi/components/cards/card/card-notification/card-notification.tsx b/libs/react-components/src/tedi/components/cards/card/card-notification/card-notification.tsx deleted file mode 100644 index 35ee6eda8..000000000 --- a/libs/react-components/src/tedi/components/cards/card/card-notification/card-notification.tsx +++ /dev/null @@ -1,23 +0,0 @@ -import cn from 'classnames'; - -import { Alert, AlertProps } from '../../../notifications/alert/alert'; -import CardContent, { CardContentProps } from '../card-content/card-content'; -import style from './card-notification.module.scss'; - -export type CardNotificationProps = AlertProps & Pick; - -export const CardNotification = (props: CardNotificationProps): JSX.Element => { - const { children, padding, className, ...rest } = props; - - const cardNotificationBEM = cn(style['tedi-card__notification'], className); - - return ( - - - {children} - - - ); -}; - -export default CardNotification; diff --git a/libs/react-components/src/tedi/components/cards/card/card-stories-templates.tsx b/libs/react-components/src/tedi/components/cards/card/card-stories-templates.tsx deleted file mode 100644 index 37fc11aa6..000000000 --- a/libs/react-components/src/tedi/components/cards/card/card-stories-templates.tsx +++ /dev/null @@ -1,463 +0,0 @@ -/* istanbul ignore file */ -import { StoryFn } from '@storybook/react/*'; - -import { Icon } from '../../base/icon/icon'; -import { Heading } from '../../base/typography/heading/heading'; -import { Text } from '../../base/typography/text/text'; -import { Button } from '../../buttons/button/button'; -import { HeadingWithIcon } from '../../content/heading-with-icon/heading-with-icon'; -import { Col, Row } from '../../layout/grid'; -import { VerticalSpacing } from '../../layout/vertical-spacing'; -import { Separator } from '../../misc/separator/separator'; -import { Link } from '../../navigation/link/link'; -import { StatusBadge } from '../../tags/status-badge/status-badge'; -import { CardStory } from './card.stories'; -import { Card, CardContentPadding } from './index'; -import { CardBackground } from './utility'; - -export const HeaderTypesTemplate: StoryFn = (_args) => ( - - - - Title - - - - - Title - Description - - - - - - - Title - - - - - - - - Description - - - - - - - - - Title - - - - - - - - - Description - - - - - - - - - Title - - - - View result - - - - - - Description - - - - - - - - - Title - - - Approved - - - - - Description - - - - - - - - - Title - - - - - - - - Description - - - - - - - - - Title - - - - - - - - Description - - - - - - - - - Title - - - - - - - - Description - - - - - - - - - Title - - - - - - - - Description - - - - - -); - -export const DefaultCardTemplates: StoryFn = (_args) => ( - - - - Description - - - - - Description - Approved - - - - - Title - Description - - - - - Title - - - Description - - - Approved - - - - - - - - - - Description - - - - - - - - - - - Title - Description - - - - - - - - - - - - Title - Description - - - - - - - - - - - - - - - Title - Description - - - - - - - - - - - - - -); - -export const CardInfoTemplate: StoryFn = (_args) => ( - - - - - - - - Title - Description - - - - - - - - - - - - Title - Description - - - - - - - - - - - - Title - Description - - - - - - - - - - - - Haigusleht: 118. päev - - - - - - -); - -export const AlternativeCardsTemplate: StoryFn = (_args) => ( - - - - - - - My statement of intention - - - - - - For example organ donation and blood transfusion - - - - - - - - - - - - - Title - For example organ donation and blood transfusion - - - - - - - - - - - - - - Short title - - - - - - - For example organ donation and blood transfusion - - - - - - - - - - - For example organ donation and blood transfusion - - - - - - - - - Card important - - - - - -); - -export const SpacingTemplate: StoryFn = (_args) => { - const paddings: CardContentPadding[] = [ - { top: 0.5, left: 0.5, right: 0.5, bottom: 0.5 }, - { top: 1, left: 1, right: 1, bottom: 1 }, - { top: 1.5, left: 1.5, right: 1.5, bottom: 1.5 }, - ]; - - return ( - - {paddings.map((padding, index) => ( - - - - - Cabbage, comprising several cultivars of Brassica oleracea, is a leafy green, red (purple), or white - (pale green) biennial plant grown as an annual vegetable crop for its dense-leaved heads. - - - - - ))} - - ); -}; - -export const BackgroundColorsTemplate: StoryFn = (_args) => { - const backgroundColors: CardBackground[] = [ - 'primary', - 'secondary', - 'tertiary', - 'brand-primary', - 'brand-secondary', - 'brand-tertiary', - 'brand-quaternary', - 'success-primary', - 'accent', - ]; - - return ( - - {backgroundColors.map((color, index) => ( - - - - - Cabbage, comprising several cultivars of Brassica oleracea, is a leafy green, red (purple), or white - (pale green) biennial plant grown as an annual vegetable crop for its dense-leaved heads. - - - - - ))} - - ); -}; diff --git a/libs/react-components/src/tedi/components/cards/card/card.module.scss b/libs/react-components/src/tedi/components/cards/card/card.module.scss deleted file mode 100644 index 391d242f1..000000000 --- a/libs/react-components/src/tedi/components/cards/card/card.module.scss +++ /dev/null @@ -1,157 +0,0 @@ -@use '@tehik-ee/tedi-core/mixins'; - -$card-colors: ( - 'accent': '--card-background-accent', - 'brand-primary': '--card-background-brand-primary', - 'brand-secondary': '--card-background-brand-secondary', - 'brand-tertiary': '--card-background-brand-tertiary', - 'brand-quaternary': '--card-background-brand-quaternary', - 'primary': '--card-background-primary', - 'secondary': '--card-background-secondary', - 'tertiary': '--card-background-tertiary', - 'info-primary': '--general-status-info-background-light', - 'info-secondary': '--general-status-info-background-dark', - 'neutral-primary': '--general-status-neutral-background-light', - 'neutral-secondary': '--general-status-neutral-background-dark', - 'success-primary': '--card-background-success', - 'success-secondary': '--general-status-success-background-secondary', - 'danger-primary': '--general-status-danger-background-primary', - 'danger-secondary': '--general-status-danger-background-secondary', - 'warning-primary': '--general-status-warning-background-light', - 'warning-secondary': '--general-status-warning-background-dark', -); - -@mixin border-radius-reset($sides...) { - @each $side in $sides { - border-#{$side}-radius: 0; - - > .tedi-card__content:first-of-type, - > .tedi-card__content:last-child { - border-#{$side}-radius: 0; - } - } -} - -@mixin card-padding { - padding: var(--card-content-padding-top, 1rem) var(--card-content-padding-right, 1rem) - var(--card-content-padding-bottom, 1rem) var(--card-content-padding-left, 1rem); -} - -.tedi-card { - position: relative; - display: flex; - flex-direction: column; - border: 1px solid var(--card-border-primary); - - @include mixins.responsive-styles(border-radius, card-radius-rounded); - - @mixin card-color($name, $color-var) { - &--background--#{$name} { - background-color: var(#{$color-var}); - } - - &--border--#{$name} { - border-color: var(#{$color-var}); - - --card-border-color: var(#{$color-var}); - } - } - - @each $name, $color-var in $card-colors { - @include card-color($name, $color-var); - } - - &--border-left { - border-top-color: var(--card-border-primary); - border-right-color: var(--card-border-primary); - border-bottom-color: var(--card-border-primary); - border-left: 4px solid var(--card-border-color); - } - - &--border-top { - border-top: 4px solid var(--card-border-color); - border-right-color: var(--card-border-primary); - border-bottom-color: var(--card-border-primary); - border-left-color: var(--card-border-primary); - } - - &--borderless { - border: none; - } - - &--no-border-radius-top { - @include border-radius-reset(top-left, top-right); - } - - &--no-border-radius-right { - @include border-radius-reset(top-right, bottom-right); - } - - &--no-border-radius-bottom { - @include border-radius-reset(bottom-right, bottom-left); - } - - &--no-border-radius-left { - @include border-radius-reset(bottom-left, top-left); - } - - &--border-left, - &--border-top { - @media print { - border-color: var(--general-border-secondary); - } - } - - &--background--brand-primary, - &--background--brand-secondary { - color: var(--general-text-white); - } -} - -.tedi-card__content { - flex: 1 1 1px; - - @include card-padding; - - &--separator { - border-bottom: 1px solid var(--card-border-primary); - } -} - -.tedi-card__header { - z-index: 1; - flex: 0; - - @include card-padding; - - @media print { - color: var(--general-text-primary); - background: var(--card-background-primary); - border-bottom: 1px solid var(--card-border-primary); - } -} - -.tedi-card__header:first-of-type, -.tedi-card__content:first-of-type { - @include mixins.responsive-styles(border-start-start-radius, card-radius-rounded); - @include mixins.responsive-styles(border-start-end-radius, card-radius-rounded); - - .tedi-card--border-top & { - border-start-start-radius: 0; - border-start-end-radius: 0; - } -} - -.tedi-card__header:last-child, -.tedi-card__content:last-child { - @include mixins.responsive-styles(border-end-start-radius, card-radius-rounded); - @include mixins.responsive-styles(border-end-end-radius, card-radius-rounded); -} - -.tedi-card__header, -.tedi-card__content { - .tedi-card--border-left & { - border-start-start-radius: 0; - border-end-start-radius: 0; - } -} diff --git a/libs/react-components/src/tedi/components/cards/card/card.spec.tsx b/libs/react-components/src/tedi/components/cards/card/card.spec.tsx deleted file mode 100644 index 9a3f86a85..000000000 --- a/libs/react-components/src/tedi/components/cards/card/card.spec.tsx +++ /dev/null @@ -1,104 +0,0 @@ -import { render, screen } from '@testing-library/react'; - -import { Card } from './card'; -import { CardContent } from './card-content/card-content'; -import { CardContext } from './card-context'; -import { CardHeader } from './card-header/card-header'; - -import '@testing-library/jest-dom'; - -describe('Card Component', () => { - it('renders the Card component with default props', () => { - render( - - -

Card Title

-
- -

Description

-
-
- ); - const card = screen.getByTestId('tedi-card'); - expect(card).toBeInTheDocument(); - expect(card).toHaveClass('tedi-card'); - }); - - it('renders children components correctly', () => { - render( - - -

Title

-
- -

Hello

-
-
- ); - - const cardContent = screen.getByText('Hello'); - expect(cardContent).toBeInTheDocument(); - }); - - it('provides context values to child components', () => { - render( - - - {(value) => ( -
- Padding: {value.padding as number}, Background: {value.background} -
- )} -
-
- ); - - const contextInfo = screen.getByText('Padding: 2, Background: primary'); - expect(contextInfo).toBeInTheDocument(); - }); - - it('applies border styles correctly', () => { - render( - - - - ); - - const card = screen.getByTestId('tedi-card'); - expect(card).toHaveClass('tedi-card tedi-card--border--primary'); - }); - - it('renders without border if `borderless` is true', () => { - render( - - - - ); - - const card = screen.getByTestId('tedi-card'); - expect(card).toHaveClass('tedi-card--borderless'); - }); - - it('applies custom border-radius styles', () => { - render( - - - - ); - - const card = screen.getByTestId('tedi-card'); - expect(card).toHaveClass('tedi-card--no-border-radius-top'); - expect(card).not.toHaveClass('tedi-card--no-border-radius-left'); - }); - - it('combines custom `className` with default styles', () => { - render( - - - - ); - - const card = screen.getByTestId('tedi-card'); - expect(card).toHaveClass('tedi-card custom-class'); - }); -}); diff --git a/libs/react-components/src/tedi/components/cards/card/card.stories.tsx b/libs/react-components/src/tedi/components/cards/card/card.stories.tsx deleted file mode 100644 index 5d8c968d5..000000000 --- a/libs/react-components/src/tedi/components/cards/card/card.stories.tsx +++ /dev/null @@ -1,331 +0,0 @@ -import { Meta, StoryFn, StoryObj } from '@storybook/react'; - -import { Icon } from '../../base/icon/icon'; -import { Heading } from '../../base/typography/heading/heading'; -import { Col, Row } from '../../layout/grid'; -import { Separator } from '../../misc/separator/separator'; -import { StretchContent } from '../../misc/stretch-content/stretch-content'; -import { CardsExample } from '../../misc/stretch-content/stretch-content.stories'; -import { - AlternativeCardsTemplate, - BackgroundColorsTemplate, - CardInfoTemplate, - DefaultCardTemplates, - HeaderTypesTemplate, - SpacingTemplate, -} from './card-stories-templates'; -import { Card, CardContentProps, CardNotificationProps, CardProps } from './index'; - -/** - * Figma ↗
- * Zeroheight ↗ - */ - -export default { - title: 'TEDI-Ready/Components/Cards/Card', - component: Card, - subcomponents: { - 'Card.Content': Card.Content, - 'Card.Header': Card.Header, - 'Card.Notification': Card.Notification, - }, - parameters: { - status: { - type: [{ name: 'breakpointSupport', url: '?path=/docs/helpers-usebreakpointprops--usebreakpointprops' }], - }, - controls: { - exclude: ['sm', 'md', 'lg', 'xl', 'xxl'], - }, - docs: { - source: { - transform: (code: string) => { - return code - .replaceAll('CardContent', 'Card.Content') - .replaceAll('CardHeader', 'Card.Header') - .replaceAll('CardNotification', 'Card.Notification'); - }, - }, - }, - design: { - type: 'figma', - url: 'https://www.figma.com/design/jWiRIXhHRxwVdMSimKX2FF/TEDI-READY-(work-in-progress)?node-id=163-19532&m=dev', - }, - }, -} as Meta; - -export interface CardStory { - card: CardProps; - cardContent: CardContentProps | boolean; - cardHeader: CardContentProps | boolean; - cardNotification: CardNotificationProps | boolean; - cardContent2?: CardContentProps | boolean; - splitContent?: boolean; -} - -type Story = StoryObj; - -const Template: StoryFn = (args) => ( - - Description - -); - -const GeneralTemplate: StoryFn = (args) => { - const getSplitContent = () => ( - - - - - -

Left

-

- Lorem ipsum dolor sit amet, consectetur adipiscing elit. In convallis mollis augue, vitae aliquet elit - congue a. Donec vitae sagittis odio, et maximus nulla. Quisque metus augue, euismod non auctor sed, - consequat in ligula. Pellentesque consectetur, justo in luctus sagittis, metus justo ultricies leo, et - mollis enim ipsum id erat. Pellentesque congue ante metus, ut tempor tortor lobortis non. Proin in - ligula sed ante accumsan viverra. Ut et tempor neque. -

-
-
- - - - - Right - - - -
-
- ); - - const getDefaultContent = (cardContent: CardContentProps) => ( - -

Description

-
- ); - - const getNotification = (notification: CardNotificationProps) => ( - -

Card notification

-
- ); - - const getContent2 = (content: CardContentProps) => ; - - const getCardHeader = (header: CardContentProps) => {header.children}; - - return ( - - {args.cardHeader && getCardHeader(typeof args.cardHeader === 'boolean' ? {} : args.cardHeader)} - {args.cardNotification && - getNotification(typeof args.cardNotification === 'boolean' ? {} : args.cardNotification)} - {args.splitContent - ? getSplitContent() - : args.cardContent === false - ? null - : getDefaultContent(typeof args.cardContent === 'boolean' ? {} : args.cardContent)} - {args.cardContent2 && getContent2(typeof args.cardContent2 === 'boolean' ? {} : args.cardContent2)} - - ); -}; - -export const Default: Story = { - render: Template, -}; - -export const HeaderTypes: Story = { - render: HeaderTypesTemplate, -}; - -export const DefaultCard: Story = { - render: DefaultCardTemplates, -}; - -export const CardInfo: Story = { - render: CardInfoTemplate, -}; - -export const AlternativeCards: Story = { - render: AlternativeCardsTemplate, -}; - -export const Spacing: Story = { - render: SpacingTemplate, -}; - -export const Backgrounds: Story = { - render: BackgroundColorsTemplate, -}; - -export const BorderColors: Story = { - render: GeneralTemplate, -}; - -export const MultipleContent: Story = { - render: GeneralTemplate, - - args: { - ...Default.args, - cardContent: { - hasSeparator: true, - }, - cardContent2: { - children:

Description 2

, - }, - cardHeader: false, - }, -}; - -const SplitCard: StoryFn = () => ( - - - - - -

Left

-

- Lorem ipsum dolor sit amet, consectetur adipiscing elit. In convallis mollis augue, vitae aliquet elit - congue a. Donec vitae sagittis odio, et maximus nulla. Quisque metus augue, euismod non auctor sed, - consequat in ligula. Pellentesque consectetur, justo in luctus sagittis, metus justo ultricies leo, et - mollis enim ipsum id erat. Pellentesque congue ante metus, ut tempor tortor lobortis non. Proin in ligula - sed ante accumsan viverra. Ut et tempor neque. -

-
-
- - - - - Right - - - -
-
-); -export const SplitCardBody: Story = { - render: SplitCard, -}; - -export const Borderless: Story = { - render: GeneralTemplate, - - args: { - ...Default.args, - card: { - borderless: true, - }, - cardHeader: false, - }, -}; - -export const WithoutBorderRadius: Story = { - render: GeneralTemplate, - - args: { - ...Default.args, - card: { - borderRadius: false, - }, - }, -}; - -export const BreakpointProps: Story = { - render: GeneralTemplate, - args: { - card: { - background: 'success-primary', - border: 'brand-primary', - }, - cardContent: { - className: 'test123', - background: undefined, - sm: { - background: 'brand-secondary', - padding: 0, - }, - md: { - background: 'brand-tertiary', - padding: 1, - }, - lg: { - background: undefined, - padding: 1.5, - }, - }, - }, -}; - -export const EqualHeight: StoryObj = { - ...CardsExample, -}; - -export const WithNotification: Story = { - render: GeneralTemplate, - args: { - card: { - padding: 0.75, - }, - cardHeader: { - background: 'primary', - children: Card title, - }, - cardNotification: true, - }, -}; - -const Timeline: StoryFn = (args) => ( - - - - -

Card content

- - - - - -

Card content

- -
-
-
-); - -export const TimelineCard: StoryObj = { - render: Timeline, - args: {}, -}; - -const TwoToned: StoryFn = (_args) => ( - - - - - - - - - - - - - - - - - -

Some statistic: x kg

-

Some description

-
-
- -
-
-); - -export const TwoTonedCard: StoryObj = { - render: TwoToned, - args: {}, -}; diff --git a/libs/react-components/src/tedi/components/cards/card/card.tsx b/libs/react-components/src/tedi/components/cards/card/card.tsx deleted file mode 100644 index 3463e99b3..000000000 --- a/libs/react-components/src/tedi/components/cards/card/card.tsx +++ /dev/null @@ -1,123 +0,0 @@ -import cn from 'classnames'; -import React, { CSSProperties, forwardRef } from 'react'; - -import { BreakpointSupport, useBreakpointProps } from '../../../helpers'; -import styles from './card.module.scss'; -import { CardContent, CardContentProps } from './card-content/card-content'; -import { CardContext } from './card-context'; -import CardHeader from './card-header/card-header'; -import CardNotification from './card-notification/card-notification'; -import { CardBackground, CardBorderType, CardContentPaddingNumber, getCardBorderPlacementColor } from './utility'; - -export type CardContentPadding = - | CardContentPaddingNumber - | { vertical: CardContentPaddingNumber; horizontal: CardContentPaddingNumber } - | { - top: CardContentPaddingNumber; - right: CardContentPaddingNumber; - bottom: CardContentPaddingNumber; - left: CardContentPaddingNumber; - }; -export interface SharedCardProps { - /** - * Additional class. - */ - className?: string; - /** - * Card content padding - * Values can be:
- * - predefined number value in rems
- * - object of separated horizontal and vertical number values in rems - * - object of separated top, right, bottom, left number values in rems - */ - padding?: CardContentPadding; - /** - * Background color. - * @default primary - */ - background?: CardBackground; - /** - * Background image. - */ - backgroundImage?: CSSProperties['backgroundImage']; - /** - * Background position for the image. - */ - backgroundPosition?: CSSProperties['backgroundPosition']; - /** - * Background size for the image. - */ - backgroundSize?: CSSProperties['backgroundSize']; - /** - * Background repeat for the image. - */ - backgroundRepeat?: CSSProperties['backgroundRepeat']; - /** - * Separator. - */ - hasSeparator?: boolean; -} - -type CardBreakpointProps = { - /** - * Additional class. - */ - className?: string; - /** - * Follows the order in border-radius CSS property - * Top-left / Top-right / Bottom-right / Bottom-left - */ - borderRadius?: false | { top?: boolean; right?: boolean; bottom?: boolean; left?: boolean }; - /** - * Remove border from card - */ - borderless?: boolean; - /** - * Type of border - */ - border?: CardBorderType; -} & Pick; - -export interface CardProps extends BreakpointSupport { - children?: React.ReactNode; -} - -const CardComponent = forwardRef((props, ref): JSX.Element => { - const { getCurrentBreakpointProps } = useBreakpointProps(props.defaultServerBreakpoint); - const { children, className, padding, background, borderRadius, borderless, border, ...rest } = - getCurrentBreakpointProps(props, { padding: 1 }); - - const [borderPlacement, borderColor] = getCardBorderPlacementColor(border); - - const cardBEM = cn( - styles['tedi-card'], - { - [styles[`tedi-card--border-${borderPlacement}`]]: borderPlacement, - [styles[`tedi-card--border--${borderColor}`]]: borderColor, - [styles['tedi-card--borderless']]: borderless, - [styles['tedi-card--no-border-radius-top']]: borderRadius === false || borderRadius?.top === false, - [styles['tedi-card--no-border-radius-right']]: borderRadius === false || borderRadius?.right === false, - [styles['tedi-card--no-border-radius-bottom']]: borderRadius === false || borderRadius?.bottom === false, - [styles['tedi-card--no-border-radius-left']]: borderRadius === false || borderRadius?.left === false, - }, - className - ); - - return ( - -
- {children} -
-
- ); -}); - -CardComponent.displayName = 'Card'; - -export const Card = Object.assign(CardComponent, { - Content: CardContent, - Header: CardHeader, - Notification: CardNotification, -}); - -export default Card; diff --git a/libs/react-components/src/tedi/components/cards/card/index.ts b/libs/react-components/src/tedi/components/cards/card/index.ts deleted file mode 100644 index a4faeeecf..000000000 --- a/libs/react-components/src/tedi/components/cards/card/index.ts +++ /dev/null @@ -1,4 +0,0 @@ -export * from './card'; -export * from './card-header/card-header'; -export * from './card-notification/card-notification'; -export * from './card-content/card-content'; diff --git a/libs/react-components/src/tedi/components/cards/card/utility.ts b/libs/react-components/src/tedi/components/cards/card/utility.ts deleted file mode 100644 index a666c74f4..000000000 --- a/libs/react-components/src/tedi/components/cards/card/utility.ts +++ /dev/null @@ -1,78 +0,0 @@ -import { CardProps } from './card'; -import { CardContentProps } from './card-content/card-content'; - -export type CardBorderPlacement = 'top' | 'left'; -export type CardBorderType = `${CardBorderPlacement}-${CardBackground}` | CardBackground; -export type CardContentPaddingNumber = 0 | 0.5 | 0.75 | 1 | 1.5 | 2 | 2.5 | 3; -export type CardBackground = - | 'primary' - | 'secondary' - | 'tertiary' - | 'accent' - | 'brand-primary' - | 'brand-secondary' - | 'brand-tertiary' - | 'brand-quaternary' - | 'danger-primary' - | 'danger-secondary' - | 'success-primary' - | 'success-secondary' - | 'info-primary' - | 'info-secondary' - | 'warning-primary' - | 'warning-secondary' - | 'neutral-primary' - | 'neutral-secondary'; - -export type CardBorderTypeArray = [CardBorderPlacement, CardBackground]; - -/** - * Extracts the border placement and color from the provided border value. - * @param border - The border value from CardProps. - * @returns A tuple [placement, color] or an empty array if border is undefined. - */ -export const getCardBorderPlacementColor = (border?: CardProps['border']): CardBorderTypeArray | [] => { - const borderColor = border?.replace(/(top-)|(left-)/s, '') as CardBackground; - const borderPlacement = border?.replace(new RegExp(`(${borderColor})|-`, 'g'), '') as CardBorderPlacement; - - return [borderPlacement, borderColor]; -}; - -/** - * Generates CSS variables for card content padding based on the provided padding configuration. - * @param padding - The padding configuration from CardContentProps. - * @returns An object with CSS variables for padding. - */ -export const getPaddingCssVariables = (padding: CardContentProps['padding']) => { - const isDirectionObject = ( - value: CardContentProps['padding'] - ): value is { vertical: CardContentPaddingNumber; horizontal: CardContentPaddingNumber } => - typeof value === 'object' && 'vertical' in value && 'horizontal' in value; - - if (typeof padding === 'number') { - return { - '--card-content-padding-top': `${padding}rem`, - '--card-content-padding-right': `${padding}rem`, - '--card-content-padding-bottom': `${padding}rem`, - '--card-content-padding-left': `${padding}rem`, - }; - } - - if (isDirectionObject(padding)) { - const { vertical, horizontal } = padding; - return { - '--card-content-padding-top': `${vertical}rem`, - '--card-content-padding-right': `${horizontal}rem`, - '--card-content-padding-bottom': `${vertical}rem`, - '--card-content-padding-left': `${horizontal}rem`, - }; - } - - const { top = 0, right = 0, bottom = 0, left = 0 } = padding || {}; - return { - '--card-content-padding-top': `${top}rem`, - '--card-content-padding-right': `${right}rem`, - '--card-content-padding-bottom': `${bottom}rem`, - '--card-content-padding-left': `${left}rem`, - }; -}; diff --git a/libs/react-components/src/tedi/components/content/heading-with-icon/heading-with-icon.module.scss b/libs/react-components/src/tedi/components/content/heading-with-icon/heading-with-icon.module.scss deleted file mode 100644 index 35e472c6b..000000000 --- a/libs/react-components/src/tedi/components/content/heading-with-icon/heading-with-icon.module.scss +++ /dev/null @@ -1,8 +0,0 @@ -@use '@tehik-ee/tedi-core/mixins'; - -.tedi-heading-with-icon { - display: flex; - align-items: center; - - @include mixins.responsive-styles(gap, content-heading-inner-spacing-x); -} diff --git a/libs/react-components/src/tedi/components/content/heading-with-icon/heading-with-icon.spec.tsx b/libs/react-components/src/tedi/components/content/heading-with-icon/heading-with-icon.spec.tsx deleted file mode 100644 index e2b93140b..000000000 --- a/libs/react-components/src/tedi/components/content/heading-with-icon/heading-with-icon.spec.tsx +++ /dev/null @@ -1,45 +0,0 @@ -import { render } from '@testing-library/react'; - -import HeadingWithIcon from './heading-with-icon'; - -import '@testing-library/jest-dom'; - -describe('HeadingWithIcon', () => { - it('renders children and icon correctly', () => { - const { container } = render(Child content); - - const iconElement = container.querySelector('.tedi-heading-with-icon .tedi-icon'); - expect(iconElement).toBeInTheDocument(); - expect(iconElement).toHaveClass('tedi-icon--color-primary'); - - const headingElement = container.querySelector('h4.tedi-heading-with-icon'); - expect(headingElement).toBeInTheDocument(); - expect(headingElement).toHaveTextContent('Child content'); - }); - - it('renders the correct heading element based on the element prop', () => { - const { container } = render( - - Child content - - ); - - const headingElement = container.querySelector('h2.tedi-heading-with-icon'); - expect(headingElement).toBeInTheDocument(); - expect(headingElement).toHaveTextContent('Child content'); - }); - - it('applies the correct heading color and icon color', () => { - const { container } = render( - - Child content - - ); - - const headingElement = container.querySelector('.tedi-heading-with-icon'); - expect(headingElement).toHaveClass('tedi-heading-with-icon tedi-text--secondary'); - - const iconElement = container.querySelector('.tedi-icon'); - expect(iconElement).toHaveClass('tedi-icon--color-tertiary'); - }); -}); diff --git a/libs/react-components/src/tedi/components/content/heading-with-icon/heading-with-icon.stories.tsx b/libs/react-components/src/tedi/components/content/heading-with-icon/heading-with-icon.stories.tsx deleted file mode 100644 index 5357b102a..000000000 --- a/libs/react-components/src/tedi/components/content/heading-with-icon/heading-with-icon.stories.tsx +++ /dev/null @@ -1,51 +0,0 @@ -import { Meta, StoryFn, StoryObj } from '@storybook/react'; - -import { VerticalSpacing } from '../../layout/vertical-spacing'; -import HeadingWithIcon, { HeadingWithIconProps } from './heading-with-icon'; - -/** - * Figma ↗
- * Zeroheight ↗ - */ - -export default { - title: 'Tedi-Ready/Content/HeadingWithIcon', - component: HeadingWithIcon, - parameters: { - design: { - type: 'figma', - url: 'https://www.figma.com/file/jWiRIXhHRxwVdMSimKX2FF/TEDI-Design-System-(draft)?type=design&node-id=2137-19827&mode=dev', - }, - }, -} as Meta; -type Story = StoryObj; - -const Template: StoryFn = (args) => ; -const TemplateColors: StoryFn = (args) => { - return ( - - - {args.children} - - {args.children} - - ); -}; - -export const Default: Story = { - render: Template, - args: { - children: 'My family physician', - name: 'assignment_ind', - headingColor: 'brand', - iconColor: 'brand', - }, -}; - -export const Colors: Story = { - render: TemplateColors, - args: { - children: 'My family physician', - name: 'assignment_ind', - }, -}; diff --git a/libs/react-components/src/tedi/components/content/heading-with-icon/heading-with-icon.tsx b/libs/react-components/src/tedi/components/content/heading-with-icon/heading-with-icon.tsx deleted file mode 100644 index 16b034833..000000000 --- a/libs/react-components/src/tedi/components/content/heading-with-icon/heading-with-icon.tsx +++ /dev/null @@ -1,57 +0,0 @@ -import cn from 'classnames'; - -import { Icon, IconColor, IconWithoutBackgroundProps } from '../../base/icon/icon'; -import { Heading, HeadingProps } from '../../base/typography/heading/heading'; -import { TextColor } from '../../base/typography/text/text'; -import styles from './heading-with-icon.module.scss'; - -export interface HeadingWithIconProps extends Omit, Omit { - /** - * Heading text - */ - children: React.ReactNode; - /** - * Additional class - */ - className?: string; - /** - * Heading text color - */ - headingColor?: TextColor; - /** - * Icon color - */ - iconColor?: IconColor; -} - -export const HeadingWithIcon = (props: HeadingWithIconProps): JSX.Element => { - const { - children, - className, - element = 'h4', - name, - size = 24, - headingColor = 'primary', - iconColor = 'primary', - ...rest - } = props; - - const headingProps: HeadingProps = { - children, - element, - className: cn(styles['tedi-heading-with-icon'], className), - color: headingColor, - ...rest, - }; - - const iconProps: IconWithoutBackgroundProps = { name, color: iconColor, size, ...rest }; - - return ( - - {name && } - {children} - - ); -}; - -export default HeadingWithIcon; diff --git a/libs/react-components/src/tedi/components/content/label/label.module.scss b/libs/react-components/src/tedi/components/content/label/label.module.scss deleted file mode 100644 index 634402efe..000000000 --- a/libs/react-components/src/tedi/components/content/label/label.module.scss +++ /dev/null @@ -1,30 +0,0 @@ -@use '@tehik-ee/tedi-core/mixins'; - -.tedi-label { - display: inline; - align-items: center; - font-style: normal; - font-feature-settings: 'clig' off, 'liga' off; - color: var(--general-text-secondary); - - @include mixins.responsive-styles(font-family, family-primary); - @include mixins.responsive-styles(font-size, body-regular-size); - @include mixins.responsive-styles(font-weight, body-regular-weight); - @include mixins.responsive-styles(line-height, body-regular-line-height); - - &--small { - @include mixins.responsive-styles(font-size, body-small-regular-size, $exclude: tablet); - @include mixins.responsive-styles(font-weight, body-small-regular-weight, $exclude: tablet); - } - - &--bold { - @include mixins.responsive-styles(font-weight, body-bold-weight); - @include mixins.responsive-styles(line-height, body-bold-line-height); - } - - &__required { - color: var(--form-general-feedback-error-border); - - @include mixins.responsive-styles(margin-left, content-label-inner-spacing-x); - } -} diff --git a/libs/react-components/src/tedi/components/content/label/label.spec.tsx b/libs/react-components/src/tedi/components/content/label/label.spec.tsx deleted file mode 100644 index bb55e534b..000000000 --- a/libs/react-components/src/tedi/components/content/label/label.spec.tsx +++ /dev/null @@ -1,100 +0,0 @@ -import { fireEvent, render, screen } from '@testing-library/react'; - -import { useBreakpointProps } from '../../../helpers'; -import { Label } from './label'; - -import '@testing-library/jest-dom'; - -jest.mock('../../../helpers', () => ({ - useBreakpointProps: jest.fn(), - useIsTouchDevice: jest.fn(), - useIsMounted: jest.fn(() => true), -})); - -describe('Label component', () => { - beforeEach(() => { - (useBreakpointProps as jest.Mock).mockReturnValue({ - getCurrentBreakpointProps: jest.fn((props) => props), - }); - }); - - it('renders with default props', () => { - const { container } = render(); - const label = container.querySelector('.tedi-label'); - expect(label).toBeInTheDocument(); - expect(label).toHaveTextContent('Label'); - expect(label).not.toHaveClass('tedi-label--bold'); - expect(label).not.toHaveClass('tedi-label--small'); - }); - - it('renders with isBold and required props', () => { - const { container } = render( - - ); - const label = container.querySelector('.tedi-label'); - const required = container.querySelector('.tedi-label__required'); - expect(label).toHaveClass('tedi-label--bold'); - expect(required).toBeInTheDocument(); - }); - - it('renders with isSmall size', () => { - const { container } = render(); - const label = container.querySelector('.tedi-label'); - expect(label).toHaveClass('tedi-label--small'); - }); - - it('renders with a custom class name', () => { - const { container } = render(); - const label = container.querySelector('.tedi-label'); - expect(label).toHaveClass('custom-class'); - }); - - it('handles breakpoint props correctly for isBold', () => { - (useBreakpointProps as jest.Mock).mockReturnValue({ - getCurrentBreakpointProps: jest.fn(() => ({ - isBold: true, - })), - }); - - const { container } = render(); - const label = container.querySelector('.tedi-label'); - expect(label).toHaveClass('tedi-label--bold'); - }); - - it('handles breakpoint props correctly for isSmall', () => { - (useBreakpointProps as jest.Mock).mockReturnValue({ - getCurrentBreakpointProps: jest.fn(() => ({ - isSmall: true, - })), - }); - - const { container } = render(); - const label = container.querySelector('.tedi-label'); - expect(label).toHaveClass('tedi-label--small'); - }); - - it('renders required symbol when required is true', () => { - const { container } = render(); - const required = container.querySelector('.tedi-label__required'); - expect(required).toBeInTheDocument(); - }); - - it('renders an InfoButton when tooltip is provided', () => { - render(); - - const infoButton = screen.getByRole('button'); - expect(infoButton).toBeInTheDocument(); - - fireEvent.mouseEnter(infoButton); - - expect(screen.getByRole('tooltip', { name: 'This is a tooltip' })).toBeInTheDocument(); - }); - - it('does not render InfoButton if tooltip is not provided', () => { - const { queryByRole } = render(); - const infoButton = queryByRole('button'); - expect(infoButton).not.toBeInTheDocument(); - }); -}); diff --git a/libs/react-components/src/tedi/components/content/label/label.stories.tsx b/libs/react-components/src/tedi/components/content/label/label.stories.tsx deleted file mode 100644 index 89b284d8c..000000000 --- a/libs/react-components/src/tedi/components/content/label/label.stories.tsx +++ /dev/null @@ -1,98 +0,0 @@ -import { Meta, StoryFn, StoryObj } from '@storybook/react'; - -import { Label, LabelProps } from './label'; - -/** - * Figma ↗
- * Zeroheight ↗ - */ - -const meta: Meta = { - component: Label, - title: 'TEDI-Ready/Content/Label', - parameters: { - status: { - type: [{ name: 'breakpointSupport', url: '?path=/docs/helpers-usebreakpointprops--usebreakpointprops' }], - }, - controls: { - exclude: ['sm', 'md', 'lg', 'xl', 'xxl'], - }, - design: { - type: 'figma', - url: 'https://www.figma.com/design/jWiRIXhHRxwVdMSimKX2FF/TEDI-READY-(work-in-progress)?node-id=2137-19322&m=dev', - }, - }, -}; - -export default meta; -type Story = StoryObj; - -const Template: StoryFn = (args) =>
} - /> - - - Mari Maasikas - - } - type="horizontal" - /> - - - - ); -}; - -export const Default: Story = { - render: TemplateWithLayouts, - args: { - label: 'Accessibility', - value: Visible to doctor and representative, - }, -}; - -export const Types: Story = { - render: TemplateWithTypes, - args: { - label: 'Accessibility', - value: Visible to doctor and representative, - }, -}; - -export const PositionType: Story = { - render: TemplateWithLayouts, - args: { - label: 'Accessibility', - value: Visible to doctor and representative, - }, -}; - -export const HorizontalLabelLength: Story = { - render: MultipleTextGroupsTemplate, - args: { - type: 'horizontal', - }, -}; - -export const LongTextValues: Story = { - render: TemplateWithLayouts, - args: { - label: 'Accessibility', - labelWidth: '150px', - value: ( - - Lorem ipsum dolor sit amet, consectetur adipiscing elit. Praesent pulvinar malesuada tellus, nec efficitur orci - interdum vitae. Proin semper venenatis est, vel malesuada sapien ornare at. Vestibulum egestas in lectus non - finibus. Donec rhoncus sapien vel justo elementum vestibulum. Vivamus euismod dui vel erat semper luctus. Nulla - egestas purus elit, non fermentum sapien sagittis nec. Pellentesque ac sapien non justo vehicula porta. - - ), - }, -}; diff --git a/libs/react-components/src/tedi/components/content/text-group/text-group.tsx b/libs/react-components/src/tedi/components/content/text-group/text-group.tsx deleted file mode 100644 index ec5294018..000000000 --- a/libs/react-components/src/tedi/components/content/text-group/text-group.tsx +++ /dev/null @@ -1,58 +0,0 @@ -import cn from 'classnames'; -import React from 'react'; - -import { BreakpointSupport, useBreakpointProps } from '../../../helpers'; -import { Label } from '../label/label'; -import styles from './text-group.module.scss'; - -type TextGroupType = 'vertical' | 'horizontal'; - -type TextGroupBreakpointProps = { - /** - * Type of text group layout - */ - type?: TextGroupType; - /** - * Width for the label (e.g., '200px', '30%', etc.) - * @default 'auto' - */ - labelWidth?: string | number; -}; - -export interface TextGroupProps extends BreakpointSupport { - /** - * Label for the text group - */ - label: string; - /** - * Value displayed alongside the label - */ - value: React.ReactNode | React.ReactNode[]; - /** - * Additional class name(s) to apply to the element - */ - className?: string; -} - -export const TextGroup = (props: TextGroupProps): JSX.Element => { - const { getCurrentBreakpointProps } = useBreakpointProps(props.defaultServerBreakpoint); - const { - label, - value, - labelWidth = 'auto', - className, - type = 'vertical', - } = getCurrentBreakpointProps(props); - - const textGroupBEM = cn(styles['tedi-text-group'], styles[`tedi-text-group--${type}`], className); - const labelWidthStyle = typeof labelWidth === 'number' ? `${labelWidth}%` : labelWidth; - - return ( -
-
- -
-
{value}
-
- ); -}; diff --git a/libs/react-components/src/tedi/components/content/truncate/truncate.spec.tsx b/libs/react-components/src/tedi/components/content/truncate/truncate.spec.tsx deleted file mode 100644 index 4f1dccc10..000000000 --- a/libs/react-components/src/tedi/components/content/truncate/truncate.spec.tsx +++ /dev/null @@ -1,56 +0,0 @@ -import { fireEvent, render, screen } from '@testing-library/react'; - -import { Truncate } from './truncate'; - -import '@testing-library/jest-dom'; - -describe('Truncate Component', () => { - const defaultProps = { - children: 'This is a long text that needs to be truncated for testing purposes.', - maxLength: 20, - }; - - it('handles ellipsis rendering', () => { - render(); - const truncatedText = screen.getByText(/This is a long text/); - expect(truncatedText).toBeInTheDocument(); - - if (defaultProps.children.length >= defaultProps.maxLength) { - expect(truncatedText).toHaveTextContent('...'); - } else { - expect(truncatedText).not.toHaveTextContent('...'); - } - }); - - it('renders the full text when expanded', () => { - render(); - - if (defaultProps.children.length >= defaultProps.maxLength) { - const button = screen.getByRole('button'); - fireEvent.click(button); - } - - const fullText = screen.getByText(defaultProps.children); - expect(fullText).toBeInTheDocument(); - expect(fullText).not.toHaveTextContent('...'); - }); - - it('does not render the button when expandable is false', () => { - render(); - - const button = screen.queryByRole('button'); - expect(button).not.toBeInTheDocument(); - }); - - it('renders custom ellipsis if provided', () => { - const ellipsis = '***' as const; - render(); - const truncatedText = screen.getByText(/This is a long text/); - - if (defaultProps.children.length >= defaultProps.maxLength) { - expect(truncatedText).toHaveTextContent(ellipsis); - } else { - expect(truncatedText).not.toHaveTextContent(ellipsis); - } - }); -}); diff --git a/libs/react-components/src/tedi/components/content/truncate/truncate.stories.tsx b/libs/react-components/src/tedi/components/content/truncate/truncate.stories.tsx deleted file mode 100644 index 4c76ed259..000000000 --- a/libs/react-components/src/tedi/components/content/truncate/truncate.stories.tsx +++ /dev/null @@ -1,59 +0,0 @@ -import { Meta, StoryFn, StoryObj } from '@storybook/react'; - -import { Col, Row } from '../../layout/grid'; -import { Truncate } from './truncate'; - -/** - * Figma ↗
- * Zeroheight ↗ - */ - -const meta: Meta = { - component: Truncate, - title: 'Tedi-ready/Content/Truncate', - parameters: { - status: { - type: [{ name: 'breakpointSupport', url: '?path=/docs/helpers-usebreakpointprops--usebreakpointprops' }], - }, - controls: { - exclude: ['sm', 'md', 'lg', 'xl', 'xxl'], - }, - design: { - type: 'figma', - url: 'https://www.figma.com/design/jWiRIXhHRxwVdMSimKX2FF/TEDI-READY-(work-in-progress)?node-id=2427-40830&m=dev', - }, - }, -}; - -export default meta; -type Story = StoryObj; - -const TemplateColumn: StoryFn = (args) => { - return ( - - - - - - ); -}; - -export const Default: Story = { - render: TemplateColumn, - args: { - children: `Lorem ipsum dolor sit amet consectetur adipisicing elit. Alias, maiores! Tempora consequatur eveniet cupiditate. - Aspernatur id quia fugiat, consequatur rerum ipsa ipsam ad suscipit provident odio est commodi velit ut quisquam amet, - harum nisi molestias excepturi sit perferendis, aliquid at consectetur? - Minima quidem cumque eaque eveniet unde esse impedit necessitatibus aut non autem, - maxime sed odit repellat distinctio, molestias laudantium saepe dignissimos eius!`, - expandable: true, - }, -}; - -export const NoTruncate: Story = { - render: TemplateColumn, - args: { - children: 'This text does not get truncated, because the length is smaller than maxLength property.', - maxLength: 100, - }, -}; diff --git a/libs/react-components/src/tedi/components/content/truncate/truncate.tsx b/libs/react-components/src/tedi/components/content/truncate/truncate.tsx deleted file mode 100644 index b3fad8a61..000000000 --- a/libs/react-components/src/tedi/components/content/truncate/truncate.tsx +++ /dev/null @@ -1,82 +0,0 @@ -import React, { ReactNode, useMemo, useState } from 'react'; - -import { BreakpointSupport, useBreakpointProps } from '../../../helpers'; -import { useLabels } from '../../../providers/label-provider'; -import { Text } from '../../base/typography/text/text'; -import Button, { ButtonProps } from '../../buttons/button/button'; - -type TruncateBreakpointProps = { - /** - * Additional class name - */ - className?: string; - /** - * Maximum number of characters to display - * @default 200 - */ - maxLength?: number; -}; - -export interface TruncateProps extends BreakpointSupport { - /** - * Text that will be truncated - */ - children: string; - /** - * Custom content to display at the end of truncated text - * @default '...' - */ - ellipsis?: ReactNode; - /** - * Whether the truncated text should be expandable - * @default true - */ - expandable?: boolean; - /** - * Override default button properties - */ - button?: Partial< - Omit & { onClick: (e: React.MouseEvent, isTruncated: boolean) => void } - >; -} - -export const Truncate = (props: TruncateProps): JSX.Element => { - const { getCurrentBreakpointProps } = useBreakpointProps(props.defaultServerBreakpoint); - const { - children, - className, - maxLength = 200, - ellipsis = '...', - expandable = true, - button, - } = getCurrentBreakpointProps(props); - const { getLabel } = useLabels(); - - const [isTruncated, setIsTruncated] = useState(true); - const truncatedText = useMemo(() => { - const slicedText = children.slice(0, maxLength).trimEnd(); - return slicedText + ellipsis + ' '; - }, [children, maxLength, ellipsis]); - - return ( - - {children.length >= maxLength && isTruncated ? truncatedText : children} - {children.length >= maxLength && expandable && ( - - )} - - ); -}; - -export default Truncate; diff --git a/libs/react-components/src/tedi/components/form/checkbox/checkbox.module.scss b/libs/react-components/src/tedi/components/form/checkbox/checkbox.module.scss deleted file mode 100644 index 83969201c..000000000 --- a/libs/react-components/src/tedi/components/form/checkbox/checkbox.module.scss +++ /dev/null @@ -1,117 +0,0 @@ -@use '@tehik-ee/tedi-core/mixins'; - -.tedi-checkbox { - position: relative; - color: var(--general-text-primary); - - @include mixins.print-grayscale; - - &:hover:not(.checkbox--disabled) { - cursor: pointer; - } - - &--disabled { - color: var(--general-text-disabled); - cursor: not-allowed; - } - - &__input { - @include mixins.visually-hidden; - } - - &__outer-indicator-wrapper { - display: flex; - align-items: center; - - @include mixins.responsive-styles(height, form-checkbox-radio-indicator-container-height); - } - - &__helper { - @include mixins.responsive-styles(padding-left, form-checkbox-radio-size-responsive); - @include mixins.responsive-styles(margin-left, form-checkbox-radio-inner-spacing); - } - - &__icon { - position: absolute; - color: var(--form-checkbox-radio-default-check-indicator-default); - opacity: 0; - transition: opacity 150ms ease; - - [type='checkbox']:checked + .tedi-checkbox__indicator &--check, - .tedi-checkbox__indicator--indeterminate &--indeterminate { - opacity: 1; - } - } - - &__tooltip-icon { - @include mixins.responsive-styles(margin-left, form-checkbox-radio-inner-spacing); - } -} - -.tedi-checkbox__indicator { - position: relative; - display: flex; - align-items: center; - justify-content: center; - background-color: var(--form-checkbox-radio-default-background-default); - border: 1px solid var(--form-checkbox-radio-default-border-default); - transition: border-color 150ms ease; - - @include mixins.responsive-styles(margin-right, form-checkbox-radio-inner-spacing); - @include mixins.responsive-styles(border-radius, form-checkbox-radio-indicator-radius-checkbox); - - &--size-default { - @include mixins.responsive-styles(width, form-checkbox-radio-size-responsive); - @include mixins.responsive-styles(height, form-checkbox-radio-size-responsive); - } - - &--size-large { - @include mixins.responsive-styles(width, form-checkbox-radio-size-large); - @include mixins.responsive-styles(height, form-checkbox-radio-size-large); - } - - &--invalid { - border-color: var(--form-general-feedback-error-border); - } - - .tedi-checkbox__input:not(:disabled) + &:active { - background: var(--form-checkbox-radio-default-background-active); - border-color: var(--form-checkbox-radio-default-border-active); - } - - .tedi-checkbox__input:not(:disabled) + &--hover, - .tedi-checkbox__input:hover:not(:disabled) + &, - .tedi-checkbox__input:not(:disabled) + &:hover, - .tedi-checkbox__input:focus:not(:disabled) + & { - cursor: pointer; - border-color: var(--form-checkbox-radio-default-border-selected); - outline: 1px solid var(--form-checkbox-radio-default-border-selected); - } - - .tedi-checkbox__input:focus-visible:not(:disabled) + & { - border-color: var(--form-checkbox-radio-default-border-selected); - outline: var(--form-checkbox-radio-default-border-selected) solid 2px; - outline-offset: 2px; - } - - [type='checkbox']:checked + &, - &--indeterminate { - background: var(--form-checkbox-radio-default-background-selected); - border-color: var(--form-checkbox-radio-default-border-selected); - } - - .tedi-checkbox__input:disabled + & { - background-color: var(--form-general-background-disabled); - border-color: var(--form-general-border-disabled); - } - - .tedi-checkbox__input:disabled:checked + & { - background-color: var(--form-checkbox-radio-default-background-selected-disabled); - border-color: var(--form-checkbox-radio-default-border-selected-disabled); - - &::after { - background-color: var(--form-checkbox-radio-default-background-selected-disabled); - border-color: var(--form-checkbox-radio-default-background-selected-disabled); - } - } -} diff --git a/libs/react-components/src/tedi/components/form/checkbox/checkbox.spec.tsx b/libs/react-components/src/tedi/components/form/checkbox/checkbox.spec.tsx deleted file mode 100644 index de72e2c29..000000000 --- a/libs/react-components/src/tedi/components/form/checkbox/checkbox.spec.tsx +++ /dev/null @@ -1,213 +0,0 @@ -import { fireEvent, render, screen } from '@testing-library/react'; -import { act, useState } from 'react'; - -import Checkbox from './checkbox'; - -import '@testing-library/jest-dom'; - -jest.mock('../../base/icon/icon', () => ({ - Icon: jest.fn(({ name }) => {name}), -})); - -describe('Checkbox component', () => { - it('renders with default props', () => { - const { container } = render(); - - const input = container.querySelector('input[type="checkbox"]'); - expect(input).toBeInTheDocument(); - expect(input).not.toBeChecked(); - }); - - it('renders with checked prop', () => { - const { container } = render( - - ); - - const input = container.querySelector('input[type="checkbox"]'); - expect(input).toBeChecked(); - }); - - it('renders with disabled prop', () => { - const { container } = render( - - ); - - const input = container.querySelector('input[type="checkbox"]'); - expect(input).toBeDisabled(); - }); - - it('calls onChange when clicked', () => { - const handleChange = jest.fn(); - const { container } = render( - - ); - - const input = container.querySelector('input[type="checkbox"]'); - if (input) { - fireEvent.click(input); - } - expect(handleChange).toHaveBeenCalledWith('check-value', true); - }); - - it('renders with hideLabel prop', () => { - const { container } = render( - - ); - - const hiddenLabel = container.querySelector('label'); - - expect(hiddenLabel).toBeInTheDocument(); - expect(hiddenLabel).toHaveClass('tedi-form-label--hidden'); - }); - - it('renders with tooltip', () => { - const { getByTestId } = render( - - ); - - expect(getByTestId('icon-info')).toBeInTheDocument(); - }); - - it('renders with extra content', () => { - const { getByText } = render( - - ); - - expect(getByText('Extra Content')).toBeInTheDocument(); - }); - - it('handles defaultChecked correctly', () => { - const { container } = render( - - ); - - const input = container.querySelector('input[type="checkbox"]'); - expect(input).toBeChecked(); - }); - - it('changes state when clicked if not controlled', async () => { - const TestComponent = () => { - const [isChecked, setIsChecked] = useState(false); - return ( - setIsChecked(!isChecked)} - /> - ); - }; - - render(); - const checkbox = screen.getByRole('checkbox'); - - expect(checkbox).not.toBeChecked(); - - await act(async () => { - fireEvent.click(checkbox); - }); - - expect(checkbox).toBeChecked(); - }); - - it('does not change state when clicked if controlled', async () => { - const handleChange = jest.fn(); - const { rerender } = render( - - ); - - const checkbox = screen.getByRole('checkbox'); - expect(checkbox).not.toBeChecked(); - - await act(async () => { - fireEvent.click(checkbox); - }); - - expect(checkbox).not.toBeChecked(); - expect(handleChange).toHaveBeenCalledTimes(1); - expect(handleChange).toHaveBeenCalledWith('check-value', true); - - rerender( - - ); - - expect(checkbox).toBeChecked(); - }); - - it('renders with indeterminate state', () => { - const { container } = render( - - ); - - const input = container.querySelector('input[type="checkbox"]'); - expect(input).toHaveAttribute('aria-checked', 'mixed'); - expect(input).not.toBeChecked(); - - const indeterminateIcon = container.querySelector('.tedi-checkbox__indicator--indeterminate'); - expect(indeterminateIcon).toBeInTheDocument(); - }); - - it('renders with indeterminate state and ignores checked', () => { - const { container } = render( - - ); - - const input = container.querySelector('input[type="checkbox"]'); - expect(input).toHaveAttribute('aria-checked', 'mixed'); - expect(input).not.toBeChecked(); - }); - - it('removes indeterminate state when clicked', () => { - const { container, rerender } = render( - - ); - - const input = container.querySelector('input[type="checkbox"]'); - expect(input).toHaveAttribute('aria-checked', 'mixed'); - - if (input) { - fireEvent.click(input); - } - - rerender(); - - expect(input).not.toHaveAttribute('aria-checked', 'mixed'); - }); - - it('calls labelRef.current.click() when clicked', () => { - const { getByTestId } = render( - - ); - - const label = getByTestId('checkbox-label'); - const indicator = getByTestId('checkbox-indicator'); - - jest.spyOn(label, 'click').mockImplementation(() => {}); - - fireEvent.click(indicator); - - expect(label.click).toHaveBeenCalled(); - }); -}); diff --git a/libs/react-components/src/tedi/components/form/checkbox/checkbox.stories.tsx b/libs/react-components/src/tedi/components/form/checkbox/checkbox.stories.tsx deleted file mode 100644 index 0276926a3..000000000 --- a/libs/react-components/src/tedi/components/form/checkbox/checkbox.stories.tsx +++ /dev/null @@ -1,263 +0,0 @@ -import { Meta, StoryFn, StoryObj } from '@storybook/react'; -import React, { useState } from 'react'; - -import { Text } from '../../base/typography/text/text'; -import { Col, Row } from '../../layout/grid'; -import { VerticalSpacing } from '../../layout/vertical-spacing'; -import Alert from '../../notifications/alert/alert'; -import Checkbox, { CheckboxProps } from './checkbox'; - -/** - * Figma ↗
- * Zeroheight ↗

- * In most cases, you should use the `ChoiceGroup` component. However, we also provide a standalone `Check` component for custom use cases. - */ - -const meta: Meta = { - component: Checkbox, - title: 'TEDI-Ready/Components/Form/ChoiceGroup/Checkbox', -}; - -export default meta; -type Story = StoryObj; - -const Template: StoryFn = (args) => ; -const sizesArray: Array<'default' | 'large'> = ['default', 'large']; - -const TemplateSizes: StoryFn = (args) => { - return ( - - - {sizesArray.map((size, key) => ( - - - - {size.charAt(0).toUpperCase() + size.slice(1)} - {size === 'large' && ( - - Applied automatically on mobile screen sizes. - Otherwise, prefer using default size. - - )} - - - - - - - ))} - - - ); -}; - -export const Default: Story = { - render: Template, - - args: { - id: 'default-check', - name: 'default-check', - defaultChecked: true, - }, -}; - -export const Sizes: Story = { - render: TemplateSizes, -}; - -export const States = () => { - const [checked, setChecked] = useState(true); - const [indeterminate, setIndeterminate] = useState(true); - - return ( - - - - - - Default - - - - - - - - Hover - - - - - - - - Selected - - - setChecked(checked)} - /> - - - - - Disabled - - - - - - - - Disabled selected - - - setChecked(checked)} - /> - - - - - Indeterminate - - - { - setIndeterminate(false); - setChecked(checked); - }} - /> - - - - - Error - - - - - - - - - ); -}; - -export const HiddenLabel: Story = { - render: Template, - - args: { - id: 'hidden-label-check', - name: 'hidden-label-check', - hideLabel: true, - }, -}; - -export const WithHelper: Story = { - render: Template, - - args: { - id: 'extra-content-check', - name: 'extra-content-check', - helper: { - text: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis scelerisque quis augue sit amet semper. Donec porttitor mauris neque, quis feugiat erat malesuada ac. Cras vel mauris a est pretium egestas.', - }, - }, -}; - -export const WithTooltip: Story = { - render: (args) => ( - - - - - - - - - - ), - - args: { - name: 'tooltip-check', - tooltip: 'This is a tooltip', - }, -}; - -export const Controlled = () => { - const [checked, setChecked] = React.useState(true); - - return ( - setChecked(checked)} - /> - ); -}; - -export const CheckWithLongTitle = () => { - return ( - - - - - - ); -}; diff --git a/libs/react-components/src/tedi/components/form/checkbox/checkbox.tsx b/libs/react-components/src/tedi/components/form/checkbox/checkbox.tsx deleted file mode 100644 index 25033fc0e..000000000 --- a/libs/react-components/src/tedi/components/form/checkbox/checkbox.tsx +++ /dev/null @@ -1,126 +0,0 @@ -import cn from 'classnames'; -import React from 'react'; - -import { Icon } from '../../base/icon/icon'; -import { Col, Row } from '../../layout/grid'; -import { ChoiceInputProps } from '../choice-input.types'; -import FeedbackText from '../feedback-text/feedback-text'; -import FormLabel from '../form-label/form-label'; -import styles from './checkbox.module.scss'; - -export interface CheckboxProps extends ChoiceInputProps { - /** - * If the check is in indeterminate state. (Not checked or unchecked) - * When this is true then the checked prop is ignored - */ - indeterminate?: boolean; -} - -export const Checkbox = (props: CheckboxProps): JSX.Element => { - const { - id, - label, - value, - className, - disabled = false, - onChange, - hideLabel = false, - helper, - checked, - defaultChecked, - indeterminate, - hover, - name, - tooltip, - invalid, - size = 'default', - ...rest - } = props; - const [innerChecked, setInnerChecked] = React.useState(defaultChecked || false); - const labelRef = React.useRef(null); - - const getChecked = React.useMemo((): boolean | 'mixed' => { - return indeterminate ? 'mixed' : onChange && typeof checked !== 'undefined' ? checked : innerChecked; - }, [indeterminate, onChange, checked, innerChecked]); - - const onChangeHandler = (event: React.ChangeEvent): void => { - if (typeof checked === 'undefined') { - setInnerChecked(event?.target.checked); - } - onChange?.(value, event?.target.checked); - }; - - const helperId = helper ? helper?.id ?? `${id}-helper` : undefined; - const LabelBEM = cn(styles['tedi-checkbox'], { [styles['tedi-checkbox--disabled']]: disabled }); - - return ( -
- - -
- - -
- - - {label && typeof label === 'string' ? ( - - ) : ( - - )} - -
- {helper && ( - - )} -
- ); -}; - -export default Checkbox; diff --git a/libs/react-components/src/tedi/components/form/choice-group/choice-group-context.spec.tsx b/libs/react-components/src/tedi/components/form/choice-group/choice-group-context.spec.tsx deleted file mode 100644 index b908951a4..000000000 --- a/libs/react-components/src/tedi/components/form/choice-group/choice-group-context.spec.tsx +++ /dev/null @@ -1,39 +0,0 @@ -import { render, screen } from '@testing-library/react'; -import { useContext } from 'react'; - -import { ChoiceGroupContext } from './choice-group-context'; - -const TestComponent = () => { - const context = useContext(ChoiceGroupContext); - return ( -
-

{context.name}

-

{context.inputType}

-

{JSON.stringify(context.currentValue)}

-
- ); -}; - -it('uses default ChoiceGroupContext values when no provider is present', () => { - render(); - - expect(screen.getByTestId('name')).toHaveTextContent(''); - expect(screen.getByTestId('inputType')).toHaveTextContent('radio'); - expect(screen.getByTestId('currentValue')).toHaveTextContent('[]'); -}); - -it('renders default ChoiceGroupContext without a provider', () => { - render( - - {(context) => { - expect(context).toEqual({ - name: '', - inputType: 'radio', - onChange: expect.any(Function), - currentValue: [], - }); - return null; - }} - - ); -}); diff --git a/libs/react-components/src/tedi/components/form/choice-group/choice-group-context.ts b/libs/react-components/src/tedi/components/form/choice-group/choice-group-context.ts deleted file mode 100644 index 6af0cd8a3..000000000 --- a/libs/react-components/src/tedi/components/form/choice-group/choice-group-context.ts +++ /dev/null @@ -1,17 +0,0 @@ -import React from 'react'; - -import { ChoiceGroupItemType, ChoiceGroupValue } from './choice-group.types'; - -export interface IChoiceGroupContext { - name: string; - inputType: ChoiceGroupItemType; - currentValue: ChoiceGroupValue; - onChange: (value: string, checked: boolean) => void; -} - -export const ChoiceGroupContext = React.createContext({ - name: '', - inputType: 'radio', - onChange: () => null, - currentValue: [], -}); diff --git a/libs/react-components/src/tedi/components/form/choice-group/choice-group.module.scss b/libs/react-components/src/tedi/components/form/choice-group/choice-group.module.scss deleted file mode 100644 index d704a2dc6..000000000 --- a/libs/react-components/src/tedi/components/form/choice-group/choice-group.module.scss +++ /dev/null @@ -1,23 +0,0 @@ -@use '@tehik-ee/tedi-core/mixins'; -@use '@tehik-ee/tedi-core/bootstrap-utility/breakpoints'; - -.tedi-choice-group { - position: relative; - padding: 0; - margin: 0; - border: none; - - legend { - padding: 0; - } -} - -.tedi-choice-group__inner--indented { - padding-left: calc(var(--_padding-left) - 5px); - - @include mixins.responsive-styles(--_padding-left, form-checkbox-radio-subitem-padding-left); - - @include breakpoints.media-breakpoint-down(md) { - @include mixins.responsive-styles(padding-left, form-checkbox-radio-subitem-padding-left); - } -} diff --git a/libs/react-components/src/tedi/components/form/choice-group/choice-group.spec.tsx b/libs/react-components/src/tedi/components/form/choice-group/choice-group.spec.tsx deleted file mode 100644 index 4e5dbf71b..000000000 --- a/libs/react-components/src/tedi/components/form/choice-group/choice-group.spec.tsx +++ /dev/null @@ -1,166 +0,0 @@ -import { fireEvent, render, screen } from '@testing-library/react'; - -import ChoiceGroup, { ChoiceGroupProps } from './choice-group'; -import { ChoiceGroupContext } from './choice-group-context'; - -import '@testing-library/jest-dom'; - -const defaultProps: ChoiceGroupProps = { - id: 'test-choice-group', - label: 'Test Choice Group', - name: 'test-choice-group', - items: [ - { id: 'option-1', value: 'option-1', label: 'Option 1' }, - { id: 'option-2', value: 'option-2', label: 'Option 2' }, - { id: 'option-3', value: 'option-3', label: 'Option 3', disabled: true }, - ], - inputType: 'checkbox', - onChange: jest.fn(), - color: 'secondary', -}; - -describe('ChoiceGroup Component', () => { - it('renders correctly with required props', () => { - render( - - - - ); - - expect(screen.getByText('Test Choice Group')).toBeInTheDocument(); - expect(screen.getByLabelText('Option 1')).toBeInTheDocument(); - expect(screen.getByLabelText('Option 2')).toBeInTheDocument(); - expect(screen.getByLabelText('Option 3')).toBeDisabled(); - }); - - it('calls onChange when selecting a checkbox', () => { - const onChangeMock = jest.fn(); - - render( - - ); - - const option1 = screen.getByLabelText('Option 1'); - - fireEvent.click(option1); - - expect(onChangeMock).toHaveBeenCalledTimes(1); - expect(onChangeMock).toHaveBeenCalledWith(['option-1']); - }); - - it('supports radio button selection', () => { - render( - - - - ); - - const option1 = screen.getByLabelText('Option 1'); - const option2 = screen.getByLabelText('Option 2'); - - fireEvent.click(option1); - expect(option1).toBeChecked(); - - fireEvent.click(option2); - expect(option1).not.toBeChecked(); - expect(option2).toBeChecked(); - }); - - it('renders helper text', () => { - render( - - - - ); - - expect(screen.getByText('This is a helper message')).toBeInTheDocument(); - }); - - it('renders and toggles the indeterminate checkbox', () => { - const onChangeMock = jest.fn(); - - render( - - ); - - const indeterminateCheckbox = screen.getByLabelText(/table.filter.select-all/i); - expect(indeterminateCheckbox).toBeInTheDocument(); - - fireEvent.click(indeterminateCheckbox); - - expect(onChangeMock).toHaveBeenCalledTimes(1); - }); - - it('returns correct indeterminate label when indeterminateCheck is a string', () => { - render(); - expect(screen.getByText('Custom Label')).toBeInTheDocument(); - }); - - it('calls indeterminateCheck function correctly', () => { - const mockIndeterminateCheck = jest.fn((state) => `State: ${state}`); - render(); - - expect(mockIndeterminateCheck).toHaveBeenCalledWith('none'); - expect(screen.getByText('State: none')).toBeInTheDocument(); - }); - - it('returns remove-all label when all items are selected', () => { - render(); - expect(screen.getByText(/table.filter.remove-all/i)).toBeInTheDocument(); - }); - - it('returns select-all label when no items are selected', () => { - render(); - expect(screen.getByText(/table.filter.select-all/i)).toBeInTheDocument(); - }); - - it('handles indeterminate checkbox change correctly', () => { - const onChangeMock = jest.fn(); - - render(); - - const indeterminateCheckbox = screen.getByLabelText(/table.filter.select-all/i); - fireEvent.click(indeterminateCheckbox); - - expect(onChangeMock).toHaveBeenCalledTimes(1); - }); -}); diff --git a/libs/react-components/src/tedi/components/form/choice-group/choice-group.stories.tsx b/libs/react-components/src/tedi/components/form/choice-group/choice-group.stories.tsx deleted file mode 100644 index 6e3d24074..000000000 --- a/libs/react-components/src/tedi/components/form/choice-group/choice-group.stories.tsx +++ /dev/null @@ -1,478 +0,0 @@ -import { Meta, StoryObj } from '@storybook/react'; -import { useState } from 'react'; - -import { Icon } from '../../base/icon/icon'; -import { Text } from '../../base/typography/text/text'; -import { Col, ColProps, Row } from '../../layout/grid'; -import { VerticalSpacing } from '../../layout/vertical-spacing'; -import Separator from '../../misc/separator/separator'; -import ChoiceGroup from './choice-group'; -import { ChoiceGroupValue } from './choice-group.types'; -import { ExtendedChoiceGroupItemProps } from './components/choice-group-item/choice-group-item'; - -/** - * Radio Figma ↗
- * Radio Zeroheight ↗
- * Checkbox Figma ↗
- * Checkbox Zeroheight ↗

- * The `ChoiceGroup` component manages the state of input elements with either the radio or checkbox role.
- * It provides flexibility to show or hide the `FormLabel` for the fieldset and supports displaying a `FormHelper` to provide additional context or guidance for the entire group. - */ -const meta: Meta = { - component: ChoiceGroup, - title: 'TEDI-Ready/Components/Form/ChoiceGroup/ChoiceGroup', - subcomponents: { 'ChoiceGroup.Item': ChoiceGroup.Item } as never, - parameters: { - docs: { - source: { - transform: (code: string) => { - return code.replaceAll('ChoiceGroupItem', 'ChoiceGroup.Item'); - }, - }, - }, - status: { - type: [{ name: 'breakpointSupport', url: '?path=/docs/helpers-usebreakpointprops--usebreakpointprops' }], - }, - controls: { - exclude: ['sm', 'md', 'lg', 'xl', 'xxl'], - }, - }, -}; - -export default meta; -type Story = StoryObj; - -interface GenerateItemsArgs { - index: number; - inputType?: 'radio' | 'checkbox'; - variant?: 'primary' | 'secondary'; - withHelper?: boolean; - withIndicator?: boolean; - extraLongTitle?: boolean; - tooltip?: boolean; - colProps?: ColProps; - layout?: 'separated' | 'segmented'; - justifyContent?: 'start' | 'center' | 'end' | 'between' | 'around' | 'evenly'; -} - -const generateItems = ({ - index, - inputType = 'radio', - variant = 'primary', - withHelper = false, - withIndicator = false, - tooltip = false, - colProps, - layout, - justifyContent, -}: GenerateItemsArgs): ExtendedChoiceGroupItemProps[] => [ - { - id: `${inputType}-${variant}-value-${index * 10 + 1}-${withHelper}-${withIndicator}-${layout}`, - label: 'Text', - value: `${inputType}-${variant}-value-${index * 10 + 1}-${withHelper}-${withIndicator}-${layout}`, - ...(withHelper && { helper: { text: 'Description' } }), - colProps, - tooltip: tooltip ? 'Tooltip' : undefined, - justifyContent, - }, - { - id: `${inputType}-${variant}-value-${index * 10 + 2}-${withHelper}-${withIndicator}-${layout}`, - label: 'Text', - value: `${inputType}-${variant}-value-${index * 10 + 2}-${withHelper}-${withIndicator}-${layout}`, - ...(withHelper && { helper: { text: 'Description' } }), - colProps, - tooltip: tooltip ? 'Tooltip' : undefined, - justifyContent, - }, - { - id: `${inputType}-${variant}-value-${index * 10 + 3}-${withHelper}-${withIndicator}-${layout}`, - label: 'Text', - value: `${inputType}-${variant}-value-${index * 10 + 3}-${withHelper}-${withIndicator}-${layout}`, - ...(withHelper && { helper: { text: 'Description', type: 'error' } }), - disabled: true, - colProps, - tooltip: tooltip ? 'Tooltip' : undefined, - justifyContent, - }, -]; - -const renderGroup = ( - inputType: 'radio' | 'checkbox', - variant: 'primary' | 'secondary', - withHelper: boolean, - withIndicator: boolean, - layout: 'segmented' | 'separated', - index: number, - justifyContent: 'start' | 'center' | 'end' | 'between' | 'around' | 'evenly' -) => ( - - - - - - - - -); - -const renderChoiceGroups = (inputType: 'radio' | 'checkbox', layout: 'segmented' | 'separated') => ( - - - - Primary - - - Secondary - - - {renderGroup(inputType, 'primary', false, true, layout, 1, 'start')} - {renderGroup(inputType, 'primary', true, true, layout, 2, 'start')} - {inputType !== 'radio' || - (layout !== 'separated' && renderGroup(inputType, 'primary', false, false, layout, 3, 'start'))} - {inputType !== 'radio' || - (layout !== 'separated' && renderGroup(inputType, 'primary', true, false, layout, 4, 'start'))} - -); - -export const Radio: Story = { - args: { - label: 'ChoiceGroup with radios:', - id: 'example-1', - defaultValue: [], - inputType: 'radio', - name: 'radio-1', - items: generateItems({ index: 0 }), - }, -}; - -export const RadioRow: Story = { - args: { - label: 'ChoiceGroup with radios:', - id: 'example-1.2', - defaultValue: [], - inputType: 'radio', - name: 'radio-1.2', - direction: 'row', - items: generateItems({ index: 1 }), - }, -}; - -export const Checkbox: Story = { - args: { - label: 'ChoiceGroup with checkboxes:', - id: 'example-2', - defaultValue: [], - inputType: 'checkbox', - name: 'check-2', - items: generateItems({ index: 2 }), - }, -}; - -export const CheckboxRow: Story = { - args: { - label: 'ChoiceGroup with checkboxes:', - id: 'example-2.1', - defaultValue: [], - inputType: 'checkbox', - name: 'check-2.1', - direction: 'row', - items: generateItems({ index: 33 }), - }, -}; - -export const RadioCardSegmented = () => {renderChoiceGroups('radio', 'segmented')}; -export const RadioCardSeparated = () => {renderChoiceGroups('radio', 'separated')}; -export const CheckboxCard = () => {renderChoiceGroups('checkbox', 'separated')}; - -export const WithError: Story = { - render: function Render(args) { - const [selectedValues, setSelectedValues] = useState([]); - const hasError = Array.isArray(selectedValues) && selectedValues.length === 0; - - const handleChange = (value: ChoiceGroupValue) => { - setSelectedValues(value); - }; - - return ( - - ); - }, - args: { - label: 'Select at least one option:', - id: 'example-with-error', - inputType: 'checkbox', - name: 'with-error', - items: [ - { - id: 'error-option-1', - label: 'Option 1', - value: 'option-1', - }, - { - id: 'error-option-2', - label: 'Option 2', - value: 'option-2', - }, - { - id: 'error-option-3', - label: 'Option 3', - value: 'option-3', - }, - ], - }, -}; - -export const WithDefaultValue: Story = { - args: { - ...Checkbox.args, - label: 'I have the second item selected by default:', - items: generateItems({ index: 13 }), - defaultValue: ['radio-primary-value-132-false-false-undefined'], - }, -}; - -export const WithIndeterminate: Story = { - args: { - ...Checkbox.args, - label: 'I have an indeterminate checkbox:', - items: generateItems({ index: 14 }), - indeterminateCheck: (state) => (state === 'all' ? 'Unselect all' : 'Select all'), - }, -}; - -export const WithExtraContent: Story = { - args: { - inputType: 'checkbox', - label: 'I have extra content after each option:', - id: 'example-extra-content', - name: 'extra-content', - items: [ - { - id: 'extra-content-1', - label: 'Basic plan', - value: 'basic', - helper: { text: 'Includes 5GB storage and basic support' }, - }, - { - id: 'extra-content-2', - label: 'Pro plan', - value: 'pro', - helper: { text: 'Includes 20GB storage and priority support' }, - }, - { - id: 'extra-content-3', - label: 'Enterprise plan', - value: 'enterprise', - helper: { text: 'Includes unlimited storage and 24/7 support' }, - }, - ], - }, -}; - -export const Responsive: Story = { - args: { - inputType: 'radio', - label: 'Choose an option:', - variant: 'card', - showIndicator: true, - layout: 'separated', - color: 'primary', - direction: 'row', - lg: { layout: 'segmented', direction: 'row', color: 'secondary' }, - items: [ - { - id: 'radio-card-1', - label: 'Option 1', - value: 'value-1', - sm: { justifyContent: 'start' }, - lg: { justifyContent: 'center', colProps: { width: 'auto', grow: 1 } }, - }, - { - id: 'radio-card-2', - label: 'Option 2', - value: 'value-2', - defaultChecked: true, - sm: { justifyContent: 'start' }, - lg: { justifyContent: 'center', colProps: { width: 'auto', grow: 1 } }, - }, - { - id: 'radio-card-3', - label: 'Option 3', - value: 'value-3', - sm: { justifyContent: 'start' }, - lg: { justifyContent: 'center', colProps: { width: 'auto', grow: 1 } }, - }, - ], - }, -}; - -export const CustomLabel: Story = { - args: { - inputType: 'checkbox', - direction: 'row', - label: ( - - Custom label - - ), - id: 'custom-label', - name: 'custom-label', - items: generateItems({ index: 16 }), - }, -}; - -export const CustomItemLabels: Story = { - args: { - inputType: 'radio', - label: 'Custom item labels:', - id: 'custom-item-labels', - name: 'custom-item-labels', - items: [ - { - id: 'radio-custom-item-labels-1', - label: ( - - Lorem ipsum dolor sit, amet - - ), - value: 'radio-custom-item-labels-1', - }, - { - id: 'radio-custom-item-labels-2', - label: ( - - - ), - value: 'radio-custom-item-labels-2', - defaultChecked: true, - }, - { - id: 'radio-custom-item-labels-3', - label: ( - - - ), - value: 'radio-custom-item-labels-3', - }, - { - id: 'radio-custom-item-labels-4', - label: 'Lorem ipsum', - value: 'radio-custom-item-labels-4', - }, - ], - }, -}; - -export const CustomItemHTMLLabels: Story = { - args: { - inputType: 'checkbox', - label: 'Custom item HTML labels:', - id: 'custom-item-html-labels', - name: 'custom-item-html-labels', - items: [ - { - id: 'checkbox-custom-item-labels-1', - label: ( -
- Lorem ipsum 1 -
- ), - value: 'checkbox-custom-item-labels-1', - }, - { - id: 'checkbox-custom-item-labels-2', - label: ( -
-
- ), - value: 'checkbox-custom-item-labels-2', - defaultChecked: true, - }, - { - id: 'checkbox-custom-item-labels-3', - label: 'Lorem ipsum 3', - value: 'checkbox-custom-item-labels-3', - }, - { - id: 'checkbox-custom-item-labels-4', - label: 'Lorem ipsum 4', - value: 'checkbox-custom-item-labels-4', - }, - ], - }, -}; diff --git a/libs/react-components/src/tedi/components/form/choice-group/choice-group.tsx b/libs/react-components/src/tedi/components/form/choice-group/choice-group.tsx deleted file mode 100644 index e2ade8d04..000000000 --- a/libs/react-components/src/tedi/components/form/choice-group/choice-group.tsx +++ /dev/null @@ -1,233 +0,0 @@ -import cn from 'classnames'; -import React, { useState } from 'react'; - -import { FeedbackText, FeedbackTextProps } from '../../../../tedi/components/form/feedback-text/feedback-text'; -import FormLabel, { FormLabelProps } from '../../../../tedi/components/form/form-label/form-label'; -import { useLabels } from '../../../../tedi/providers/label-provider'; -import { BreakpointSupport, isBreakpointBelow, useBreakpoint, useBreakpointProps } from '../../../helpers'; -import { Col, Direction, Row, RowProps } from '../../layout/grid'; -import Checkbox, { CheckboxProps } from '../checkbox/checkbox'; -import styles from './choice-group.module.scss'; -import { - ChoiceGroupIndeterminateState, - ChoiceGroupItemColor, - ChoiceGroupItemLayout, - ChoiceGroupItemType, - ChoiceGroupItemVariant, - ChoiceGroupValue, -} from './choice-group.types'; -import { ChoiceGroupContext, IChoiceGroupContext } from './choice-group-context'; -import ChoiceGroupItem, { ExtendedChoiceGroupItemProps } from './components/choice-group-item/choice-group-item'; - -interface ChoiceGroupAllProps extends Omit { - id: string; - items: ExtendedChoiceGroupItemProps[]; - name: string; - label: React.ReactNode | string; - inputType?: ChoiceGroupItemType; - helper?: FeedbackTextProps; - className?: string; - defaultValue?: ChoiceGroupValue; - value?: ChoiceGroupValue; - onChange?: (value: ChoiceGroupValue) => void; - variant?: ChoiceGroupItemVariant; - color?: ChoiceGroupItemColor; - direction?: Direction; - layout?: ChoiceGroupItemLayout; - rowProps?: RowProps; - showIndicator?: boolean; - indeterminateCheck?: boolean | string | ((state: ChoiceGroupIndeterminateState) => string); - indeterminateCheckProps?: { indented?: boolean } & Partial< - Omit - >; -} - -export interface ChoiceGroupProps extends BreakpointSupport {} - -export const ChoiceGroup = (props: ChoiceGroupProps): React.ReactElement => { - const { getLabel } = useLabels(); - const currentBreakpoint = useBreakpoint(props.defaultServerBreakpoint); - const { getCurrentBreakpointProps } = useBreakpointProps(props.defaultServerBreakpoint); - const { - id, - className, - label, - required, - helper, - items, - variant = 'default', - direction = variant === 'default' || isBreakpointBelow(currentBreakpoint, 'md') ? 'column' : 'row', - rowProps, - name, - inputType = 'radio', - value, - defaultValue, - onChange, - hideLabel, - indeterminateCheck, - indeterminateCheckProps = {}, - color, - layout, - showIndicator, - ...rest - } = getCurrentBreakpointProps(props); - - const { ...restIndeterminate } = indeterminateCheckProps; - const isIndented = indeterminateCheckProps?.indented ?? true; - const helperId = helper?.id ?? `${id}-helper`; - const showIndeterminate = indeterminateCheck && inputType === 'checkbox'; - - const [innerValue, setInnerValue] = useState(() => { - if (defaultValue) return defaultValue; - - if (inputType === 'radio') { - const defaultItem = items.find((item) => item.defaultChecked); - return defaultItem ? defaultItem.value : null; - } - - if (inputType === 'checkbox') { - return items.filter((item) => item.defaultChecked).map((item) => item.value); - } - - return null; - }); - - const isValueControlled = (value = props.value): value is ChoiceGroupValue => - !!onChange && typeof value !== 'undefined'; - - const currentValue: ChoiceGroupValue = isValueControlled(value) ? value : innerValue; - - const isNoneSelected = React.useMemo(() => currentValue?.length === 0, [currentValue?.length]); - const isAllSelected = React.useMemo( - () => currentValue?.length === items?.filter((item) => !item.disabled)?.length, - [currentValue?.length, items] - ); - const isSomeSelected = React.useMemo(() => !isNoneSelected && !isAllSelected, [isAllSelected, isNoneSelected]); - - const getIndeterminateLabel = React.useMemo(() => { - const state = isAllSelected ? 'all' : isNoneSelected ? 'none' : 'some'; - - return typeof indeterminateCheck === 'string' - ? indeterminateCheck - : typeof indeterminateCheck === 'function' - ? indeterminateCheck(state) - : indeterminateCheck - ? state === 'all' - ? getLabel('table.filter.remove-all') - : getLabel('table.filter.select-all') - : ''; - }, [getLabel, indeterminateCheck, isAllSelected, isNoneSelected]); - - const onChangeHandler = (value: string, checked: boolean): void => { - let nextValue = currentValue; - - if (inputType === 'checkbox' && nextValue) { - nextValue = checked - ? Array.isArray(nextValue) - ? [...nextValue, value] - : [value] - : Array.isArray(nextValue) - ? nextValue.filter((item) => item !== value) - : nextValue; - } else { - nextValue = value; - } - - if (!isValueControlled()) { - setInnerValue(Array.isArray(nextValue) ? [...nextValue] : nextValue); - } - - onChange?.(nextValue); - }; - - const ContextValue: IChoiceGroupContext = { - name, - inputType, - onChange: onChangeHandler, - currentValue: currentValue, - }; - - const FieldSetBEM = cn(styles['tedi-choice-group'], className); - const CheckGroupBEM = cn(styles['tedi-choice-group__inner'], rowProps?.className, { - [styles['tedi-choice-group__inner--indented']]: showIndeterminate && isIndented, - }); - - const onIndeterminateChangeHandler = (): void => { - const nextValue = !isAllSelected ? items?.filter((item) => !item.disabled)?.map((item) => item.value) : []; - - if (!isValueControlled()) { - setInnerValue([...nextValue]); - } - - onChange?.(nextValue); - }; - - return ( - -
- {label && typeof label === 'string' ? ( - - ) : ( - - )} - - - {items?.length ? ( - <> - {showIndeterminate && ( - - )} - - {items.map((item) => ( - - ))} - - - ) : ( -

{getLabel('table.filter.no-options')}

- )} - -
- {helper && } -
-
- ); -}; - -ChoiceGroup.Item = ChoiceGroupItem; -export default ChoiceGroup; diff --git a/libs/react-components/src/tedi/components/form/choice-group/choice-group.types.ts b/libs/react-components/src/tedi/components/form/choice-group/choice-group.types.ts deleted file mode 100644 index 37a8f98dd..000000000 --- a/libs/react-components/src/tedi/components/form/choice-group/choice-group.types.ts +++ /dev/null @@ -1,6 +0,0 @@ -export type ChoiceGroupItemType = 'radio' | 'checkbox'; -export type ChoiceGroupItemVariant = 'default' | 'card'; -export type ChoiceGroupItemLayout = 'segmented' | 'separated'; -export type ChoiceGroupItemColor = 'primary' | 'secondary'; -export type ChoiceGroupIndeterminateState = 'none' | 'some' | 'all'; -export type ChoiceGroupValue = string | string[] | null; diff --git a/libs/react-components/src/tedi/components/form/choice-group/components/choice-group-item/choice-group-item.module.scss b/libs/react-components/src/tedi/components/form/choice-group/components/choice-group-item/choice-group-item.module.scss deleted file mode 100644 index c1b3f5e8a..000000000 --- a/libs/react-components/src/tedi/components/form/choice-group/components/choice-group-item/choice-group-item.module.scss +++ /dev/null @@ -1,257 +0,0 @@ -@use '@tehik-ee/tedi-core/mixins'; - -.tedi-choice-group-item { - position: relative; - - &--card { - display: flex; - align-items: center; - min-height: 2.5rem; - margin-top: 0; - - @include mixins.responsive-styles(border-radius, form-checkbox-radio-card-radius); - @include mixins.responsive-styles(padding-left, form-checkbox-radio-card-checkbox-padding-x); - @include mixins.responsive-styles(padding-right, form-checkbox-radio-card-checkbox-padding-x); - @include mixins.responsive-styles(padding-top, form-checkbox-radio-card-checkbox-padding-y); - @include mixins.responsive-styles(padding-bottom, form-checkbox-radio-card-checkbox-padding-y); - - &:hover { - cursor: pointer; - } - - label, - &__feedback-text { - color: var(--general-text-secondary); - cursor: unset; - } - - .tedi-choice-group-item__input { - @include mixins.visually-hidden; - } - - &__label { - box-sizing: border-box; - display: block; - width: 100%; - cursor: pointer; - - .tedi-choice-group-item__input:focus + & { - @include mixins.focus-element; - } - } - - &.tedi-choice-group-item--disabled { - background-color: var(--form-general-background-disabled); - border-color: var(--form-general-border-disabled); - - &:hover { - cursor: unset; - } - - label, - .tedi-choice-group-item__feedback-text { - color: var(--general-text-disabled); - cursor: unset; - } - } - - &:has(:focus-visible) { - z-index: 5; - outline: 2px solid var(--form-checkbox-radio-default-border-selected); - outline-offset: 2px; - } - } -} - -.tedi-choice-group-item--card-primary { - background-color: var(--form-checkbox-radio-card-primary-inactive-background); - border: 1px solid var(--form-checkbox-radio-card-primary-inactive-border-group); - - &.tedi-choice-group-item--checkbox { - border-color: var(--filter-primary-inactive-background); - } - - &.tedi-choice-group-item--checked { - background-color: var(--form-checkbox-radio-card-primary-active-background); - border-color: var(--form-checkbox-radio-card-primary-hover-border); - - [type='checkbox']:checked + .tedi-choice-group-item__indicator { - border-color: var(--form-checkbox-radio-default-border-inverted); - } - - [type='radio']:checked + .tedi-choice-group-item__indicator { - background-color: var(--form-checkbox-radio-card-primary-active-background); - border-color: var(--form-checkbox-radio-default-border-inverted); - } - - [type='radio']:checked + .tedi-choice-group-item__indicator::after { - background-color: var(--form-checkbox-radio-default-background-selected-inverted); - border-color: var(--form-checkbox-radio-default-background-inverted); - } - - label, - .tedi-choice-group-item__label, - .tedi-choice-group-item__feedback-text { - color: var(--form-checkbox-radio-card-primary-active-text); - } - } -} - -.tedi-choice-group-item--card-secondary { - background-color: var(--form-checkbox-radio-card-secondary-inactive-background); - border: 1px solid var(--form-checkbox-radio-card-secondary-inactive-border); - - &.tedi-choice-group-item--checked { - outline: 2px solid var(--form-checkbox-radio-card-secondary-active-border); - outline-offset: -2px; - - &:not(:last-child) { - border-right: 2px solid var(--form-checkbox-radio-card-secondary-active-border); - } - - [type='radio']:checked + .tedi-choice-group-item__indicator { - border-color: var(--form-checkbox-radio-default-border-selected); - } - - label, - .tedi-choice-group-item__label, - .tedi-choice-group-item__feedback-text { - color: var(--form-checkbox-radio-card-secondary-active-text); - } - } - - &:has(:focus-visible) { - z-index: 5; - outline: 2px solid var(--form-checkbox-radio-default-border-selected); - outline-offset: 2px; - } -} - -.tedi-choice-group-item--segmented { - .tedi-choice-group-item { - border-radius: 0; - } - - &.tedi-choice-group-item--column { - &:not(:first-of-type) { - margin-top: -1px; - } - - &:not(:first-of-type) .tedi-choice-group-item--disabled { - margin-top: 1px; - border-top-color: var(--form-general-background-disabled); - } - - &:first-of-type .tedi-choice-group-item { - border-top-left-radius: var(--border-radius-default); - border-top-right-radius: var(--border-radius-default); - } - - &:last-of-type .tedi-choice-group-item { - border-bottom-right-radius: var(--border-radius-default); - border-bottom-left-radius: var(--border-radius-default); - } - } - - &.tedi-choice-group-item--row { - &:not(:first-of-type) .tedi-choice-group-item { - margin-left: -1px; - } - - &:not(:first-of-type) .tedi-choice-group-item--disabled { - margin-left: 0; - - &.tedi-choice-group-item--card-primary { - border-left-color: var(--form-checkbox-radio-card-primary-inactive-background); - } - - &.tedi-choice-group-item--card-secondary { - border-left-color: var(--form-checkbox-radio-card-secondary-inactive-background); - } - } - - &:first-of-type .tedi-choice-group-item { - border-top-left-radius: var(--border-radius-default); - border-bottom-left-radius: var(--border-radius-default); - } - - &:last-of-type .tedi-choice-group-item { - border-top-right-radius: var(--border-radius-default); - border-bottom-right-radius: var(--border-radius-default); - } - - .tedi-choice-group-item--disabled.tedi-choice-group-item--card-secondary { - background-color: var(--form-checkbox-radio-card-secondary-inactive-background); - border-color: var(--form-general-border-disabled); - } - } - - .tedi-choice-group-item--card-primary:not(.tedi-choice-group-item--disabled):hover { - background-color: var(--form-checkbox-radio-card-primary-hover-background); - border-color: var(--form-checkbox-radio-card-primary-hover-border); - - label, - .tedi-choice-group-item__label, - .tedi-choice-group-item__feedback-text { - color: var(--form-checkbox-radio-card-primary-hover-text); - } - } - - .tedi-choice-group-item--card-primary .tedi-choice-group-item--checked { - outline: 1px solid var(--form-checkbox-radio-card-primary-selected-background); - } - - .tedi-choice-group-item--card-secondary:not(.tedi-choice-group-item--disabled):hover { - background-color: var(--form-checkbox-radio-card-secondary-hover-background); - border-color: var(--form-checkbox-radio-card-secondary-hover-border); - - label, - .tedi-choice-group-item__label, - .tedi-choice-group-item__feedback-text { - color: var(--form-checkbox-radio-card-secondary-hover-text); - } - } -} - -.tedi-choice-group-item--separated { - .tedi-choice-group-item--card-primary { - border: 1px solid var(--form-checkbox-radio-card-primary-default-background); - - .tedi-choice-group-item--checked { - border: 1px solid var(--form-checkbox-radio-card-primary-selected-background); - } - - &:not(.tedi-choice-group-item--disabled):hover { - background-color: var(--form-checkbox-radio-card-primary-hover-background); - border-color: var(--form-checkbox-radio-card-primary-hover-border); - - label, - .tedi-choice-group-item__label, - .tedi-choice-group-item__feedback-text { - color: var(--form-checkbox-radio-card-primary-hover-text); - } - } - - &.tedi-choice-group-item--radio { - border-color: var(--filter-primary-inactive-background); - } - } - - .tedi-choice-group-item--card-secondary { - &:not(.tedi-choice-group-item--disabled):hover { - background-color: var(--form-checkbox-radio-card-secondary-hover-background); - outline: 2px solid var(--form-checkbox-radio-card-secondary-hover-border); - outline-offset: -2px; - - label, - .tedi-choice-group-item__label, - .tedi-choice-group-item__feedback-text { - color: var(--form-checkbox-radio-card-secondary-hover-text); - } - } - - &.tedi-choice-group-item--disabled { - background-color: var(--form-checkbox-radio-card-secondary-inactive-background); - } - } -} diff --git a/libs/react-components/src/tedi/components/form/choice-group/components/choice-group-item/choice-group-item.spec.tsx b/libs/react-components/src/tedi/components/form/choice-group/components/choice-group-item/choice-group-item.spec.tsx deleted file mode 100644 index 22e97fab7..000000000 --- a/libs/react-components/src/tedi/components/form/choice-group/components/choice-group-item/choice-group-item.spec.tsx +++ /dev/null @@ -1,102 +0,0 @@ -import { fireEvent, render, screen } from '@testing-library/react'; - -import { ChoiceGroupItemType } from '../../choice-group.types'; -import { ChoiceGroupContext } from '../../choice-group-context'; -import ChoiceGroupItem, { ExtendedChoiceGroupItemProps } from './choice-group-item'; - -describe('ChoiceGroupItem', () => { - const defaultProps: ExtendedChoiceGroupItemProps = { - id: 'test-id', - label: 'Test Label', - value: 'test-value', - type: 'radio', - variant: 'default', - color: 'primary', - showIndicator: false, - }; - - const renderWithContext = ( - props = {}, - contextValue = { - currentValue: '', - name: 'test-name', - onChange: jest.fn(), - inputType: 'radio' as ChoiceGroupItemType, - } - ) => { - return render( - - - - ); - }; - - it('renders the radio input correctly', () => { - renderWithContext(); - const radioInput = screen.getByRole('radio', { name: 'Test Label' }); - expect(radioInput).toBeInTheDocument(); - }); - - it('renders the checkbox input correctly when type is checkbox', () => { - renderWithContext({ type: 'checkbox' }); - const checkboxInput = screen.getByRole('checkbox', { name: 'Test Label' }); - expect(checkboxInput).toBeInTheDocument(); - }); - - it('renders the card variant correctly', () => { - renderWithContext({ variant: 'card' }); - const cardInput = screen.getByRole('radio', { name: 'Test Label' }); - expect(cardInput).toBeInTheDocument(); - }); - - it('calls onChange handler when input is clicked', () => { - const onChange = jest.fn(); - renderWithContext({ onChange }, { currentValue: '', name: 'test-name', onChange, inputType: 'radio' }); - const radioInput = screen.getByRole('radio', { name: 'Test Label' }); - fireEvent.click(radioInput); - expect(onChange).toHaveBeenCalledWith('test-value', true); - }); - - it('renders with disabled state correctly', () => { - renderWithContext({ disabled: true }); - const radioInput = screen.getByRole('radio', { name: 'Test Label' }); - expect(radioInput).toBeDisabled(); - }); - - it('renders with helper text correctly', () => { - const helper = { text: 'Helper text', className: 'helper-class' }; - renderWithContext({ helper }); - const helperText = screen.getByText('Helper text'); - expect(helperText).toBeInTheDocument(); - }); - - it('renders with indicator when showIndicator is true', () => { - renderWithContext({ showIndicator: true }); - const indicator = screen.getByTestId('choice-group-item-indicator'); - expect(indicator).toBeInTheDocument(); - }); - - it('calls onChange handler when card input is clicked', () => { - const onChange = jest.fn(); - renderWithContext( - { variant: 'card', onChange }, - { currentValue: '', name: 'test-name', onChange, inputType: 'radio' } - ); - const cardInput = screen.getByRole('radio', { name: 'Test Label' }); - fireEvent.click(cardInput); - expect(onChange).toHaveBeenCalledWith('test-value', true); - }); - - it('renders the card variant with disabled state correctly', () => { - renderWithContext({ variant: 'card', disabled: true }); - const cardInput = screen.getByRole('radio', { name: 'Test Label' }); - expect(cardInput).toBeDisabled(); - }); - - it('renders the card variant with helper text correctly', () => { - const helper = { text: 'Helper text', className: 'helper-class' }; - renderWithContext({ variant: 'card', helper }); - const helperText = screen.getByText('Helper text'); - expect(helperText).toBeInTheDocument(); - }); -}); diff --git a/libs/react-components/src/tedi/components/form/choice-group/components/choice-group-item/choice-group-item.tsx b/libs/react-components/src/tedi/components/form/choice-group/components/choice-group-item/choice-group-item.tsx deleted file mode 100644 index 4f919120b..000000000 --- a/libs/react-components/src/tedi/components/form/choice-group/components/choice-group-item/choice-group-item.tsx +++ /dev/null @@ -1,145 +0,0 @@ -import cn from 'classnames'; -import React from 'react'; - -import { BreakpointSupport, useBreakpointProps } from '../../../../../helpers'; -import { Col, ColProps, Direction } from '../../../../layout/grid'; -import Checkbox from '../../../checkbox/checkbox'; -import { ChoiceInputProps } from '../../../choice-input.types'; -import FeedbackText from '../../../feedback-text/feedback-text'; -import Radio from '../../../radio/radio'; -import { - ChoiceGroupItemColor, - ChoiceGroupItemLayout, - ChoiceGroupItemType, - ChoiceGroupItemVariant, -} from '../../choice-group.types'; -import { ChoiceGroupContext } from '../../choice-group-context'; -import styles from './choice-group-item.module.scss'; - -interface ChoiceGroupItemBreakpointProps extends Omit { - direction?: Direction; - showIndicator?: boolean; - justifyContent?: 'start' | 'center' | 'end' | 'between' | 'around' | 'evenly'; - className?: string; - type?: ChoiceGroupItemType; - variant?: ChoiceGroupItemVariant; - color?: ChoiceGroupItemColor; - colProps?: ColProps; - layout?: ChoiceGroupItemLayout; -} - -export interface ExtendedChoiceGroupItemProps extends BreakpointSupport { - id: string; - label: string | React.ReactNode; - value: string; -} - -export const ChoiceGroupItem = (props: ExtendedChoiceGroupItemProps): React.ReactElement => { - const { getCurrentBreakpointProps } = useBreakpointProps(props.defaultServerBreakpoint); - const { - id, - label, - value, - className, - direction, - disabled, - colProps = direction === 'column' ? { width: 12 } : { width: 'auto' }, - onChange: onChangeItem, - hideLabel, - helper, - tooltip, - type = 'radio', - variant = 'default', - color = 'primary', - layout, - showIndicator, - justifyContent = 'start', - } = getCurrentBreakpointProps(props); - - const { currentValue, name, onChange, inputType } = React.useContext(ChoiceGroupContext); - const isChecked = Array.isArray(currentValue) ? currentValue.includes(value) : value === currentValue; - const defaultChecked = currentValue === undefined ? props.defaultChecked : isChecked; - - const onChangeHandler = (value: string, checked: boolean): void => { - onChange?.(value, checked); - onChangeItem?.(value, checked); - }; - - const ColumnBEM = cn( - styles[`tedi-choice-group-item--${layout === 'separated' ? 'separated' : 'segmented'}`], - direction && styles[`tedi-choice-group-item--${direction}`] - ); - - const ChoiceGroupItemBEM = cn( - styles['tedi-choice-group-item'], - styles[`tedi-choice-group-item--${variant}`], - styles[`tedi-choice-group-item--${variant}-${color}`], - showIndicator && styles['tedi-choice-group-item--indicator'], - type && styles[`tedi-choice-group-item--${type}`], - { [styles['tedi-choice-group-item--disabled']]: disabled }, - { [styles['tedi-choice-group-item--checked']]: isChecked }, - { [`justify-content-${justifyContent}`]: justifyContent } - ); - - const InputComponent = type === 'radio' ? Radio : Checkbox; - - const handleClick = (e: React.MouseEvent) => { - if ((e.target as HTMLElement).tagName === 'LABEL') return; - if (!disabled && variant === 'card') { - onChangeHandler(value, !isChecked); - } - }; - - return ( - -
- {variant === 'default' || showIndicator ? ( - - ) : ( - <> - onChangeHandler(value, e.target.checked)} - className={styles['tedi-choice-group-item__input']} - role={type === 'radio' ? 'radio' : undefined} - aria-checked={isChecked} - /> - - - )} -
- - ); -}; - -export default ChoiceGroupItem; diff --git a/libs/react-components/src/tedi/components/form/choice-group/index.ts b/libs/react-components/src/tedi/components/form/choice-group/index.ts deleted file mode 100644 index 7cd93cd62..000000000 --- a/libs/react-components/src/tedi/components/form/choice-group/index.ts +++ /dev/null @@ -1,4 +0,0 @@ -export * from './choice-group'; -export * from './choice-group-context'; -export * from './choice-group.types'; -export * from './components/choice-group-item/choice-group-item'; diff --git a/libs/react-components/src/tedi/components/form/choice-input.types.ts b/libs/react-components/src/tedi/components/form/choice-input.types.ts deleted file mode 100644 index 74106bbaa..000000000 --- a/libs/react-components/src/tedi/components/form/choice-input.types.ts +++ /dev/null @@ -1,64 +0,0 @@ -import { FeedbackTextProps } from './feedback-text/feedback-text'; - -export interface ChoiceInputProps { - /** - * ID property - */ - id: string; - /** - * Label text - */ - label: string | React.ReactNode; - /** - * Additional classes. - */ - className?: string; - /** - * Value property - */ - value: string; - /** - * name of the input - */ - name: string; - /** - * is the label hidden - */ - hideLabel?: boolean; - /** - * If the option is disabled - */ - disabled?: boolean; - /** - * onChange handler - */ - onChange?: (value: string, checked: boolean) => void; - /** - * Helper text displayed below the input. - */ - helper?: FeedbackTextProps; - /** - * If the check is controlled from outside the components - */ - checked?: boolean; - /** - * If the check is checked by default - */ - defaultChecked?: boolean; - /** - * If the item should be in hover state - */ - hover?: boolean; - /** - * Provide content for tooltip. - */ - tooltip?: string; - /** - * Input size - */ - size?: 'default' | 'large'; - /** - * Whether the input is marked as invalid. - */ - invalid?: boolean; -} diff --git a/libs/react-components/src/tedi/components/form/feedback-text/feedback-text.module.scss b/libs/react-components/src/tedi/components/form/feedback-text/feedback-text.module.scss deleted file mode 100644 index 3f762d04b..000000000 --- a/libs/react-components/src/tedi/components/form/feedback-text/feedback-text.module.scss +++ /dev/null @@ -1,26 +0,0 @@ -@use '@tehik-ee/tedi-core/mixins'; - -.tedi-feedback-text { - color: var(--general-text-tertiary); - - @include mixins.responsive-styles(font-size, body-small-regular-size, $exclude: tablet); - @include mixins.print-grayscale; - - &--valid { - color: var(--general-status-success-text); - } - - &--error { - color: var(--general-status-danger-text); - } - - &--left { - flex-grow: 1; - text-align: left; - } - - &--right { - flex-grow: 0; - text-align: right; - } -} diff --git a/libs/react-components/src/tedi/components/form/feedback-text/feedback-text.spec.tsx b/libs/react-components/src/tedi/components/form/feedback-text/feedback-text.spec.tsx deleted file mode 100644 index 7cbeb8fd9..000000000 --- a/libs/react-components/src/tedi/components/form/feedback-text/feedback-text.spec.tsx +++ /dev/null @@ -1,73 +0,0 @@ -import { render } from '@testing-library/react'; - -import { FeedbackText, FeedbackTextProps } from './feedback-text'; - -import '@testing-library/jest-dom'; - -describe('FeedbackText component', () => { - const defaultProps: FeedbackTextProps = { - text: 'Helper text', - }; - - it('renders with default props', () => { - const { container } = render(); - const helperElement = container.querySelector('div[data-name="feedback-text"]'); - expect(helperElement).toBeInTheDocument(); - expect(helperElement).toHaveClass('tedi-feedback-text'); - expect(helperElement).toHaveClass('tedi-feedback-text--hint'); - expect(helperElement).toHaveClass('tedi-feedback-text--left'); - expect(helperElement).toHaveTextContent('Helper text'); - }); - - it('renders with custom id', () => { - const { container } = render(); - const helperElement = container.querySelector('div[data-name="feedback-text"]'); - expect(helperElement).toBeInTheDocument(); - expect(helperElement).toHaveAttribute('id', 'custom-id'); - }); - - it('renders with additional className', () => { - const { container } = render(); - const helperElement = container.querySelector('div[data-name="feedback-text"]'); - expect(helperElement).toBeInTheDocument(); - expect(helperElement).toHaveClass('custom-class'); - }); - - it('applies the "valid" type style and role="alert"', () => { - const { container } = render(); - const helperElement = container.querySelector('div[data-name="feedback-text"]'); - expect(helperElement).toBeInTheDocument(); - expect(helperElement).toHaveClass('tedi-feedback-text--valid'); - expect(helperElement).toHaveAttribute('role', 'alert'); - }); - - it('applies the "error" type style and role="alert"', () => { - const { container } = render(); - const helperElement = container.querySelector('div[data-name="feedback-text"]'); - expect(helperElement).toBeInTheDocument(); - expect(helperElement).toHaveClass('tedi-feedback-text--error'); - expect(helperElement).toHaveAttribute('role', 'alert'); - }); - - it('renders with "help" type without role attribute', () => { - const { container } = render(); - const helperElement = container.querySelector('div[data-name="feedback-text"]'); - expect(helperElement).toBeInTheDocument(); - expect(helperElement).toHaveClass('tedi-feedback-text--hint'); - expect(helperElement).not.toHaveAttribute('role'); - }); - - it('renders with the "left" position by default', () => { - const { container } = render(); - const helperElement = container.querySelector('div[data-name="feedback-text"]'); - expect(helperElement).toBeInTheDocument(); - expect(helperElement).toHaveClass('tedi-feedback-text--left'); - }); - - it('renders with the "right" position when specified', () => { - const { container } = render(); - const helperElement = container.querySelector('div[data-name="feedback-text"]'); - expect(helperElement).toBeInTheDocument(); - expect(helperElement).toHaveClass('tedi-feedback-text--right'); - }); -}); diff --git a/libs/react-components/src/tedi/components/form/feedback-text/feedback-text.stories.tsx b/libs/react-components/src/tedi/components/form/feedback-text/feedback-text.stories.tsx deleted file mode 100644 index d3d19834e..000000000 --- a/libs/react-components/src/tedi/components/form/feedback-text/feedback-text.stories.tsx +++ /dev/null @@ -1,55 +0,0 @@ -import { Meta, StoryObj } from '@storybook/react'; - -import { FeedbackText } from './feedback-text'; - -/** - * Zeroheight ↗ - */ - -const meta: Meta = { - component: FeedbackText, - title: 'TEDI-Ready/Components/Form/FeedbackText', - parameters: { - status: { - type: ['devComponent'], - }, - }, -}; - -export default meta; -type Story = StoryObj; - -export const Helper: Story = { - args: { - text: 'I am a hint text', - }, -}; - -export const Error: Story = { - args: { - text: 'I am an error text', - type: 'error', - }, -}; - -export const Valid: Story = { - args: { - text: 'I am a valid text', - type: 'valid', - }, -}; - -export const PositionLeft: Story = { - args: { - text: 'I am a hint text', - position: 'left', - }, -}; - -export const PositionRight: Story = { - args: { - text: 'I am a valid text', - type: 'valid', - position: 'right', - }, -}; diff --git a/libs/react-components/src/tedi/components/form/feedback-text/feedback-text.tsx b/libs/react-components/src/tedi/components/form/feedback-text/feedback-text.tsx deleted file mode 100644 index bc9e98f8f..000000000 --- a/libs/react-components/src/tedi/components/form/feedback-text/feedback-text.tsx +++ /dev/null @@ -1,60 +0,0 @@ -import cn from 'classnames'; -import { AriaRole } from 'react'; - -import styles from './feedback-text.module.scss'; - -export type FeedbackTextType = 'hint' | 'valid' | 'error'; -export type FeedbackTextPosition = 'left' | 'right'; - -export interface FeedbackTextProps { - /** - * Helper text - */ - text: string; - /** - * ID to reference the helper from aria-describedby attributes. - * If omitted, then the id might be set through a parent component. - */ - id?: string; - /** - * Additional custom class. - */ - className?: string; - /** - * Type of form-helper. - * @default hint - */ - type?: FeedbackTextType; - /** - * Position of the helper. - * @default left - */ - position?: FeedbackTextPosition; -} - -export const FeedbackText = (props: FeedbackTextProps): JSX.Element => { - const { text, id, className, type = 'hint', position = 'left', ...rest } = props; - - const role: AriaRole | undefined = type === 'valid' || type === 'error' ? 'alert' : undefined; - const ariaLive = type === 'error' || type === 'valid' ? 'assertive' : 'polite'; - - return ( -
- {text} -
- ); -}; - -export default FeedbackText; diff --git a/libs/react-components/src/tedi/components/form/file-dropzone/file-dropzone.module.scss b/libs/react-components/src/tedi/components/form/file-dropzone/file-dropzone.module.scss deleted file mode 100644 index 9b071793c..000000000 --- a/libs/react-components/src/tedi/components/form/file-dropzone/file-dropzone.module.scss +++ /dev/null @@ -1,79 +0,0 @@ -@use '@tehik-ee/tedi-core/mixins'; - -.tedi-file-dropzone { - text-align: center; - cursor: pointer; - background-color: var(--file-dropzone-background-default); - border: 2px dotted var(--file-dropzone-border-default); - transition: border-color 0.2s ease-in-out, background-color 0.2s ease-in-out; - - @include mixins.responsive-styles(border-radius, form-field-radius); - @include mixins.responsive-styles(padding-left, file-dropzone-padding-x); - @include mixins.responsive-styles(padding-right, file-dropzone-padding-x); - @include mixins.responsive-styles(padding-top, file-dropzone-padding-y); - @include mixins.responsive-styles(padding-bottom, file-dropzone-padding-y); - @include mixins.responsive-styles(margin-bottom, layout-grid-gutters-04); - - &:hover { - background-color: var(--file-dropzone-background-hover); - border-color: var(--file-dropzone-border-hover); - } - - .tedi-file-dropzone__label-wrapper { - display: flex; - justify-content: center; - - .tedi-file-dropzone__label { - color: var(--file-dropzone-text-default); - - &:hover { - cursor: pointer; - } - } - } - - &--disabled { - pointer-events: none; - cursor: not-allowed; - background-color: var(--file-dropzone-background-disabled); - border-color: var(--file-dropzone-border-disabled); - - .tedi-file-dropzone__label-wrapper .tedi-file-dropzone__label { - color: var(--general-text-disabled); - } - } - - &--invalid { - border-color: var(--form-general-feedback-error-border); - } - - &--valid { - border-color: var(--form-general-feedback-success-border); - } - - &--drop-over { - color: var(--file-dropzone-text-drop-over); - background-color: var(--file-dropzone-background-drop-over); - border-color: var(--file-dropzone-border-drop-over); - } - - &:focus-visible { - outline: 2px solid var(--file-dropzone-border-hover); - outline-offset: 1px; - } -} - -.tedi-file-dropzone__input { - display: none; -} - -.tedi-file-dropzone__file-list { - width: 100%; - list-style: none; - - @include mixins.responsive-styles(margin-top, layout-grid-gutters-04); -} - -.tedi-file-dropzone__file-name { - overflow-wrap: anywhere; -} diff --git a/libs/react-components/src/tedi/components/form/file-dropzone/file-dropzone.spec.tsx b/libs/react-components/src/tedi/components/form/file-dropzone/file-dropzone.spec.tsx deleted file mode 100644 index 57176cd4c..000000000 --- a/libs/react-components/src/tedi/components/form/file-dropzone/file-dropzone.spec.tsx +++ /dev/null @@ -1,144 +0,0 @@ -import { fireEvent, render, screen } from '@testing-library/react'; -import React from 'react'; -import { useDropzone } from 'react-dropzone'; - -import { useFileUpload } from '../../../helpers/hooks/use-file-upload'; -import { useLabels } from '../../../providers/label-provider'; -import FileDropzone from './file-dropzone'; - -jest.mock('../../../helpers/hooks/use-file-upload'); -jest.mock('../../../providers/label-provider'); - -jest.mock('react-dropzone', () => ({ - useDropzone: jest.fn(), -})); - -describe('FileDropzone', () => { - const mockUseFileUpload = useFileUpload as jest.Mock; - const mockUseLabels = useLabels as jest.Mock; - const mockUseDropzone = useDropzone as jest.Mock; - - beforeEach(() => { - mockUseFileUpload.mockReturnValue({ - innerFiles: [], - uploadErrorHelper: undefined, - onFileChange: jest.fn(), - onFileRemove: jest.fn(), - handleClear: jest.fn(), - fileInputRef: { current: null }, - }); - - mockUseLabels.mockReturnValue({ - getLabel: (key: string) => key, - }); - - mockUseDropzone.mockImplementation((props) => { - return { - getRootProps: jest.fn(() => ({ - className: 'tedi-file-dropzone', - })), - getInputProps: jest.fn(() => ({ - type: 'file', - })), - isDragActive: false, - ...props, - }; - }); - }); - - afterEach(() => { - jest.clearAllMocks(); - }); - - it('renders the dropzone with the correct label', () => { - render(); - expect(screen.getByText('Upload File')).toBeInTheDocument(); - }); - - it('renders the dropzone with a helper text', () => { - render(); - expect(screen.getByText('Helper text')).toBeInTheDocument(); - }); - - it('renders the dropzone with an error message', () => { - mockUseFileUpload.mockReturnValue({ - innerFiles: [], - uploadErrorHelper: { type: 'error', text: 'Error message' }, - onFileChange: jest.fn(), - onFileRemove: jest.fn(), - handleClear: jest.fn(), - fileInputRef: { current: null }, - }); - - render(); - expect(screen.getByText('Error message')).toBeInTheDocument(); - }); - - it('configures useDropzone with correct props and handles file drop', () => { - const onFileChange = jest.fn(); - mockUseFileUpload.mockReturnValue({ - innerFiles: [], - uploadErrorHelper: undefined, - onFileChange, - onFileRemove: jest.fn(), - handleClear: jest.fn(), - fileInputRef: { current: null }, - }); - - render(); - - const useDropzoneMock = useDropzone as jest.Mock; - const dropzoneProps = useDropzoneMock.mock.calls[0][0]; - - expect(dropzoneProps.accept).toEqual({ 'application/*': ['image/png'] }); - expect(dropzoneProps.multiple).toBe(true); - expect(dropzoneProps.maxSize).toBe(5 * 1024 ** 2); - - const file = new File(['file content'], 'file.png', { type: 'image/png' }); - dropzoneProps.onDrop([file]); - - expect(onFileChange).toHaveBeenCalledWith({ - target: { files: [file] }, - } as unknown as React.ChangeEvent); - }); - - it('renders uploaded files', () => { - const file = { id: '1', name: 'file.png', isValid: true }; - mockUseFileUpload.mockReturnValue({ - innerFiles: [file], - uploadErrorHelper: undefined, - onFileChange: jest.fn(), - onFileRemove: jest.fn(), - handleClear: jest.fn(), - fileInputRef: { current: null }, - }); - - render(); - expect(screen.getByText('file.png')).toBeInTheDocument(); - }); - - it('calls onFileRemove when a file is removed', () => { - const onFileRemove = jest.fn(); - const file = { id: '1', name: 'file.png', isValid: true }; - mockUseFileUpload.mockReturnValue({ - innerFiles: [file], - uploadErrorHelper: undefined, - onFileChange: jest.fn(), - onFileRemove, - handleClear: jest.fn(), - fileInputRef: { current: null }, - }); - - render(); - const removeButton = screen.getByRole('button', { name: /clear/i }); - fireEvent.click(removeButton); - - expect(onFileRemove).toHaveBeenCalledWith(file); - }); - - it('disables the dropzone when disabled prop is true', () => { - render(); - const dropzone = screen.getByText('Upload File').closest('.tedi-file-dropzone'); - expect(dropzone).toHaveClass('tedi-file-dropzone--disabled'); - }); -}); diff --git a/libs/react-components/src/tedi/components/form/file-dropzone/file-dropzone.stories.tsx b/libs/react-components/src/tedi/components/form/file-dropzone/file-dropzone.stories.tsx deleted file mode 100644 index 3849c37eb..000000000 --- a/libs/react-components/src/tedi/components/form/file-dropzone/file-dropzone.stories.tsx +++ /dev/null @@ -1,122 +0,0 @@ -import { Meta, StoryFn, StoryObj } from '@storybook/react'; - -import { Col, Row } from '../../layout/grid'; -import { FileDropzone, FileDropzoneProps } from './file-dropzone'; - -/** - * Figma ↗
- * Zeroheight ↗ - */ - -const meta: Meta = { - component: FileDropzone, - title: 'TEDI-Ready/Components/Form/FileDropzone', - args: { - name: 'file-dropzone', - }, -}; - -export default meta; -type Story = StoryObj; - -export const Default: Story = {}; - -const Template: StoryFn = (args) => ( - - - - - -); - -export const WithHint: Story = { - render: Template, - args: { - name: 'file', - helper: { - text: 'JPG, PNG, PDF with size 1MB.', - }, - }, -}; - -export const Disabled: Story = { - render: Template, - args: { - id: 'file-dropzone-disabled', - name: 'file-loading', - label: 'Drop files here', - disabled: true, - }, -}; - -export const Multiple: Story = { - render: Template, - args: { - id: 'file-dropzone-multiple', - name: 'file-multiple', - multiple: true, - defaultFiles: [{ name: 'report.pdf' }, { name: 'report_1.pdf' }, { name: 'report_2.pdf' }], - helper: { - text: 'JPG, PNG, PDF with size 1MB.', - }, - }, -}; - -export const ValidationFailed: Story = { - args: { - id: 'file-dropzone-validation-failed', - name: 'file-validation-failed', - maxSize: 1, - accept: '.pdf,.txt', - multiple: true, - validateIndividually: true, - defaultFiles: [{ name: 'invalid_file.pdf', isValid: false }], - }, - render: (args) => ( - - - - - - ), -}; - -export const MultipleWithIndividualValidation: Story = { - args: { - id: 'file-dropzone-multiple-individual-validation', - name: 'file-multiple-individual-validation', - multiple: true, - maxSize: 0.01, - accept: '.pdf,.txt', - validateIndividually: true, - defaultFiles: [ - { name: 'taotlus_scan_lk_1.pdf' }, - { name: 'taotlus_scan_lk_2.pdf' }, - { name: 'taotlus_scan_lk_3.pdf' }, - { name: 'taotlus_scan_lk_4.pdf' }, - { name: 'taotlus_scan_lk_5.pdf', isValid: false }, - ], - helper: { - text: 'Only .pdf and .txt files under 1KB are allowed.', - type: 'error', - }, - }, - render: (args) => ( - - - { - console.log('Uploaded files:', files); - }} - /> - - - ), -}; diff --git a/libs/react-components/src/tedi/components/form/file-dropzone/file-dropzone.tsx b/libs/react-components/src/tedi/components/form/file-dropzone/file-dropzone.tsx deleted file mode 100644 index e0631cc19..000000000 --- a/libs/react-components/src/tedi/components/form/file-dropzone/file-dropzone.tsx +++ /dev/null @@ -1,115 +0,0 @@ -import cn from 'classnames'; -import React from 'react'; -import { useDropzone } from 'react-dropzone'; - -import { FileUploadFile, useFileUpload, UseFileUploadProps } from '../../../helpers/hooks/use-file-upload'; -import { useLabels } from '../../../providers/label-provider'; -import { Icon } from '../../base/icon/icon'; -import ClosingButton from '../../buttons/closing-button/closing-button'; -import { Card, CardContent } from '../../cards/card'; -import { Col, Row } from '../../layout/grid'; -import { VerticalSpacing } from '../../layout/vertical-spacing'; -import FeedbackText, { FeedbackTextProps } from '../feedback-text/feedback-text'; -import FormLabel, { FormLabelProps } from '../form-label/form-label'; -import styles from './file-dropzone.module.scss'; - -export interface FileDropzoneProps extends Omit, UseFileUploadProps { - /* - * Additional CSS class names to apply to the dropzone for custom styling - */ - className?: string; - /* - * The name attribute for the file input, used for form submission and identifying the field. - */ - name: string; - /* - * The text label displayed for the file dropzone, providing context for users. - */ - label: string; - /* - * Provides helper text or feedback (such as an error or instruction message) to guide the user. - */ - helper?: FeedbackTextProps; - /* - * Disables the file dropzone, preventing user interaction. - */ - disabled?: boolean; -} - -export const FileDropzone = (props: FileDropzoneProps): JSX.Element => { - const { getLabel } = useLabels(); - const { label = getLabel('file-dropzone.label'), className, disabled = false, helper, id } = props; - const { innerFiles, uploadErrorHelper, onFileChange, onFileRemove } = useFileUpload(props); - - const { getRootProps, getInputProps, isDragActive } = useDropzone({ - accept: props.accept ? { 'application/*': [props.accept] } : undefined, - multiple: props.multiple, - maxSize: props.maxSize ? props.maxSize * 1024 ** 2 : undefined, - onDrop: (acceptedFiles) => { - const event = { - target: { files: acceptedFiles }, - } as unknown as React.ChangeEvent; - onFileChange(event); - }, - }); - - const fileDropzoneBEM = cn( - styles['tedi-file-dropzone'], - { [styles['tedi-file-dropzone--disabled']]: disabled }, - { [styles['tedi-file-dropzone--invalid']]: (uploadErrorHelper?.type || helper?.type) === 'error' }, - { [styles['tedi-file-dropzone--valid']]: (uploadErrorHelper?.type || helper?.type) === 'valid' }, - { [styles['tedi-file-dropzone--drop-over']]: isDragActive }, - className - ); - const helperId = helper ? helper?.id ?? `${id}-helper` : undefined; - - return ( - <> -
- -
- - -
-
- {helper ? ( - - ) : uploadErrorHelper ? ( - - ) : null} - {!!innerFiles.length && ( - - {innerFiles.map((file: FileUploadFile) => ( - - - - - {file.name}{' '} - {file.isValid === false && } - - - onFileRemove(file)} /> - - - - - ))} - - )} - - ); -}; - -export default FileDropzone; diff --git a/libs/react-components/src/tedi/components/form/file-upload/examples/multiple-handled.tsx b/libs/react-components/src/tedi/components/form/file-upload/examples/multiple-handled.tsx deleted file mode 100644 index 2430e854f..000000000 --- a/libs/react-components/src/tedi/components/form/file-upload/examples/multiple-handled.tsx +++ /dev/null @@ -1,12 +0,0 @@ -/* istanbul ignore file */ -import { StoryFn } from '@storybook/react'; -import React from 'react'; - -import { FileUploadFile } from '../../../../helpers/hooks/use-file-upload'; -import FileUpload from '../file-upload'; - -export const MultipleHandledTemplate: StoryFn = (args) => { - const [files, setFiles] = React.useState([{ name: 'avaldus.pdf' }]); - - return ; -}; diff --git a/libs/react-components/src/tedi/components/form/file-upload/file-upload.module.scss b/libs/react-components/src/tedi/components/form/file-upload/file-upload.module.scss deleted file mode 100644 index a855c50e7..000000000 --- a/libs/react-components/src/tedi/components/form/file-upload/file-upload.module.scss +++ /dev/null @@ -1,99 +0,0 @@ -@use '@tehik-ee/tedi-core/mixins'; -@use '@tehik-ee/tedi-core/bootstrap-utility/breakpoints'; - -$container-height: 2.5rem; -$container-height-small: 2rem; - -.tedi-file-upload { - display: flex; - align-items: center; - - & input[type='file'] { - display: none; - } -} - -.tedi-file-upload__items { - display: flex; - flex-wrap: wrap; - margin: 0; - - @include mixins.responsive-styles(gap, form-field-inner-spacing); - @include mixins.responsive-styles(padding-top, form-field-inner-spacing); - @include mixins.responsive-styles(padding-bottom, form-field-inner-spacing); - - & li { - word-break: break-word; - overflow-wrap: break-word; - list-style-type: none; - } -} - -.tedi-file-upload__container { - display: flex; - flex-direction: column; - justify-content: center; - background-color: var(--form-input-background-default); - border: 1px solid var(--form-input-border-default); - - @include mixins.responsive-styles(border-radius, form-field-radius); - - &--default { - min-height: $container-height; - - @include mixins.responsive-styles(padding-left, form-field-padding-x-md-default); - @include mixins.responsive-styles(padding-right, form-field-padding-x-md-default); - - .tedi-file-upload__button { - height: $container-height; - } - } - - &--small { - min-height: $container-height-small; - - @include mixins.responsive-styles(padding-left, form-field-padding-x-md-default); - @include mixins.responsive-styles(padding-right, form-field-padding-x-md-default); - @include mixins.responsive-styles(padding-top, form-field-padding-y-xxs); - @include mixins.responsive-styles(padding-bottom, form-field-padding-y-xxs); - - .tedi-file-upload__button { - height: $container-height-small; - - @include mixins.responsive-styles(padding-left, form-field-padding-x-md-default); - } - } - - .tedi-file-upload__button { - padding-right: 0; - padding-left: 0; - - @include breakpoints.media-breakpoint-down(md) { - width: 100%; - margin: 0 auto; - } - } - - &.tedi-file-upload--disabled { - color: var(--form-input-text-disabled); - pointer-events: none; - cursor: initial; - background-color: var(--form-input-background-disabled); - border-color: var(--form-input-border-disabled); - } - - &.tedi-file-upload--error { - border-color: var(--form-general-feedback-error-border); - } - - &.tedi-file-upload--valid { - border-color: var(--form-general-feedback-success-border); - } -} - -.tedi-file-upload__content { - display: flex; - flex-direction: column; - - @include mixins.responsive-styles(gap, form-field-inner-spacing); -} diff --git a/libs/react-components/src/tedi/components/form/file-upload/file-upload.spec.tsx b/libs/react-components/src/tedi/components/form/file-upload/file-upload.spec.tsx deleted file mode 100644 index 96c810c0f..000000000 --- a/libs/react-components/src/tedi/components/form/file-upload/file-upload.spec.tsx +++ /dev/null @@ -1,354 +0,0 @@ -import { fireEvent, render, screen, waitFor } from '@testing-library/react'; -import React from 'react'; - -import { FileUploadFile } from '../../../helpers'; -import FileUpload, { FileUploadProps } from './file-upload'; - -import '@testing-library/jest-dom'; - -jest.mock('../../../providers/label-provider', () => ({ - useLabels: () => ({ getLabel: jest.fn((key) => key) }), -})); - -describe('FileUpload component', () => { - const defaultProps: FileUploadProps = { - id: 'test-file-upload', - name: 'file-upload', - onChange: jest.fn(), - onDelete: jest.fn(), - accept: '.jpg,.png', - multiple: true, - maxSize: 5, - label: 'Upload files', - }; - - afterEach(() => { - jest.clearAllMocks(); - }); - - it('renders the FileUpload component', () => { - render(); - const input = screen.getByLabelText(/Upload files/i); - expect(input).toBeInTheDocument(); - }); - - it('allows file selection', () => { - render(); - const input = screen.getByLabelText(/Upload files/i); - const file = new File(['dummy content'], 'test.jpg', { type: 'image/jpeg' }); - fireEvent.change(input, { target: { files: [file] } }); - expect(defaultProps.onChange).toHaveBeenCalledWith([expect.objectContaining({ name: 'test.jpg' })]); - }); - - it('rejects files with invalid extensions', async () => { - render(); - const input = screen.getByLabelText(/Upload files/i); - const file = new File(['dummy content'], 'test.txt', { type: 'text/plain' }); - fireEvent.change(input, { target: { files: [file] } }); - await waitFor(() => { - expect(defaultProps.onChange).not.toHaveBeenCalled(); - expect(screen.getByText(/file-upload.extension-rejected/i)).toBeInTheDocument(); - }); - }); - - it('rejects files exceeding max size', () => { - render(); - const input = screen.getByLabelText(/Upload files/i); - const file = new File(['a'.repeat(6 * 1024 * 1024)], 'large.jpg', { type: 'image/jpeg' }); - Object.defineProperty(file, 'size', { value: 6 * 1024 * 1024 }); - fireEvent.change(input, { target: { files: [file] } }); - expect(defaultProps.onChange).not.toHaveBeenCalled(); - expect(screen.getByText(/file-upload.size-rejected/i)).toBeInTheDocument(); - }); - - it('does not render close button when there is only one file', () => { - render(); - expect(screen.queryByRole('button', { name: /close/i })).not.toBeInTheDocument(); - }); - - it('renders file list but no input when readOnly is true', () => { - render(); - expect(screen.getByText('test.jpg')).toBeInTheDocument(); - expect(screen.queryByRole('button', { name: /Upload files/i })).not.toBeInTheDocument(); - expect(screen.queryByLabelText(/Upload files/i)).not.toBeInTheDocument(); - }); - - it('clears all files when clear button is clicked', () => { - render(); - const clearButton = screen.getByRole('button', { name: /clear/i }); - fireEvent.click(clearButton); - expect(defaultProps.onChange).toHaveBeenCalledWith([]); - }); - - it('renders the file list correctly', () => { - const files = [ - { name: 'test1.jpg', id: '1' }, - { name: 'test2.png', id: '2' }, - ]; - render(); - files.forEach((file) => { - expect(screen.getByText(file.name)).toBeInTheDocument(); - }); - }); - - it('should return helper text when accept and maxSize are provided', () => { - const props = { ...defaultProps, accept: '.jpg,.png', maxSize: 5 }; - render(); - expect(screen.getByText(/file-upload.accept .jpg, .png/i)).toBeInTheDocument(); - expect(screen.getByText(/file-upload.max-size 5MB/i)).toBeInTheDocument(); - }); - - it('should not return helper text when accept and maxSize are not provided', () => { - const props = { ...defaultProps, accept: undefined, maxSize: undefined }; - render(); - expect(screen.queryByText(/file-upload.accept/i)).not.toBeInTheDocument(); - expect(screen.queryByText(/file-upload.max-size/i)).not.toBeInTheDocument(); - }); - - it('should display error message for files with invalid extensions', () => { - render(); - const input = screen.getByLabelText(/Upload files/i); - const file = new File(['dummy content'], 'test.txt', { type: 'text/plain' }); - fireEvent.change(input, { target: { files: [file] } }); - expect(screen.getByText(/file-upload.extension-rejected/i)).toBeInTheDocument(); - }); - - it('should display error message for files exceeding max size', () => { - render(); - const input = screen.getByLabelText(/Upload files/i); - const file = new File(['a'.repeat(6 * 1024 * 1024)], 'large.jpg', { type: 'image/jpeg' }); - Object.defineProperty(file, 'size', { value: 6 * 1024 * 1024 }); - fireEvent.change(input, { target: { files: [file] } }); - expect(screen.getByText(/file-upload.size-rejected/i)).toBeInTheDocument(); - }); - - it('handles empty accept prop', () => { - const { container } = render(); - const fileInput = container.querySelector('input[type="file"]'); - expect((fileInput as HTMLInputElement)?.accept).toBe(''); - - const file = new File(['test'], 'test.pdf', { type: 'application/pdf' }); - fireEvent.change(fileInput!, { - target: { files: [file] }, - }); - expect(defaultProps.onChange).toHaveBeenCalled(); - }); - - it('handles controlled and uncontrolled state transitions', () => { - const files = [{ name: 'test.pdf', id: '1' }]; - render(); - expect(screen.getByText('test.pdf')).toBeInTheDocument(); - - render(); - const input = screen.getByLabelText(/Upload files/i); - const file = new File(['test'], 'test.pdf', { type: 'application/pdf' }); - fireEvent.change(input, { target: { files: [file] } }); - expect(screen.getByText('test.pdf')).toBeInTheDocument(); - }); - - it('handles multiple error conditions', () => { - render(); - const input = screen.getByLabelText(/Upload files/i); - - const invalidFiles = [ - new File(['test'], 'test.txt', { type: 'text/plain' }), - new File(['a'.repeat(6 * 1024 * 1024)], 'large.pdf', { type: 'application/pdf' }), - ]; - - fireEvent.change(input, { target: { files: invalidFiles } }); - expect(screen.getByText(/file-upload.extension-rejected/i)).toBeInTheDocument(); - expect(screen.getByText(/file-upload.size-rejected/i)).toBeInTheDocument(); - }); - - it('calls handleClear when clicked', () => { - const handleClear = jest.fn(); - render( - - ); - - const clearButton = screen.getByRole('button', { - name: /clear/i, - }); - - expect(clearButton).toBeInTheDocument(); - - fireEvent.click(clearButton); - expect(handleClear).toHaveBeenCalled(); - }); - - it('should use files prop when provided and onChange is defined', () => { - const files = [{ name: 'test.jpg', id: '1' }]; - render(); - expect(screen.getByText('test.jpg')).toBeInTheDocument(); - }); - - it('should use innerFiles when files prop is not provided', () => { - render(); - expect(screen.getByText('test.jpg')).toBeInTheDocument(); - }); - - it('should add valid files individually when validateIndividually is true', () => { - render(); - const input = screen.getByLabelText(/Upload files/i); - const validFile = new File(['dummy content'], 'test.jpg', { type: 'image/jpeg' }); - const invalidFile = new File(['dummy content'], 'test.txt', { type: 'text/plain' }); - fireEvent.change(input, { target: { files: [validFile, invalidFile] } }); - expect(defaultProps.onChange).toHaveBeenCalledWith([ - expect.objectContaining({ - name: 'test.jpg', - isValid: true, - isLoading: false, - }), - expect.objectContaining({ - name: 'test.txt', - isValid: false, - isLoading: false, - }), - ]); - }); - - it('should update innerFiles when files prop is not provided', () => { - render(); - const removeButton = screen.getByRole('button', { name: /clear/i }); - fireEvent.click(removeButton); - expect(screen.queryByText('test.jpg')).not.toBeInTheDocument(); - }); - - it('should render Tag component for each file', () => { - render(); - expect(screen.getByText('test.jpg')).toBeInTheDocument(); - }); - - it('should handle file size validation correctly', () => { - render(); - - const input = screen.getByLabelText(/Upload files/i); - const largeFile = new File(['a'.repeat(6 * 1024 * 1024)], 'large.jpg', { - type: 'image/jpeg', - }); - - fireEvent.change(input, { target: { files: [largeFile] } }); - - expect(defaultProps.onChange).not.toHaveBeenCalled(); - expect(screen.getByText(/file-upload.size-rejected/i)).toBeInTheDocument(); - }); - - it('should update innerFiles when files prop is not provided', () => { - render(); - - const removeButton = screen.getByRole('button', { name: /clear/i }); - fireEvent.click(removeButton); - - expect(screen.queryByText('test.jpg')).not.toBeInTheDocument(); - }); - - it('should call onChange with the updated list of files', () => { - const onChange = jest.fn(); - render(); - - const removeButton = screen.getByRole('button', { name: /clear/i }); - fireEvent.click(removeButton); - - expect(onChange).toHaveBeenCalledWith([]); - }); - - it('should use files prop when files and onChange are provided', () => { - const files = [{ name: 'test.jpg', id: '1' }]; - render(); - expect(screen.getByText('test.jpg')).toBeInTheDocument(); - }); - - it('renders Tag component with onClose prop when file is valid and not read-only', () => { - const file = { name: 'test.jpg', id: '1', isValid: true, isLoading: false }; - render(); - - const closeButton = screen.getByRole('button', { name: /clear/i }); - expect(closeButton).toBeInTheDocument(); - }); - - it('does not render onClose prop for Tag when file is disabled and read-only', () => { - const file = { name: 'test.jpg', id: '1', isValid: true, isLoading: true }; - render(); - - const closeButton = screen.queryByRole('button', { name: /clear/i }); - expect(closeButton).not.toBeInTheDocument(); - }); - - it('does not render onClose prop for Tag when file is disabled', () => { - const file = { name: 'test.jpg', id: '1', isValid: true, isLoading: false }; - render(); - - const closeButton = screen.queryByRole('button', { name: /clear/i }); - expect(closeButton).not.toBeInTheDocument(); - }); - - it('does not render onClose prop for Tag when file is read-only', () => { - const file = { name: 'test.jpg', id: '1', isValid: true, isLoading: false }; - render(); - - const closeButton = screen.queryByRole('button', { name: /clear/i }); - expect(closeButton).not.toBeInTheDocument(); - }); - - it('renders single file name using Text component', () => { - const file = { name: 'test.jpg', id: '1' }; - render(); - - const fileName = screen.getByText('test.jpg'); - expect(fileName).toBeInTheDocument(); - expect(fileName.tagName.toLowerCase()).toBe('p'); - }); - - it('triggers file input click when the "Add" button is clicked', () => { - render(); - - const fileInput = screen.getByLabelText(/Upload files/i) as HTMLInputElement; - const clickSpy = jest.spyOn(fileInput, 'click'); - - const addButton = screen.getByRole('button', { name: /file-upload.add/i }); - fireEvent.click(addButton); - - expect(clickSpy).toHaveBeenCalled(); - }); - - it('handles controlled files prop correctly', () => { - const files = [{ name: 'controlled.jpg', id: '1', isValid: true }]; - render(); - - expect(screen.getByText('controlled.jpg')).toBeInTheDocument(); - }); - - it('handles controlled mode with file uploads and rejections', async () => { - const ControlledWrapper = ({ initialFiles }: { initialFiles: FileUploadFile[] }) => { - const [files, setFiles] = React.useState(initialFiles); - return ; - }; - - const initialFiles = [{ name: 'initial.jpg', id: '1', isValid: true }]; - render(); - - expect(screen.getByText('initial.jpg')).toBeInTheDocument(); - - const input = screen.getByLabelText(/Upload files/i); - const validFile = new File(['dummy content'], 'new.jpg', { type: 'image/jpeg' }); - const invalidFile = new File(['dummy content'], 'test.txt', { type: 'text/plain' }); - fireEvent.change(input, { target: { files: [validFile, invalidFile] } }); - - await waitFor(() => { - expect(screen.getByText('new.jpg')).toBeInTheDocument(); - expect(screen.getByText(/file-upload.extension-rejected/i)).toBeInTheDocument(); - }); - - const clearButton = screen.getByRole('button', { name: /clear/i }); - fireEvent.click(clearButton); - await waitFor(() => { - expect(screen.queryByText('initial.jpg')).not.toBeInTheDocument(); - expect(screen.queryByText('new.jpg')).not.toBeInTheDocument(); - }); - }); -}); diff --git a/libs/react-components/src/tedi/components/form/file-upload/file-upload.stories.tsx b/libs/react-components/src/tedi/components/form/file-upload/file-upload.stories.tsx deleted file mode 100644 index b6ea36a9b..000000000 --- a/libs/react-components/src/tedi/components/form/file-upload/file-upload.stories.tsx +++ /dev/null @@ -1,268 +0,0 @@ -import { Meta, StoryFn, StoryObj } from '@storybook/react'; -import React from 'react'; - -import { FileUploadFile } from '../../../helpers'; -import { Text } from '../../base/typography/text/text'; -import Button from '../../buttons/button/button'; -import { Col, Row } from '../../layout/grid'; -import { MultipleHandledTemplate } from './examples/multiple-handled'; -import FileUpload, { FileUploadProps } from './file-upload'; - -/** - * Figma ↗
- * Zeroheight ↗ - */ - -const meta: Meta = { - component: FileUpload, - title: 'TEDI-Ready/Components/Form/FileUpload', - parameters: { - design: { - type: 'figma', - url: 'https://www.figma.com/design/jWiRIXhHRxwVdMSimKX2FF/TEDI-READY-(work-in-progress)?node-id=4536-78765&m=dev', - }, - }, -}; - -export default meta; -type Story = StoryObj; - -const sizesArray: Array<'default' | 'small'> = ['default', 'small']; - -const TemplateSizes: StoryFn = (args) => { - return ( -
- {sizesArray.map((size, key) => ( - - - {size.charAt(0).toUpperCase() + size.slice(1)} - - - - - - ))} -
- ); -}; - -export const Default: Story = { - args: { - id: 'file-upload', - name: 'file', - label: 'Upload file', - }, -}; - -export const Sizes: Story = { - render: TemplateSizes, - args: { - label: 'Label', - }, -}; - -export const WithHint: Story = { - args: { - id: 'file-upload', - name: 'file', - label: 'Upload file', - helper: { - text: 'JPG, PNG, PDF with size 0.001MB.', - }, - }, -}; - -export const Disabled: Story = { - args: { - id: 'file-upload-disabled', - name: 'file-loading', - label: 'Upload file', - defaultFiles: [{ name: 'report.pdf' }], - disabled: true, - }, -}; - -export const ValidationFailed: Story = { - args: { - id: 'file-upload-validation-failed', - name: 'file-validation-failed', - label: 'Upload file', - maxSize: 0.001, - accept: '.pdf,.txt', - multiple: true, - validateIndividually: true, - defaultFiles: [{ name: 'taotlus_scan_lk_1.pdf', isValid: false }], - }, - render: (args) => ( - - ), -}; - -export const ValidationSuccess: Story = { - args: { - id: 'file-upload-validation-failed', - name: 'file-validation-failed', - label: 'Upload file', - maxSize: 0.001, - accept: '.pdf,.txt', - multiple: true, - validateIndividually: true, - defaultFiles: [{ name: 'taotlus_scan_lk_1.pdf', isValid: false }], - }, - render: (args) => ( - - ), -}; - -export const MultipleWithIndividualValidation: Story = { - args: { - id: 'file-upload-multiple-individual-validation', - name: 'file-multiple-individual-validation', - label: 'Upload files', - multiple: true, - maxSize: 0.01, - accept: '.pdf,.txt', - validateIndividually: true, - hasClearButton: true, - defaultFiles: [ - { name: 'taotlus_scan_lk_1.pdf' }, - { name: 'taotlus_scan_lk_2.pdf' }, - { name: 'taotlus_scan_lk_3.pdf' }, - { name: 'taotlus_scan_lk_4.pdf' }, - { name: 'taotlus_scan_lk_5.pdf', isValid: false }, - ], - helper: { - text: 'Only .pdf and .txt files under 1KB are allowed.', - }, - }, - render: (args) => ( - { - console.log('Uploaded files:', files); - }} - helper={{ - type: 'error', - text: 'Invalid file uploaded. Only .pdf and .txt files are allowed, and must be under 1KB.', - }} - /> - ), -}; - -export const LoadingState: Story = { - args: { - id: 'file-upload-loading', - name: 'file-loading', - label: 'Upload file', - defaultFiles: [{ name: 'report.pdf', isLoading: true }, { name: 'report_1.pdf' }], - }, -}; - -export const Multiple: Story = { - args: { - id: 'file-upload-MULTIPLE', - name: 'file-multiple', - label: 'Upload file', - multiple: true, - defaultFiles: [{ name: 'report.pdf' }, { name: 'report_1.pdf' }, { name: 'report_2.pdf' }], - helper: { - text: 'JPG, PNG, PDF with size 0.001MB.', - }, - }, -}; - -export const MultipleHandled: Story = { - render: MultipleHandledTemplate, - args: { - id: 'file-upload-handled', - name: 'file-loading-handled', - label: 'Upload file', - multiple: true, - onDelete: (file) => { - console.log(`Deleted - ${file.name}`); - }, - }, -}; - -export const ReadOnlyFiles: Story = { - args: { - id: 'file-upload-read-only', - name: 'file-loading', - label: 'Upload file', - defaultFiles: [{ name: 'report.pdf' }, { name: 'report_1.pdf' }, { name: 'report_2.pdf' }], - onChange: (files) => { - console.log(files); - }, - readOnly: true, - }, -}; - -export const PdfAndTxtOnly: Story = { - args: { - id: 'file-upload-accepts', - name: 'file-accepts', - label: 'Upload file', - accept: '.pdf,.txt', - }, -}; - -export const SizeLimited: Story = { - args: { - id: 'file-upload-size-limited', - name: 'file-size-limited', - label: 'Upload file', - maxSize: 0.001, - multiple: true, - }, -}; - -export const ExtensionAndSizeLimit: Story = { - args: { - id: 'file-upload-size-extension-limited', - name: 'file-size-extension-limited', - label: 'Upload file', - maxSize: 0.001, - accept: '.pdf,.txt', - multiple: true, - }, -}; - -export const ControlledClearing: Story = { - render: (args) => { - const [files, setFiles] = React.useState([ - { name: 'report.pdf' }, - { name: 'report_1.pdf' }, - { name: 'report_2.pdf' }, - ]); - - return ( - <> - setFiles(f)} multiple /> - -
- - -
Files: {JSON.stringify(files, null, 2)}
-
- - ); - }, - args: { - id: 'file-upload-clear-controlled', - name: 'file-upload-clear-controlled', - label: 'Upload file', - hasClearButton: true, - }, -}; diff --git a/libs/react-components/src/tedi/components/form/file-upload/file-upload.tsx b/libs/react-components/src/tedi/components/form/file-upload/file-upload.tsx deleted file mode 100644 index 9ed237d8e..000000000 --- a/libs/react-components/src/tedi/components/form/file-upload/file-upload.tsx +++ /dev/null @@ -1,248 +0,0 @@ -import cn from 'classnames'; -import React from 'react'; - -import { isBreakpointBelow, useBreakpoint } from '../../../helpers'; -import { FileUploadFile, useFileUpload } from '../../../helpers/hooks/use-file-upload'; -import { useLabels } from '../../../providers/label-provider'; -import { Text } from '../../base/typography/text/text'; -import { Button } from '../../buttons/button/button'; -import { ClosingButton } from '../../buttons/closing-button/closing-button'; -import { FormLabel, FormLabelProps } from '../../form/form-label/form-label'; -import { Col, Row } from '../../layout/grid'; -import Separator from '../../misc/separator/separator'; -import { Tag } from '../../tags/tag/tag'; -import { FeedbackText, FeedbackTextProps } from '../feedback-text/feedback-text'; -import styles from './file-upload.module.scss'; - -export interface FileUploadProps extends FormLabelProps { - /** - * Additional class names for styling the component. - */ - className?: string; - - /** - * The name of the file input field, used for form submission and accessibility. - */ - name: string; - - /** - * A helper text or error message to display below the file upload field. - */ - helper?: FeedbackTextProps; - - /** - * Specifies the allowed file types (e.g., "image/png, image/jpeg"). - */ - accept?: string; - - /** - * Allows multiple file selection if `true`. Defaults to `false`. - */ - multiple?: boolean; - - /** - * Callback function triggered when files are added or changed. - */ - onChange?: (files: FileUploadFile[]) => void; - - /** - * An array of preloaded files that appear in the upload field by default. - */ - defaultFiles?: FileUploadFile[]; - - /** - * Callback function triggered when a file is removed. - */ - onDelete?: (file: FileUploadFile) => void; - - /** - * Determines whether a "Clear" button is shown to remove all files. - */ - hasClearButton?: boolean; - - /** - * A controlled list of uploaded files. If provided, `onChange` should be used to update them. - */ - files?: FileUploadFile[]; - - /** - * If `true`, prevents file selection and removal, making the field read-only. - */ - readOnly?: boolean; - - /** - * Disables the file upload field, preventing interactions. - */ - disabled?: boolean; - - /** - * Maximum allowed file size in bytes. - */ - maxSize?: number; - - /** - * If `true`, validates each file separately instead of rejecting all at once. - */ - validateIndividually?: boolean; - - /** - * Determines the visual size of the file upload field. Defaults to `"default"`. - */ - size?: 'small' | 'default'; -} - -export const FileUpload = (props: FileUploadProps): JSX.Element => { - const { getLabel } = useLabels(); - const { - id, - name, - accept, - multiple, - onChange, - className, - defaultFiles, - onDelete, - hasClearButton = true, - files, - readOnly, - disabled = false, - maxSize, - validateIndividually = false, - size = 'default', - helper, - ...rest - } = props; - - const { innerFiles, uploadErrorHelper, onFileChange, onFileRemove, handleClear } = useFileUpload({ - accept, - maxSize, - multiple, - validateIndividually, - defaultFiles, - onChange, - onDelete, - files, - }); - - const currentBreakpoint = useBreakpoint(); - - const fileUploadBEM = cn(styles['tedi-file-upload'], { [styles['tedi-file-upload--disabled']]: disabled }, className); - const helperId = helper ? helper?.id ?? `${id}-helper` : undefined; - - const getFiles = React.useMemo(() => { - return !!files && !!onChange ? files : innerFiles; - }, [files, innerFiles, onChange]); - - const getFileElement = (file: FileUploadFile, index: number) => { - return ( -
  • - onFileRemove(file) : undefined} - isLoading={file.isLoading} - > - {file.name} - -
  • - ); - }; - - const showFiles = () => { - if (getFiles.length > 1) { - return ( -
      - {getFiles.map((file, index) => getFileElement(file, index))} -
    - ); - } else if (getFiles.length === 1) { - return {getFiles[0].name}; - } - return null; - }; - - return ( - <> -
    - -
    - {readOnly ? ( - showFiles() - ) : ( -
    -
    - - {showFiles()} - -
    - - {hasClearButton && getFiles.length > 0 && !disabled && ( - <> - {isBreakpointBelow(currentBreakpoint, 'md') ? ( - - ) : ( - - )} - - - )} - -
    - -
    -
    -
    - )} - {helper ? ( - - ) : uploadErrorHelper ? ( - - ) : null} - - ); -}; - -export default FileUpload; diff --git a/libs/react-components/src/tedi/components/form/file-upload/index.ts b/libs/react-components/src/tedi/components/form/file-upload/index.ts deleted file mode 100644 index a00190ec5..000000000 --- a/libs/react-components/src/tedi/components/form/file-upload/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './file-upload'; diff --git a/libs/react-components/src/tedi/components/form/form-label/form-label.module.scss b/libs/react-components/src/tedi/components/form/form-label/form-label.module.scss deleted file mode 100644 index 49524d44e..000000000 --- a/libs/react-components/src/tedi/components/form/form-label/form-label.module.scss +++ /dev/null @@ -1,11 +0,0 @@ -@use '@tehik-ee/tedi-core/mixins'; - -.tedi-form-label { - &--hidden { - @include mixins.visually-hidden; - } - - &--hidden-keep-space { - visibility: hidden; - } -} diff --git a/libs/react-components/src/tedi/components/form/form-label/form-label.spec.tsx b/libs/react-components/src/tedi/components/form/form-label/form-label.spec.tsx deleted file mode 100644 index 9c470d5aa..000000000 --- a/libs/react-components/src/tedi/components/form/form-label/form-label.spec.tsx +++ /dev/null @@ -1,64 +0,0 @@ -import { render } from '@testing-library/react'; - -import { FormLabel } from './form-label'; - -describe('FormLabel component', () => { - it('renders with default props', () => { - const { container } = render(); - - const label = container.querySelector('label'); - expect(label).toBeInTheDocument(); - expect(label).toHaveTextContent('Test Label'); - expect(label).toHaveAttribute('for', 'test-id'); - expect(label).not.toHaveClass('tedi-form-label--small'); - }); - - it('renders with hideLabel as true', () => { - const { container } = render(); - - const label = container.querySelector('.tedi-form-label'); - expect(label).toHaveClass('tedi-form-label--hidden'); - }); - - it('renders with hideLabel as "keep-space"', () => { - const { container } = render(); - - const label = container.querySelector('.tedi-form-label'); - expect(label).toHaveClass('tedi-form-label--hidden-keep-space'); - }); - - it('renders with a custom class name', () => { - const { container } = render(); - - const label = container.querySelector('.tedi-form-label'); - expect(label).toHaveClass('custom-class'); - }); - - it('renders as span when renderWithoutLabel is true', () => { - const { container } = render(); - - const span = container.querySelector('span'); - expect(span).toBeInTheDocument(); - expect(span).toHaveTextContent('Test Label'); - }); - - it('renders with small size', () => { - const { container } = render(); - - const label = container.querySelector('.tedi-form-label'); - expect(label).toHaveClass('tedi-form-label--small'); - }); - - it('renders with default size', () => { - const { container } = render(); - - const label = container.querySelector('.tedi-form-label'); - expect(label).toHaveClass('tedi-form-label--default'); - }); - - it('renders the label text correctly', () => { - const { getByText } = render(); - - expect(getByText('Form Label Text')).toBeInTheDocument(); - }); -}); diff --git a/libs/react-components/src/tedi/components/form/form-label/form-label.stories.tsx b/libs/react-components/src/tedi/components/form/form-label/form-label.stories.tsx deleted file mode 100644 index e9c4daedb..000000000 --- a/libs/react-components/src/tedi/components/form/form-label/form-label.stories.tsx +++ /dev/null @@ -1,76 +0,0 @@ -import { Meta, StoryFn, StoryObj } from '@storybook/react'; - -import { Label } from '../../content/label/label'; -import { Col, Row } from '../../layout/grid'; -import FormLabel from './form-label'; - -const meta: Meta = { - component: FormLabel, - title: 'TEDI-Ready/Components/Form/FormLabel', - parameters: { - status: { - type: 'internalComponent', - }, - }, -}; - -export default meta; -type Story = StoryObj; - -export const Default: Story = { - args: { - id: 'input-id-1', - label: 'Label of input', - }, -}; - -const sizeArray = ['default', 'small']; - -const SizeTemplate: StoryFn = () => { - return ( -
    - {sizeArray.map((size, key) => ( - - - {size.charAt(0).toUpperCase() + size.slice(1)} - - - - - - - ))} -
    - ); -}; - -export const Size = { - render: SizeTemplate, -}; - -const StructureTemplate: StoryFn = () => { - return ( - - - - - - - - - - - - - - - ); -}; - -export const Structure = { - render: StructureTemplate, -}; diff --git a/libs/react-components/src/tedi/components/form/form-label/form-label.tsx b/libs/react-components/src/tedi/components/form/form-label/form-label.tsx deleted file mode 100644 index 287c92d6b..000000000 --- a/libs/react-components/src/tedi/components/form/form-label/form-label.tsx +++ /dev/null @@ -1,80 +0,0 @@ -import cn from 'classnames'; -import { forwardRef } from 'react'; - -import { Label } from '../../content/label/label'; -import styles from './form-label.module.scss'; - -export interface FormLabelProps { - /** - * The unique identifier for the input element that this label is associated with. - * This ID should match the input element's `id` attribute to ensure accessibility. - */ - id: string; - /** - * The text content of the label that describes the input field. - */ - label: string; - /** - * Controls the visibility of the label. - * Use `true` to hide the label visually while maintaining its space in the layout, - * or use 'keep-space' to hide it without collapsing the space it occupies. - */ - hideLabel?: boolean | 'keep-space'; - /** - * Indicates whether the input field is required. - * If set to `true`, the required indicator (if provided) will be displayed next to the label. - */ - required?: boolean; - /** - * Additional className. - */ - className?: string; - /** - * Renders the label as a `` instead of a `