SCSS
```scss styles.scss // styles.scss -@forward "@db-ui/components/build/styles/db-ui-42-webpack"; +@forward "@db-ui/components/build/styles/db-ui-42-rollup"; ```SCSS
@@ -51,7 +51,7 @@ import "@db-ui/components/build/styles/db-ui-42-rollup.css"; ```tsx import { DBButton } from '@db-ui/react-components'; ... -SCSS
+ +```scss +// index.scss +@forward "@db-ui/components/build/styles/db-ui-42-rollup"; +``` + +CSS
+ +```js +// main.js +import "@db-ui/components/build/styles/db-ui-42-rollup.css"; +``` + +SCSS
@@ -59,7 +59,7 @@ import { DBButton } from "@db-ui/v-components"; --
+
- + DBStack + +
- DBSwitch diff --git a/packages/components/mitosis-angular.config.js b/packages/components/mitosis-angular.config.js deleted file mode 100644 index 3f4dcb9e36e..00000000000 --- a/packages/components/mitosis-angular.config.js +++ /dev/null @@ -1,12 +0,0 @@ -module.exports = { - files: 'src/**', - targets: ['angular'], - dest: '../../output/tmp', - options: { - angular: { - typescript: true, - standalone: true, - visuallyIgnoreHostElement: false - } - } -}; diff --git a/packages/components/mitosis-react.config.js b/packages/components/mitosis-react.config.js deleted file mode 100644 index 84ab3f8c89c..00000000000 --- a/packages/components/mitosis-react.config.js +++ /dev/null @@ -1,10 +0,0 @@ -module.exports = { - files: 'src/**', - targets: ['react'], - dest: '../../output/tmp', - options: { - react: { - typescript: true - } - } -}; diff --git a/packages/components/mitosis-vue.config.js b/packages/components/mitosis-vue.config.js deleted file mode 100644 index 172455c9f7f..00000000000 --- a/packages/components/mitosis-vue.config.js +++ /dev/null @@ -1,10 +0,0 @@ -module.exports = { - files: 'src/**', - targets: ['vue'], - dest: '../../output/tmp', - options: { - vue: { - typescript: true - } - } -}; diff --git a/packages/components/mitosis.config.js b/packages/components/mitosis.config.js deleted file mode 100644 index 7bd332c714a..00000000000 --- a/packages/components/mitosis.config.js +++ /dev/null @@ -1,40 +0,0 @@ -module.exports = { - files: 'src/**', - targets: ['angular', 'vue', 'webcomponent', 'react'], - dest: '../../output', - options: { - react: { - typescript: true - }, - angular: { - typescript: true, - standalone: true, - visuallyIgnoreHostElement: false - }, - vue: { - typescript: true - }, - webcomponent: { - experimental: { - attributeChangedCallback(test, json) { - const properties = - json?.meta?.useMetadata?.component?.properties?.map( - (prop) => prop.name.toLowerCase() - ) || []; - return ( - 'const foundProp = this.componentProps.find(prop=> prop.toLowerCase() === name);\n' + - "if (newValue === 'false') {\n" + - '\t\t\tdelete this.props[foundProp];\n' + - '\t\t} else {\n' + - '\t\t\tthis.props[foundProp] = newValue;\n' + - '\t\t}' + - ' this.update();' + - '}' + - 'static get observedAttributes() {\n' + - ` return ${JSON.stringify(properties)};` - ); - } - } - } - } -}; diff --git a/packages/components/package.json b/packages/components/package.json index 65b92c8a83c..56841e56a99 100644 --- a/packages/components/package.json +++ b/packages/components/package.json @@ -17,15 +17,16 @@ "scripts": { "build": "npm-run-all -p build-components build-assets -s build-style:*", "build-assets": "cpr src build -o -f \"(.ts|.tsx|.md|.html)$\"", - "build-components": "npm-run-all build:mitosis -p build-components:*", - "build-components:docs": "react-docgen ../../output/react/src/components/**/*.tsx -o ../../output/docs.json -i *.spec.tsx", + "build-components": "npm-run-all build:mitosis build-components:post build-components:docs", + "build-components:docs": "npm run build:cem -w @db-ui/web-components", "build-components:post": "tsx scripts/post-build/index.ts", "build-style:01_sass": "sass src:build --no-source-map --load-path=node_modules/ --load-path=../../node_modules/ --future-deprecation=import", "build-style:02_postcss": "postcss build/**/*.css --replace", - "build:mitosis": "mitosis build", - "compile:angular": "mitosis build -c mitosis-angular.config.js && tsx scripts/exec/angular.ts && cpr ../../output/tmp/angular/src ../../output/angular/src -o", - "compile:react": "mitosis build -c mitosis-react.config.js && tsx scripts/exec/react.ts && cpr ../../output/tmp/react/src ../../output/react/src -o", - "compile:vue": "mitosis build -c mitosis-vue.config.js && tsx scripts/exec/vue.ts && cpr ../../output/tmp/vue/src ../../output/vue/src -o", + "build:mitosis": "mitosis build -c configs/mitosis.config.js", + "compile:angular": "mitosis build -c configs/angular/mitosis.config.js && tsx scripts/exec/angular.ts && cpr ../../output/tmp/angular/src ../../output/angular/src -o", + "compile:react": "mitosis build -c configs/react/mitosis.config.js && tsx scripts/exec/react.ts && cpr ../../output/tmp/react/src ../../output/react/src -o", + "compile:stencil": "mitosis build -c configs/stencil/mitosis.config.js && tsx scripts/exec/stencil.ts && cpr ../../output/tmp/stencil/src ../../output/stencil/src -o", + "compile:vue": "mitosis build -c configs/vue/mitosis.config.js && tsx scripts/exec/vue.ts && cpr ../../output/tmp/vue/src ../../output/vue/src -o", "copy-assets": "cpr ../foundations/assets build/assets -o", "copy-output": "npm-run-all copy:*", "copy:outputs": "cpr build ../../build-outputs/components/build -o", @@ -35,29 +36,28 @@ "dev:html": "npm run copy-assets && npm run build-assets && npm run build-style:01_sass && vite --open", "dev:react": "nodemon --watch src --watch overrides -e tsx,ts -x \"npm run compile:react\"", "dev:scss": "npm run build-style:01_sass -- --watch", + "dev:stencil": "nodemon --watch src --watch overrides -e tsx,ts -x \"npm run compile:stencil\"", "dev:vue": "nodemon --watch src --watch overrides -e tsx,ts -x \"npm run compile:vue\"", "generate:component": "hygen mitosis new", "generate:docs": "hygen update-docs new", - "generate:icon-types": "tsx ./scripts/generate-icon-types.ts", "prepack": "npm run copy-assets", "start": "nodemon --watch src --watch scripts --watch overrides -e js,tsx,ts,scss,json -x \"npm run build\"" }, "dependencies": { - "@db-ui/foundations": "*" + "@db-ui/foundations": "*", + "@db-ux/core-icons": "0.0.7" }, "devDependencies": { - "@builder.io/eslint-plugin-mitosis": "^0.0.16", - "@builder.io/mitosis": "^0.4.3", - "@builder.io/mitosis-cli": "^0.4.3", - "@react-docgen/cli": "^2.0.3", + "@builder.io/eslint-plugin-mitosis": "0.0.16", + "@builder.io/mitosis": "0.5.18", + "@builder.io/mitosis-cli": "0.5.18", "cpr": "3.0.1", "cssnano": "^7.0.6", - "eslint": "^9.0.0", + "eslint": "^8", "hygen": "^6.2.11", - "lit": "^3.2.0", - "nodemon": "3.1.4", + "nodemon": "3.1.9", "sass": "1.77.4", - "tsx": "^4.19.1" + "tsx": "^4.19.2" }, "publishConfig": { "registry": "https://registry.npmjs.org/", diff --git a/packages/components/scripts/exec/stencil.ts b/packages/components/scripts/exec/stencil.ts new file mode 100644 index 00000000000..0280d206ae8 --- /dev/null +++ b/packages/components/scripts/exec/stencil.ts @@ -0,0 +1,3 @@ +import stencil from '../post-build/stencil'; + +stencil(true); diff --git a/packages/components/scripts/generate-icon-types.ts b/packages/components/scripts/generate-icon-types.ts deleted file mode 100644 index 61298a80790..00000000000 --- a/packages/components/scripts/generate-icon-types.ts +++ /dev/null @@ -1,69 +0,0 @@ -/* - * This script can be used to update the icon type for all components using icons. - */ - -import { readdirSync, lstatSync, writeFileSync } from 'node:fs'; - -const foundationAssetsPath = '../foundations/assets/icons/functional/images'; - -const generateIconTypes = () => { - try { - const allIcons = []; - const icons = readdirSync(foundationAssetsPath) - .reduce((previousValue, currentValue) => { - const path_string = `${foundationAssetsPath}/${currentValue}`; - - if ( - currentValue.includes('.svg') || - !lstatSync(path_string).isDirectory() - ) { - return previousValue; - } - - let iconPaths = readdirSync(path_string); - - return [ - ...previousValue, - `// Directory: ${currentValue}`, - ...iconPaths - ]; - }, []) - .filter( - (icon) => - (!icon.includes('_inverted') && - !icon.includes('_filled') && - icon.includes('_24')) || - icon.startsWith('//') - ) - .map((icon) => - icon - .replace('db_ic_', '') - .replace('_24.svg', '') - .replace(/-/g, '_') - ) - .map((icon) => { - if (icon.startsWith('//')) { - return icon; - } - - allIcons.push(icon); - - return `| '${icon}'`; - }); - - const generatedDisclaimer = - '/* This file was generated by packages/components/scripts/generate-icon-types.js */\n'; - const iconTypes = `${generatedDisclaimer}export type IconTypes = string \n ${icons.join( - '\n' - )}`; - const allIconsFile = `${generatedDisclaimer}export const ALL_ICONS: string[] = ${JSON.stringify( - allIcons - )};`; - writeFileSync('./src/shared/icon-types.ts', iconTypes); - writeFileSync('./src/shared/all-icons.ts', allIconsFile); - } catch (e) { - console.log(e); - } -}; - -generateIconTypes(); diff --git a/packages/components/scripts/post-build/angular.ts b/packages/components/scripts/post-build/angular.ts index 53a7abfc996..65d5771c26f 100644 --- a/packages/components/scripts/post-build/angular.ts +++ b/packages/components/scripts/post-build/angular.ts @@ -9,13 +9,11 @@ import { runReplacements, transformToUpperComponentName } from '../utils'; const changeFile = (input: string) => { return input .split('\n') - .filter( - (line) => - !line.includes('@db-ui') && - !line.includes(`Props } from "../`) && - !line.includes(`[key]=`) - ) .map((line) => { + if (line.includes('export default')) { + return line.replace('export default', 'export'); + } + if (line.includes(': ElementRef')) { return line.replace(': ElementRef', ': ElementRef | undefined'); } @@ -68,8 +66,8 @@ const setControlValueAccessorReplacements = ( // implementing interface and constructor replacements.push({ - from: `export default class ${upperComponentName} {`, - to: `export default class ${upperComponentName} implements ControlValueAccessor { + from: `export class ${upperComponentName} {`, + to: `export class ${upperComponentName} implements ControlValueAccessor { constructor(private renderer: Renderer2) { }` }); @@ -130,9 +128,9 @@ const setDirectiveReplacements = ( } replacements.push({ - from: `export default class ${upperComponentName} {\n`, + from: `export class ${upperComponentName} {\n`, to: - `export default class ${upperComponentName} {\n` + + `export class ${upperComponentName} {\n` + `\t@ContentChild(${directive.name}Directive, { read: TemplateRef }) db${directive.name}: any;\n` }); @@ -178,21 +176,7 @@ export class ${directive.name}Directive {} const getAttributePassing = (componentName: string) => ` ngAfterViewInit(): void { \t\tconst element: HTMLElement | null = this.ref?.nativeElement; -\t\tconst parent = element?.closest('db-${componentName}') ?? element?.closest('db${componentName.replace(/-/g, '')}'); -\t\tif (element && parent) { -\t\t\tconst attributes = parent.attributes; -\t\t\tfor (let i = 0; i < attributes.length; i++) { -\t\t\t\tconst attr = attributes.item(i); -\t\t\t\tif ( -\t\t\t\t\tattr && -\t\t\t\t\t(attr.name.startsWith('data-') || -\t\t\t\t\t\tattr.name.startsWith('aria-')) -\t\t\t\t) { -\t\t\t\t\telement.setAttribute(attr.name, attr.value); -\t\t\t\t\tparent.removeAttribute(attr.name); -\t\t\t\t} -\t\t\t} -\t\t} +\t\tenableCustomElementAttributePassing(element,'db-${componentName}') \t}`; export default (tmp?: boolean) => { @@ -207,41 +191,32 @@ export default (tmp?: boolean) => { const componentName = component.name; const upperComponentName = `DB${transformToUpperComponentName(component.name)}`; const file = `../../${outputFolder}/angular/src/components/${componentName}/${componentName}.ts`; - const options = { - files: file, - processor: (input: string) => changeFile(input) - }; + const indexFile = `../../${outputFolder}/angular/src/components/${componentName}/index.ts`; + + replaceInFileSync({ + files: indexFile, + from: 'default as ', + to: '' + }); const replacements: Overwrite[] = [ - { - from: 'attr.disabled', - to: 'disabled' - }, { from: 'ngOnChanges', to: 'ngAfterContentChecked' }, - { - from: 'mouseOver', - to: 'mouseover' - }, - { - from: 'mouseEnter', - to: 'mouseenter' - }, - { - from: 'mouseLeave', - to: 'mouseleave' - }, - { - from: 'mouseMove', - to: 'mousemove' - }, { from: '@ViewChild("ref") ref!: ElementRef | undefined;', to: '@ViewChild("ref") ref!: ElementRef | undefined;' + getAttributePassing(component.name) + }, + { + from: '} from "../../utils"', + to: ', enableCustomElementAttributePassing } from "../../utils"' + }, + { + from: /this.ref.nativeElement/g, + to: 'this.ref?.nativeElement' } ]; @@ -273,7 +248,10 @@ export default (tmp?: boolean) => { } try { - replaceInFileSync(options); + replaceInFileSync({ + files: file, + processor: (input: string) => changeFile(input) + }); runReplacements(replacements, component, 'angular', file); } catch (error) { console.error('Error occurred:', error); diff --git a/packages/components/scripts/post-build/components.ts b/packages/components/scripts/post-build/components.ts index 01cdcf423cc..1735958f3d0 100644 --- a/packages/components/scripts/post-build/components.ts +++ b/packages/components/scripts/post-build/components.ts @@ -8,9 +8,9 @@ export type Component = { overwrites?: { global?: Overwrite[]; angular?: Overwrite[]; + stencil?: Overwrite[]; react?: Overwrite[]; vue?: Overwrite[]; - webComponents?: Overwrite[]; }; config?: { vue?: { @@ -29,8 +29,14 @@ export type Component = { }; export const getComponents = (): Component[] => [ + { + name: 'stack' + }, { name: 'switch', + overwrites: { + stencil: [{ from: 'HTMLElement', to: 'HTMLInputElement' }] + }, config: { vue: { vModel: [{ modelValue: 'checked', binding: ':checked' }] @@ -63,10 +69,6 @@ export const getComponents = (): Component[] => [ { from: 'scrollContainer = null;', to: 'scrollContainer: Element | null = null;' - }, - { - from: '& > .db-tab-panel', - to: '& > dbtabpanel > .db-tab-panel, & > db-tab-panel > .db-tab-panel' } ] } @@ -89,17 +91,14 @@ export const getComponents = (): Component[] => [ name: 'accordion-item', overwrites: { // this is an issue from mitosis always adding `attr` - angular: [{ from: 'attr.open', to: 'open' }] + angular: [{ from: 'attr.open', to: 'open' }], + // TS issue + stencil: [{ from: 'name={this.name}', to: '' }] } }, { - name: 'accordion', - overwrites: { - angular: [ - { from: 'openItems = []', to: 'openItems: string[] = []' } - ] - } + name: 'accordion' }, { @@ -118,7 +117,8 @@ export const getComponents = (): Component[] => [ from: '', to: '{{value}}' } - ] + ], + stencil: [{ from: 'HTMLElement', to: 'HTMLTextAreaElement' }] } }, { @@ -142,6 +142,12 @@ export const getComponents = (): Component[] => [ from: 'navigationItemSafeTriangle: undefined', to: 'navigationItemSafeTriangle: undefined as undefined | NavigationItemSafeTriangle' } + ], + react: [ + { + from: 'onMouseMove={(event)', + to: 'onMouseMove={(event: any)' + } ] }, config: { @@ -158,6 +164,10 @@ export const getComponents = (): Component[] => [ // React not allowing selected for options { from: 'selected={option.selected}', to: '' }, { from: 'selected={optgroupOption.selected}', to: '' } + ], + stencil: [ + { from: 'HTMLElement', to: 'HTMLSelectElement' }, + { from: 'value={', to: '/* @ts-ignore */\nvalue={' } ] }, config: { @@ -175,7 +185,7 @@ export const getComponents = (): Component[] => [ { name: 'drawer', overwrites: { - webComponents: [{ from: '__prev.find', to: '!!__prev.find' }] + stencil: [{ from: /onClose/g, to: 'close' }] }, config: { react: { @@ -185,18 +195,13 @@ export const getComponents = (): Component[] => [ }, { - name: 'tag', - overwrites: { - angular: [ - { - from: /this.ref.nativeElement/g, - to: 'this.ref?.nativeElement' - } - ] - } + name: 'tag' }, { name: 'checkbox', + overwrites: { + stencil: [{ from: 'HTMLElement', to: 'HTMLInputElement' }] + }, config: { vue: { vModel: [{ modelValue: 'checked', binding: ':checked' }] @@ -209,6 +214,9 @@ export const getComponents = (): Component[] => [ { name: 'radio', + overwrites: { + stencil: [{ from: 'HTMLElement', to: 'HTMLInputElement' }] + }, config: { vue: { vModel: [{ modelValue: 'checked', binding: ':checked' }] @@ -268,31 +276,7 @@ export const getComponents = (): Component[] => [ to: '() => toggle()' } ], - angular: [{ from: '(close)', to: '(onClose)' }], - webComponents: [ - { - from: '
-
+ ++
state.toggle(event)}> +
++ {props.headlinePlain} + ++ ++ +++ {props.text} + +
);
}
diff --git a/packages/components/src/components/accordion-item/accordion-item.scss b/packages/components/src/components/accordion-item/accordion-item.scss
index ca3334ebdf6..5a97deb5126 100644
--- a/packages/components/src/components/accordion-item/accordion-item.scss
+++ b/packages/components/src/components/accordion-item/accordion-item.scss
@@ -11,22 +11,39 @@ $db-accordion-item-border-radius: variables.$db-border-radius-sm;
position: relative;
inline-size: 100%;
border-radius: $db-accordion-item-border-radius;
+ list-style-type: "";
+
+ > details {
+ &[open] {
+ summary + div {
+ @media screen and (prefers-reduced-motion: no-preference) {
+ animation: accordion-open
+ #{variables.$db-transition-straight-emotional} normal
+ both;
+ }
+ }
+
+ summary::after {
+ transform: form-components.$dropdown-icon-transform;
+ }
+ }
+
+ &[aria-disabled="true"] {
+ pointer-events: none;
+ opacity: component.$default-disabled;
+ }
+ }
summary + div {
padding: variables.$db-spacing-fixed-md;
padding-block-end: variables.$db-spacing-fixed-lg;
}
- &[aria-disabled="true"] {
- pointer-events: none;
- opacity: component.$default-disabled;
- }
-
summary {
@extend %dropdown-icon;
@extend %db-overwrite-font-size-md;
- background-color: colors.$db-adaptive-bg-basic-transparent-full-default;
+ background-color: colors.$db-adaptive-bg-basic-level-1-default;
list-style: none;
display: flex;
justify-content: space-between;
@@ -35,19 +52,11 @@ $db-accordion-item-border-radius: variables.$db-border-radius-sm;
border-radius: $db-accordion-item-border-radius;
@include helpers.hover {
- background-color: colors.$db-adaptive-bg-basic-transparent-hovered;
- }
-
- @include helpers.active {
- background-color: colors.$db-adaptive-bg-basic-transparent-pressed;
- }
-
- @include helpers.hover {
- background-color: colors.$db-adaptive-bg-basic-transparent-hovered;
+ background-color: colors.$db-adaptive-bg-basic-level-1-hovered;
}
@include helpers.active {
- background-color: colors.$db-adaptive-bg-basic-transparent-pressed;
+ background-color: colors.$db-adaptive-bg-basic-level-1-pressed;
}
&::-webkit-details-marker {
@@ -62,17 +71,4 @@ $db-accordion-item-border-radius: variables.$db-border-radius-sm;
border-radius: variables.$db-border-radius-xs;
}
}
-
- &[open] {
- summary + div {
- @media screen and (prefers-reduced-motion: no-preference) {
- animation: accordion-open #{variables.$db-transition-straight-emotional}
- normal both;
- }
- }
-
- summary::after {
- transform: form-components.$dropdown-icon-transform;
- }
- }
}
diff --git a/packages/components/src/components/accordion-item/accordion-item.spec.tsx b/packages/components/src/components/accordion-item/accordion-item.spec.tsx
index 46e4a6bb0a0..c5c5a7a9789 100644
--- a/packages/components/src/components/accordion-item/accordion-item.spec.tsx
+++ b/packages/components/src/components/accordion-item/accordion-item.spec.tsx
@@ -1,4 +1,4 @@
-import { test, expect } from '@playwright/experimental-ct-react';
+import { expect, test } from '@playwright/experimental-ct-react';
import AxeBuilder from '@axe-core/playwright';
import { DBAccordionItem } from './index';
@@ -20,10 +20,18 @@ const testComponent = () => {
};
const testA11y = () => {
+ test('should have same aria-snapshot', async ({ mount }, testInfo) => {
+ const component = await mount(comp);
+ const snapshot = await component.ariaSnapshot();
+ expect(snapshot).toMatchSnapshot(`${testInfo.testId}.yaml`);
+ });
test('should not have any A11y issues', async ({ page, mount }) => {
await mount(comp);
const accessibilityScanResults = await new AxeBuilder({ page })
.include('.db-accordion-item')
+ // Showcase uses - outside of
- in this case
+ // TODO: Let's investigate whether we could prevent this deactivation later on
+ .disableRules(['listitem'])
.analyze();
expect(accessibilityScanResults.violations).toEqual([]);
diff --git a/packages/components/src/components/accordion-item/model.ts b/packages/components/src/components/accordion-item/model.ts
index 0ef6cc2f7e8..58893078d0e 100644
--- a/packages/components/src/components/accordion-item/model.ts
+++ b/packages/components/src/components/accordion-item/model.ts
@@ -1,15 +1,12 @@
import {
GlobalProps,
GlobalState,
+ TextProps,
ToggleEventProps,
ToggleEventState
} from '../../shared/model';
-export interface DBAccordionItemDefaultProps {
- /**
- * Alternative for passing only a string instead of children
- */
- content?: string;
+export type DBAccordionItemDefaultProps = {
/**
* Initial state for the accordion item
*/
@@ -21,24 +18,20 @@ export interface DBAccordionItemDefaultProps {
/**
* Title of the accordion-item as slot
*/
- headline?: unknown;
+ headline?: any;
/**
* Title of the accordion-item as plain text
*/
headlinePlain?: string;
- /**
- * Set details name for exclusive accordions, see https://chromestatus.com/feature/6710427028815872
- */
- name?: string;
-}
+} & TextProps;
export type DBAccordionItemProps = DBAccordionItemDefaultProps &
GlobalProps &
ToggleEventProps;
-export interface DBAccordionItemDefaultState {
+export type DBAccordionItemDefaultState = {
_open: boolean;
-}
+};
export type DBAccordionItemState = DBAccordionItemDefaultState &
GlobalState &
diff --git a/packages/components/src/components/accordion/accordion-web-component.scss b/packages/components/src/components/accordion/accordion-web-component.scss
deleted file mode 100644
index 65bc6f97afa..00000000000
--- a/packages/components/src/components/accordion/accordion-web-component.scss
+++ /dev/null
@@ -1 +0,0 @@
-@forward "accordion";
diff --git a/packages/components/src/components/accordion/accordion.lite.tsx b/packages/components/src/components/accordion/accordion.lite.tsx
index 82fc6d4ff74..41193e14503 100644
--- a/packages/components/src/components/accordion/accordion.lite.tsx
+++ b/packages/components/src/components/accordion/accordion.lite.tsx
@@ -7,117 +7,110 @@ import {
useRef,
useStore
} from '@builder.io/mitosis';
+import { DBAccordionItemDefaultProps } from '../accordion-item/model';
import { DBAccordionProps, DBAccordionState } from './model';
-import { cls } from '../../utils';
+import { cls, uuid } from '../../utils';
import DBAccordionItem from '../accordion-item/accordion-item.lite';
-import { DBAccordionItemDefaultProps } from '../accordion-item/model';
+import { DEFAULT_ID } from '../../shared/constants';
-useMetadata({
- isAttachedToShadowDom: true
-});
+useMetadata({});
export default function DBAccordion(props: DBAccordionProps) {
- const ref = useRef
(null); + const ref = useRef (null); // jscpd:ignore-start const state = useStore ({ - openItems: [], - clickedId: '', + _id: DEFAULT_ID, + _name: '', initialized: false, - convertItems(items: unknown[] | string | undefined) { + _initOpenIndexDone: false, + convertItems( + items: unknown[] | string | undefined + ): DBAccordionItemDefaultProps[] { try { if (typeof items === 'string') { return JSON.parse(items); } - return items; + return items as DBAccordionItemDefaultProps[]; } catch (error) { console.error(error); } - return undefined; - }, - handleItemClick: (id: string) => { - if (state.openItems.includes(id)) { - if (props.behaviour === 'single') { - state.openItems = []; - } else { - state.openItems = state.openItems.filter( - (oItem) => oItem !== id - ); - } - } else if (props.behaviour === 'single') { - state.openItems = [id]; - } else { - state.openItems = [...state.openItems, id]; - } - - if (props.onChange) { - props.onChange(state.openItems); - } + return []; } }); onMount(() => { + state._id = props.id || 'accordion-' + uuid(); state.initialized = true; + state._initOpenIndexDone = true; }); // jscpd:ignore-end onUpdate(() => { - if (ref && state.initialized) { - const childDetails = ref.getElementsByTagName('details'); - if (childDetails) { - let initOpenItems: string[] = []; - Array.from (childDetails).forEach( - (details: HTMLDetailsElement, index: number) => { - const id = details.id; - if ( - details.open || - props.initOpenIndex?.includes(index) - ) { - initOpenItems.push(id); - } - const summaries = - details.getElementsByTagName('summary'); - if (summaries?.length > 0) { - summaries[0].addEventListener('click', () => { - state.clickedId = id; - }); - } + // If we have a single behaviour we first check for + // props.name otherwise for state_id + if (state.initialized) { + if (props.behaviour === 'single') { + if (props.name) { + if (state._name !== props.name) { + state._name = props.name; + } + } else { + if (state._name !== state._id && state._id) { + state._name = state._id; } - ); - if (props.behaviour === 'single' && initOpenItems.length > 1) { - initOpenItems = [initOpenItems[0]]; } - state.openItems = initOpenItems; - state.initialized = false; + } else { + state._name = ''; } } - }, [ref, state.initialized]); - - onUpdate(() => { - if (state.clickedId?.length > 0) { - state.handleItemClick(state.clickedId); - state.clickedId = ''; - } - }, [state.clickedId]); + }, [state.initialized, props.name, props.behaviour, state._id]); onUpdate(() => { if (ref) { const childDetails = ref.getElementsByTagName('details'); if (childDetails) { - Array.from (childDetails).forEach( - (details: HTMLDetailsElement) => { - details.open = state.openItems.includes(details.id); + for (const details of Array.from ( + childDetails + )) { + if (state._name === '') { + details.removeAttribute('name'); + } else { + details.name = state._name; } - ); + } + } + } + }, [ref, state._name]); + + onUpdate(() => { + if (ref && state._initOpenIndexDone) { + if (props?.initOpenIndex && props.initOpenIndex?.length > 0) { + const childDetails = ref.getElementsByTagName('details'); + if (childDetails) { + const initOpenIndex = + props.behaviour === 'single' && + props.initOpenIndex.length > 1 + ? [props.initOpenIndex[0]] // use only one index for behaviour=single + : props.initOpenIndex; + Array.from (childDetails).forEach( + (details: HTMLDetailsElement, index: number) => { + if (initOpenIndex.includes(index)) { + details.open = true; + } + } + ); + } } + state._initOpenIndexDone = false; } - }, [state.openItems]); + }, [ref, state._initOpenIndexDone, props.initOpenIndex]); return ( - {props.children} @@ -128,11 +121,11 @@ export default function DBAccordion(props: DBAccordionProps) { key={`accordion-item-${index}`} headlinePlain={item.headlinePlain} disabled={item.disabled} - content={item.content} + text={item.text} /> )} -- @@ -75,7 +78,10 @@ const testAction = () => { await component.getByTestId('item2').click(); await expect(component.getByTestId('button')).toBeHidden(); await expect(component.getByTestId('textarea')).toBeVisible(); - await expect(component.getByTestId('item3')).toBeDisabled(); + await expect( + component.getByTestId('item3') + // VUE: .getByRole('group') + ).toBeDisabled(); }); test('click inside item works', async ({ mount }) => { @@ -98,6 +104,11 @@ const testAction = () => { }; const testA11y = () => { + test('should have same aria-snapshot', async ({ mount }, testInfo) => { + const component = await mount(comp); + const snapshot = await component.ariaSnapshot(); + expect(snapshot).toMatchSnapshot(`${testInfo.testId}.yaml`); + }); test('should not have any A11y issues', async ({ page, mount }) => { await mount(comp); const accessibilityScanResults = await new AxeBuilder({ page }) diff --git a/packages/components/src/components/accordion/model.ts b/packages/components/src/components/accordion/model.ts index 3a4856aa59a..3a7c1aebb15 100644 --- a/packages/components/src/components/accordion/model.ts +++ b/packages/components/src/components/accordion/model.ts @@ -1,29 +1,17 @@ -import { - GlobalProps, - GlobalState, - InitializedState, - ItemClickState -} from '../../shared/model'; +import { GlobalProps, GlobalState, InitializedState } from '../../shared/model'; import { DBAccordionItemDefaultProps } from '../accordion-item/model'; -export const AccordionVariantList = ['card'] as const; +export const AccordionVariantList = ['default', 'card'] as const; export type AccordionVariantType = (typeof AccordionVariantList)[number]; export const AccordionBehaviourList = ['multiple', 'single'] as const; export type AccordionBehaviourType = (typeof AccordionBehaviourList)[number]; -export interface DBAccordionDefaultProps { - /** - * Defines the display of the accordion and the items: - * "default": with a dividing line between the items - * "card": w/o dividing line, but items are shown in the card variant - */ - variant?: AccordionVariantType; +export type DBAccordionDefaultProps = { /** * To allow multiple items open at the same time or only 1 item */ behaviour?: AccordionBehaviourType; - /** * The index of items which should be open when loading the accordion */ @@ -34,20 +22,32 @@ export interface DBAccordionDefaultProps { */ items?: DBAccordionItemDefaultProps[] | string; + /** + * Set details name for exclusive accordions, see https://developer.mozilla.org/en-US/docs/Web/HTML/Element/details#name + */ + name?: string; + /** * Informs about the changes in the internal state, which item is open */ onChange?: (openAccordionItemIds: string[]) => void; -} + + /** + * Defines the display of the accordion and the items: + * "default": with a dividing line between the items + * "card": w/o dividing line, but items are shown in the card variant + */ + variant?: AccordionVariantType; +}; export type DBAccordionProps = DBAccordionDefaultProps & GlobalProps; -export interface DBAccordionDefaultState { - openItems: string[]; +export type DBAccordionDefaultState = { + _initOpenIndexDone: boolean; + _name: string; convertItems: (items?: unknown[] | string) => DBAccordionItemDefaultProps[]; -} +}; export type DBAccordionState = DBAccordionDefaultState & GlobalState & - InitializedState & - ItemClickState; + InitializedState; diff --git a/packages/components/src/components/badge/badge-web-component.scss b/packages/components/src/components/badge/badge-web-component.scss deleted file mode 100644 index 97e7f44396d..00000000000 --- a/packages/components/src/components/badge/badge-web-component.scss +++ /dev/null @@ -1 +0,0 @@ -@forward "badge"; diff --git a/packages/components/src/components/badge/badge.lite.tsx b/packages/components/src/components/badge/badge.lite.tsx index 7bbef7371b1..c0a11fe0e57 100644 --- a/packages/components/src/components/badge/badge.lite.tsx +++ b/packages/components/src/components/badge/badge.lite.tsx @@ -10,9 +10,7 @@ import { DBBadgeProps, DBBadgeState } from './model'; import { cls } from '../../utils'; import { DEFAULT_LABEL } from '../../shared/constants'; -useMetadata({ - isAttachedToShadowDom: true -}); +useMetadata({}); export default function DBBadge(props: DBBadgeProps) { const ref = useRef+ Click me + + Click me (null); @@ -54,7 +52,9 @@ export default function DBBadge(props: DBBadgeProps) { props.placement?.startsWith('corner') && (props.label ?? DEFAULT_LABEL) }> - {props.children} + + {props.text} +