From 4c2a7552054047745fc06f4640c76603ce67860e Mon Sep 17 00:00:00 2001 From: Nicolas Merget <104347736+nmerget@users.noreply.github.com> Date: Thu, 11 Apr 2024 11:25:36 +0200 Subject: [PATCH 1/6] fix: wrong/missing react attributes (#2337) --- .../components/scripts/post-build/react.js | 6 +-- .../accordion-item/accordion-item.lite.tsx | 6 +-- .../accordion-item/accordion-item.spec.tsx | 2 +- .../src/components/accordion-item/model.ts | 4 +- .../components/accordion/accordion.lite.tsx | 4 +- .../components/accordion/accordion.spec.tsx | 6 +-- .../src/components/button/button.lite.tsx | 1 - .../src/components/icon/icon.lite.tsx | 3 +- .../src/components/infotext/infotext.lite.tsx | 1 - .../src/components/link/link.lite.tsx | 1 - packages/components/src/shared/model.ts | 10 ----- packages/components/src/utils/index.ts | 42 ++++++++++++++++++- .../accordion-item.component.html | 2 +- .../accordion/accordion.component.html | 6 +-- .../src/components/accordion-item/index.tsx | 7 +++- .../src/components/accordion/index.tsx | 6 +-- .../src/components/brand/index.tsx | 8 +--- .../src/components/header/index.tsx | 4 -- .../src/components/page/index.tsx | 8 +--- showcases/shared/accordion-item.json | 14 +++---- showcases/shared/accordion.json | 30 ++++++------- .../accordion-item/AccordionItem.vue | 2 +- .../src/components/accordion/Accordion.vue | 6 +-- 23 files changed, 98 insertions(+), 81 deletions(-) diff --git a/packages/components/scripts/post-build/react.js b/packages/components/scripts/post-build/react.js index b0bbb3e5fe3..47537a0de88 100644 --- a/packages/components/scripts/post-build/react.js +++ b/packages/components/scripts/post-build/react.js @@ -48,15 +48,15 @@ module.exports = (tmp) => { }, { from: ` } from "react"`, - to: `, forwardRef, HTMLProps } from "react"` + to: `, forwardRef, HTMLAttributes } from "react"` }, { from: `function DB${upperComponentName}(props: DB${upperComponentName}Props) {`, - to: `function DB${upperComponentName}Fn(props: Omit, keyof DB${upperComponentName}Props> & DB${upperComponentName}Props, component: any) {` + to: `function DB${upperComponentName}Fn(props: Omit, keyof DB${upperComponentName}Props> & DB${upperComponentName}Props, component: any) {` }, { from: `export default DB${upperComponentName};`, - to: `const DB${upperComponentName} = forwardRef<${htmlElement}, Omit, keyof DB${upperComponentName}Props> & DB${upperComponentName}Props>(DB${upperComponentName}Fn);\nexport default DB${upperComponentName};` + to: `const DB${upperComponentName} = forwardRef<${htmlElement}, Omit, keyof DB${upperComponentName}Props> & DB${upperComponentName}Props>(DB${upperComponentName}Fn);\nexport default DB${upperComponentName};` }, { from: 'if (ref.current)', diff --git a/packages/components/src/components/accordion-item/accordion-item.lite.tsx b/packages/components/src/components/accordion-item/accordion-item.lite.tsx index 673601d6727..c132cd08457 100644 --- a/packages/components/src/components/accordion-item/accordion-item.lite.tsx +++ b/packages/components/src/components/accordion-item/accordion-item.lite.tsx @@ -54,9 +54,9 @@ export default function DBAccordionItem(props: DBAccordionItemProps) { open={state._open} name={props.name}> state.toggle(event)}> - {props.title} - - + {props.headline} + +
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 7991f1acfa9..4b7c0006433 100644 --- a/packages/components/src/components/accordion-item/accordion-item.spec.tsx +++ b/packages/components/src/components/accordion-item/accordion-item.spec.tsx @@ -5,7 +5,7 @@ import { DBAccordionItem } from './index'; // @ts-ignore - vue can only find it with .ts as file ending import { DEFAULT_VIEWPORT } from '../../shared/constants.ts'; -const comp = Test; +const comp = Test; const testComponent = () => { test('should contain text', async ({ mount }) => { diff --git a/packages/components/src/components/accordion-item/model.ts b/packages/components/src/components/accordion-item/model.ts index 5435a973289..f71197d04a1 100644 --- a/packages/components/src/components/accordion-item/model.ts +++ b/packages/components/src/components/accordion-item/model.ts @@ -21,11 +21,11 @@ export interface DBAccordionItemDefaultProps { /** * For react only to pass any title element to the specific slot */ - slotTitle?: any; + slotHeadline?: any; /** * Alternative for passing only a string instead of a slot */ - title?: string; + headline?: string; /** * Set details name for exclusive accordions, see https://chromestatus.com/feature/6710427028815872 */ diff --git a/packages/components/src/components/accordion/accordion.lite.tsx b/packages/components/src/components/accordion/accordion.lite.tsx index 6d462645a1c..79bb8a01b98 100644 --- a/packages/components/src/components/accordion/accordion.lite.tsx +++ b/packages/components/src/components/accordion/accordion.lite.tsx @@ -133,8 +133,8 @@ export default function DBAccordion(props: DBAccordionProps) { {(item: DBAccordionItemInterface, index: number) => ( diff --git a/packages/components/src/components/accordion/accordion.spec.tsx b/packages/components/src/components/accordion/accordion.spec.tsx index a58d93cc093..3e95e5c5587 100644 --- a/packages/components/src/components/accordion/accordion.spec.tsx +++ b/packages/components/src/components/accordion/accordion.spec.tsx @@ -8,9 +8,9 @@ import { DBAccordionItem } from '../accordion-item'; const comp = ( - - - + + + ); diff --git a/packages/components/src/components/button/button.lite.tsx b/packages/components/src/components/button/button.lite.tsx index 6d4e7f7e241..4cea05c488a 100644 --- a/packages/components/src/components/button/button.lite.tsx +++ b/packages/components/src/components/button/button.lite.tsx @@ -28,7 +28,6 @@ export default function DBButton(props: DBButtonProps) { 'is-icon-text-replace': props.noText })} type={props.type} - title={props.title} disabled={props.disabled} aria-label={props.label} data-icon={props.icon} diff --git a/packages/components/src/components/icon/icon.lite.tsx b/packages/components/src/components/icon/icon.lite.tsx index a2496ad3b24..dd1dd67b8d6 100644 --- a/packages/components/src/components/icon/icon.lite.tsx +++ b/packages/components/src/components/icon/icon.lite.tsx @@ -21,8 +21,7 @@ export default function DBIcon(props: DBIconProps) { data-icon={props.icon} data-icon-weight={props.weight} data-icon-variant={props.variant} - aria-hidden="true" - title={props.title}> + aria-hidden="true"> {props.children} ); diff --git a/packages/components/src/components/infotext/infotext.lite.tsx b/packages/components/src/components/infotext/infotext.lite.tsx index 0d5cfcde288..f0a9dc8e5b4 100644 --- a/packages/components/src/components/infotext/infotext.lite.tsx +++ b/packages/components/src/components/infotext/infotext.lite.tsx @@ -18,7 +18,6 @@ export default function DBInfotext(props: DBInfotextProps) { ref={ref} id={props.id} class={cls('db-infotext', props.className)} - title={props.title} data-icon={props.icon} data-variant={props.variant} data-size={props.size}> diff --git a/packages/components/src/components/link/link.lite.tsx b/packages/components/src/components/link/link.lite.tsx index acce97b517e..5942202c2da 100644 --- a/packages/components/src/components/link/link.lite.tsx +++ b/packages/components/src/components/link/link.lite.tsx @@ -28,7 +28,6 @@ export default function DBLink(props: DBLinkProps) { id={props.id} class={cls('db-link', props.className)} href={props.href} - title={props.title} target={props.target} rel={props.rel} role={props.role} diff --git a/packages/components/src/shared/model.ts b/packages/components/src/shared/model.ts index 00cc2782cec..17ff80f84a7 100644 --- a/packages/components/src/shared/model.ts +++ b/packages/components/src/shared/model.ts @@ -25,16 +25,6 @@ export type GlobalProps = { * React specific for render process. */ key?: string; - - /** - * The default tabindex (https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/tabindex?retiredLocale=de). - */ - tabIndex?: number; - - /** - * The [title attribute](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/title) specifies the tooltip of the component. - */ - title?: string; }; export type GlobalState = { diff --git a/packages/components/src/utils/index.ts b/packages/components/src/utils/index.ts index 101b3d451f4..720be46ad39 100644 --- a/packages/components/src/utils/index.ts +++ b/packages/components/src/utils/index.ts @@ -1,4 +1,5 @@ import { DefaultVariantType } from '../shared/model'; +import { AriaRole, CSSProperties } from 'react'; export const uuid = () => { if (typeof window !== 'undefined') { @@ -60,6 +61,43 @@ export const getMessageIcon = ( : undefined; }; +const reactHtmlAttributes = [ + 'suppressHydrationWarning', + 'suppressContentEditableWarning', + 'translate', + 'title', + 'tabIndex', + 'style', + 'spellCheck', + 'nonce', + 'lang', + 'hidden', + 'draggable', + 'dir', + 'contextMenu', + 'contentEditable', + 'autoFocus', + 'accessKey', + 'is', + 'inputMode', + 'unselectable', + 'security', + 'results', + 'vocab', + 'typeof', + 'rev', + 'resource', + 'rel', + 'property', + 'inlist', + 'datatype', + 'content', + 'about', + 'role', + 'radioGroup', + 'color' +]; + export const filterPassingProps = ( props: any, propsPassingFilter: string[] @@ -71,7 +109,9 @@ export const filterPassingProps = ( key.startsWith('aria-') || key.startsWith('default') || key.startsWith('auto') || - key.startsWith('on')) && + key.startsWith('item') || + key.startsWith('on') || + reactHtmlAttributes.includes(key)) && !propsPassingFilter.includes(key) ) .reduce((obj: any, key: string) => { diff --git a/showcases/angular-showcase/src/app/components/accordion-item/accordion-item.component.html b/showcases/angular-showcase/src/app/components/accordion-item/accordion-item.component.html index 0ae79951969..242cec143fb 100644 --- a/showcases/angular-showcase/src/app/components/accordion-item/accordion-item.component.html +++ b/showcases/angular-showcase/src/app/components/accordion-item/accordion-item.component.html @@ -11,7 +11,7 @@ let-variantIndex="variantIndex" > diff --git a/showcases/angular-showcase/src/app/components/accordion/accordion.component.html b/showcases/angular-showcase/src/app/components/accordion/accordion.component.html index 780da5e5a3b..5b47400fba3 100644 --- a/showcases/angular-showcase/src/app/components/accordion/accordion.component.html +++ b/showcases/angular-showcase/src/app/components/accordion/accordion.component.html @@ -18,15 +18,15 @@ [variant]="exampleProps.variant" > diff --git a/showcases/react-showcase/src/components/accordion-item/index.tsx b/showcases/react-showcase/src/components/accordion-item/index.tsx index 1f3d2285a9d..6aebe83c14a 100644 --- a/showcases/react-showcase/src/components/accordion-item/index.tsx +++ b/showcases/react-showcase/src/components/accordion-item/index.tsx @@ -9,10 +9,13 @@ const getAccordionItem = ({ children, disabled, open, - title + headline }: DBAccordionItemProps & { open: boolean }) => { return ( - + {children} ); diff --git a/showcases/react-showcase/src/components/accordion/index.tsx b/showcases/react-showcase/src/components/accordion/index.tsx index 1bc64933077..4998ca6e949 100644 --- a/showcases/react-showcase/src/components/accordion/index.tsx +++ b/showcases/react-showcase/src/components/accordion/index.tsx @@ -15,9 +15,9 @@ const getAccordion = ({ behaviour, children, variant }: DBAccordionProps) => ( {children} - - - + + + ); diff --git a/showcases/react-showcase/src/components/brand/index.tsx b/showcases/react-showcase/src/components/brand/index.tsx index 466f4d804e5..3903f4331f3 100644 --- a/showcases/react-showcase/src/components/brand/index.tsx +++ b/showcases/react-showcase/src/components/brand/index.tsx @@ -18,9 +18,7 @@ const getBrand = ({ className, describedbyid, id, - key, - tabIndex, - title + key }: DBBrandProps) => ( + key={key}> {children} ); diff --git a/showcases/react-showcase/src/components/header/index.tsx b/showcases/react-showcase/src/components/header/index.tsx index 996286644dc..54d8245eee8 100644 --- a/showcases/react-showcase/src/components/header/index.tsx +++ b/showcases/react-showcase/src/components/header/index.tsx @@ -20,8 +20,6 @@ const getHeader = ({ describedbyid, id, key, - tabIndex, - title, onToggle }: DBHeaderProps) => ( diff --git a/showcases/react-showcase/src/components/page/index.tsx b/showcases/react-showcase/src/components/page/index.tsx index 2d607755a59..cbcb47a944a 100644 --- a/showcases/react-showcase/src/components/page/index.tsx +++ b/showcases/react-showcase/src/components/page/index.tsx @@ -19,9 +19,7 @@ const getPage = ({ className, describedbyid, id, - key, - tabIndex, - title + key }: DBPageProps) => ( - } - title={title}> + }> {children} diff --git a/showcases/shared/accordion-item.json b/showcases/shared/accordion-item.json index 3d60b6d0e61..ab6e64d7ac8 100644 --- a/showcases/shared/accordion-item.json +++ b/showcases/shared/accordion-item.json @@ -6,21 +6,21 @@ "name": "Functional", "className": "db-ui-functional", "props": { - "title": "Functional" + "headline": "Functional" } }, { "name": "(Default) Regular", "className": "db-ui-regular", "props": { - "title": "(Default) Regular" + "headline": "(Default) Regular" } }, { "name": "Expressive", "className": "db-ui-expressive", "props": { - "title": "Expressive" + "headline": "Expressive" } } ] @@ -31,13 +31,13 @@ { "name": "Enabled (Default)/Hover/Pressed", "props": { - "title": "Enabled (Default)/Hover/Pressed" + "headline": "Enabled (Default)/Hover/Pressed" } }, { "name": "Disabled", "props": { - "title": "Disabled", + "headline": "Disabled", "disabled": true } } @@ -49,13 +49,13 @@ { "name": "(Default) Collapsed", "props": { - "title": "(Default) Collapsed" + "headline": "(Default) Collapsed" } }, { "name": "Open", "props": { - "title": "Open", + "headline": "Open", "open": true } } diff --git a/showcases/shared/accordion.json b/showcases/shared/accordion.json index 241caa2bb2d..de55bca3a76 100644 --- a/showcases/shared/accordion.json +++ b/showcases/shared/accordion.json @@ -8,9 +8,9 @@ "props": {}, "code": { "html": "The accordion is a pure JS Component", - "angular": "\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t", - "react": "\n\t\t\t\n\t\t\t\n\t\t\t\n\t\t", - "vue": "\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t" + "angular": "\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t", + "react": "\n\t\t\t\n\t\t\t\n\t\t\t\n\t\t", + "vue": "\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t" } }, { @@ -19,9 +19,9 @@ "props": {}, "code": { "html": "The accordion is a pure JS Component", - "angular": "\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t", - "react": "\n\t\t\t\n\t\t\t\n\t\t\t\n\t\t", - "vue": "\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t" + "angular": "\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t", + "react": "\n\t\t\t\n\t\t\t\n\t\t\t\n\t\t", + "vue": "\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t" } }, { @@ -30,9 +30,9 @@ "props": {}, "code": { "html": "The accordion is a pure JS Component", - "angular": "\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t", - "react": "\n\t\t\t\n\t\t\t\n\t\t\t\n\t\t", - "vue": "\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t" + "angular": "\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t", + "react": "\n\t\t\t\n\t\t\t\n\t\t\t\n\t\t", + "vue": "\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t" } } ] @@ -74,9 +74,9 @@ }, "code": { "html": "The accordion is a pure JS Component", - "angular": "\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t", - "react": "\n\t\t\t\n\t\t\t\n\t\t\t\n\t\t", - "vue": "\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t" + "angular": "\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t", + "react": "\n\t\t\t\n\t\t\t\n\t\t\t\n\t\t", + "vue": "\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t" } }, { @@ -86,9 +86,9 @@ }, "code": { "html": "The accordion is a pure JS Component", - "angular": "\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t", - "react": "\n\t\t\t\n\t\t\t\n\t\t\t\n\t\t", - "vue": "\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t" + "angular": "\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t", + "react": "\n\t\t\t\n\t\t\t\n\t\t\t\n\t\t", + "vue": "\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t" } } ] diff --git a/showcases/vue-showcase/src/components/accordion-item/AccordionItem.vue b/showcases/vue-showcase/src/components/accordion-item/AccordionItem.vue index 06eb3c0d278..1074847a434 100644 --- a/showcases/vue-showcase/src/components/accordion-item/AccordionItem.vue +++ b/showcases/vue-showcase/src/components/accordion-item/AccordionItem.vue @@ -14,7 +14,7 @@ import { ref } from "vue"; #example="{ exampleIndex, variantIndex, exampleName, exampleProps }" > diff --git a/showcases/vue-showcase/src/components/accordion/Accordion.vue b/showcases/vue-showcase/src/components/accordion/Accordion.vue index 50845e401ef..fb45ed09f69 100644 --- a/showcases/vue-showcase/src/components/accordion/Accordion.vue +++ b/showcases/vue-showcase/src/components/accordion/Accordion.vue @@ -20,9 +20,9 @@ import { :behaviour="exampleProps.behaviour" :variant="exampleProps.variant" > - - - + + + From b9b180cb5c4682c45741778c4b308ff2545d4187 Mon Sep 17 00:00:00 2001 From: Nicolas Merget <104347736+nmerget@users.noreply.github.com> Date: Thu, 11 Apr 2024 11:31:30 +0200 Subject: [PATCH 2/6] docs: update overview for checkbox and radio (#2484) --- showcases/shared/checkbox.json | 83 ++++++++++++++++++++++++++-------- showcases/shared/radio.json | 59 +++++++++++++++--------- 2 files changed, 101 insertions(+), 41 deletions(-) diff --git a/showcases/shared/checkbox.json b/showcases/shared/checkbox.json index 3166ee6af5f..ef0b850bc31 100644 --- a/showcases/shared/checkbox.json +++ b/showcases/shared/checkbox.json @@ -25,6 +25,24 @@ } ] }, + { + "name": "Interaction States", + "examples": [ + { + "name": "(Default) Enabled", + "props": { + "name": "Interaction States" + } + }, + { + "name": "Disabled", + "props": { + "name": "Interaction States", + "disabled": true + } + } + ] + }, { "name": "States", "examples": [ @@ -35,48 +53,47 @@ } }, { - "name": "Checked", + "name": "Unchecked - Invalid", "props": { "name": "States", - "checked": true + "required": true } }, { - "name": "Indeterminate", + "name": "Checked", "props": { "name": "States", - "indeterminate": true + "checked": true } }, { - "name": "Disabled", + "name": "Checked - Valid", "props": { "name": "States", - "disabled": true + "required": true, + "checked": true } - } - ] - }, - { - "name": "Requirement", - "examples": [ + }, { - "name": "(Default) Optional", + "name": "Indeterminate", "props": { - "name": "Requirement" + "name": "States", + "indeterminate": true } }, { - "name": "Required", + "name": "Indeterminate - Invalid", "props": { - "name": "Requirement", + "name": "States", + "indeterminate": true, "required": true } }, { - "name": "Required - Checked", + "name": "Indeterminate - Valid", "props": { - "name": "Requirement", + "name": "States", + "indeterminate": true, "required": true, "checked": true } @@ -102,7 +119,33 @@ ] }, { - "name": "Variant Label", + "name": "Requirement", + "examples": [ + { + "name": "(Default) Optional", + "props": { + "name": "Requirement" + } + }, + { + "name": "Required", + "props": { + "name": "Requirement", + "required": true + } + }, + { + "name": "Required - Checked", + "props": { + "name": "Requirement", + "required": true, + "checked": true + } + } + ] + }, + { + "name": "Content", "examples": [ { "name": "(Default) Label", @@ -111,7 +154,7 @@ } }, { - "name": "Hidden Label", + "name": "No Label", "props": { "name": "Content", "labelVariant": "hidden" diff --git a/showcases/shared/radio.json b/showcases/shared/radio.json index fec5e1e8a10..f57be93cbfe 100644 --- a/showcases/shared/radio.json +++ b/showcases/shared/radio.json @@ -29,51 +29,50 @@ ] }, { - "name": "States", + "name": "Interaction States", "examples": [ { - "name": "(Default) Unchecked", + "name": "(Default) Enabled", "props": { - "name": "States" - } - }, - { - "name": "Checked", - "props": { - "name": "States", - "checked": true + "name": "Interaction States" } }, { "name": "Disabled", "props": { - "name": "States", + "name": "Interaction States", "disabled": true } } ] }, { - "name": "Requirement", + "name": "States", "examples": [ { - "name": "(Default) Optional", + "name": "(Default) Unselected", "props": { - "name": "Requirement" + "name": "States" } }, { - "name": "Required", + "name": "Unselected - Invalid", "props": { - "name": "Requirement", - "required": true, - "invalid": true + "name": "States Required", + "required": true } }, { - "name": "Required - Checked", + "name": "Selected", "props": { - "name": "Requirement", + "name": "States", + "checked": true + } + }, + { + "name": "Selected - Valid", + "props": { + "name": "States Required", "required": true, "checked": true } @@ -99,7 +98,25 @@ ] }, { - "name": "Variant Label", + "name": "Requirement", + "examples": [ + { + "name": "(Default) Optional", + "props": { + "name": "Requirement" + } + }, + { + "name": "Required", + "props": { + "name": "Requirement", + "required": true + } + } + ] + }, + { + "name": "Content", "examples": [ { "name": "(Default) Label", From e6f091db22e7db51f103f5be45cb7709465af093 Mon Sep 17 00:00:00 2001 From: Nicolas Merget <104347736+nmerget@users.noreply.github.com> Date: Thu, 11 Apr 2024 11:38:30 +0200 Subject: [PATCH 3/6] feat: add auto close to navigation-item inside header and main-navigation (#2344) --- .../src/components/header/header.lite.tsx | 17 ++++++++-- .../components/src/components/header/model.ts | 4 ++- .../main-navigation/main-navigation.lite.tsx | 33 ++++++++++++++++--- .../main-navigation/main-navigation.scss | 8 +++++ .../src/components/main-navigation/model.ts | 16 +++++++-- packages/components/src/shared/model.ts | 4 +++ packages/components/src/utils/navigation.ts | 11 +++++++ 7 files changed, 82 insertions(+), 11 deletions(-) create mode 100644 packages/components/src/utils/navigation.ts diff --git a/packages/components/src/components/header/header.lite.tsx b/packages/components/src/components/header/header.lite.tsx index 938947f0537..4d904364dbc 100644 --- a/packages/components/src/components/header/header.lite.tsx +++ b/packages/components/src/components/header/header.lite.tsx @@ -1,17 +1,17 @@ import { onMount, onUpdate, - Show, Slot, useMetadata, useRef, useStore } from '@builder.io/mitosis'; -import { DBHeaderState, DBHeaderProps } from './model'; +import { DBHeaderProps, DBHeaderState } from './model'; import { addAttributeToChildren, cls, uuid } from '../../utils'; import { DBButton } from '../button'; import { DBDrawer } from '../drawer'; import { DEFAULT_ID } from '../../shared/constants'; +import { isEventTargetNavigationItem } from '../../utils/navigation'; useMetadata({ isAttachedToShadowDom: true @@ -31,6 +31,11 @@ export default function DBHeader(props: DBHeaderProps) { if (props.onToggle) { props.onToggle(!props.drawerOpen); } + }, + handleNavigationItemClick: (event: unknown) => { + if (isEventTargetNavigationItem(event)) { + state.toggle(); + } } }); @@ -72,7 +77,13 @@ export default function DBHeader(props: DBHeaderProps) { open={props.drawerOpen} onClose={() => state.toggle()}>
-
{props.children}
+
+ state.handleNavigationItemClick(event) + }> + {props.children} +
diff --git a/packages/components/src/components/header/model.ts b/packages/components/src/components/header/model.ts index 0f83b7d4a9e..38baeaee0cf 100644 --- a/packages/components/src/components/header/model.ts +++ b/packages/components/src/components/header/model.ts @@ -2,6 +2,7 @@ import { GlobalProps, GlobalState, InitializedState, + NavigationBehaviourState, ToggleEventProps, ToggleEventState } from '../../shared/model'; @@ -38,4 +39,5 @@ export interface DBHeaderDefaultState { export type DBHeaderState = DBHeaderDefaultState & GlobalState & ToggleEventState & - InitializedState; + InitializedState & + NavigationBehaviourState; diff --git a/packages/components/src/components/main-navigation/main-navigation.lite.tsx b/packages/components/src/components/main-navigation/main-navigation.lite.tsx index 0a61b274f5a..2f2b6cad965 100644 --- a/packages/components/src/components/main-navigation/main-navigation.lite.tsx +++ b/packages/components/src/components/main-navigation/main-navigation.lite.tsx @@ -1,13 +1,14 @@ import { onMount, - Show, + onUpdate, useMetadata, useRef, useStore } from '@builder.io/mitosis'; -import { DBMainNavigationState, DBMainNavigationProps } from './model'; +import { DBMainNavigationProps, DBMainNavigationState } from './model'; import { cls, uuid } from '../../utils'; import { DEFAULT_ID } from '../../shared/constants'; +import { isEventTargetNavigationItem } from '../../utils/navigation'; useMetadata({ isAttachedToShadowDom: true @@ -17,20 +18,44 @@ export default function DBMainNavigation(props: DBMainNavigationProps) { const ref = useRef(null); // jscpd:ignore-start const state = useStore({ - _id: DEFAULT_ID + _id: DEFAULT_ID, + initialized: false, + forceClose: false, + handleNavigationItemClick: (event: unknown) => { + if (isEventTargetNavigationItem(event)) { + state.forceClose = true; + } + } }); onMount(() => { state._id = props.id || 'main-navigation-' + uuid(); + state.initialized = true; }); + onUpdate(() => { + if (ref && state.initialized) { + state.initialized = false; + } + }, [ref, state.initialized]); + + onUpdate(() => { + if (state.forceClose) { + requestAnimationFrame(() => { + state.forceClose = false; + }); + } + }, [state.forceClose]); + // jscpd:ignore-end return ( ); diff --git a/packages/components/src/components/main-navigation/main-navigation.scss b/packages/components/src/components/main-navigation/main-navigation.scss index 398f9902787..6b5135f4b77 100644 --- a/packages/components/src/components/main-navigation/main-navigation.scss +++ b/packages/components/src/components/main-navigation/main-navigation.scss @@ -89,4 +89,12 @@ } } } + + &[data-force-close="true"] { + & > menu menu { + @include screen-sizes.screen("md") { + display: none; + } + } + } } diff --git a/packages/components/src/components/main-navigation/model.ts b/packages/components/src/components/main-navigation/model.ts index 00b497d9644..3d70bd15474 100644 --- a/packages/components/src/components/main-navigation/model.ts +++ b/packages/components/src/components/main-navigation/model.ts @@ -1,9 +1,19 @@ -import { GlobalProps, GlobalState } from '../../shared/model'; +import { + GlobalProps, + GlobalState, + InitializedState, + NavigationBehaviourState +} from '../../shared/model'; export interface DBMainNavigationDefaultProps {} export type DBMainNavigationProps = DBMainNavigationDefaultProps & GlobalProps; -export interface DBMainNavigationDefaultState {} +export interface DBMainNavigationDefaultState { + forceClose: boolean; +} -export type DBMainNavigationState = DBMainNavigationDefaultState & GlobalState; +export type DBMainNavigationState = DBMainNavigationDefaultState & + InitializedState & + GlobalState & + NavigationBehaviourState; diff --git a/packages/components/src/shared/model.ts b/packages/components/src/shared/model.ts index 17ff80f84a7..d66ffdcb947 100644 --- a/packages/components/src/shared/model.ts +++ b/packages/components/src/shared/model.ts @@ -86,6 +86,10 @@ export type PlacementProps = { | 'bottom-end'; }; +export type NavigationBehaviourState = { + handleNavigationItemClick: (event: unknown) => void; +}; + export type GapProps = { /** * If the absolute element should have a gap between the parent element. diff --git a/packages/components/src/utils/navigation.ts b/packages/components/src/utils/navigation.ts new file mode 100644 index 00000000000..d10bdb6679c --- /dev/null +++ b/packages/components/src/utils/navigation.ts @@ -0,0 +1,11 @@ +export const isEventTargetNavigationItem = (event: unknown): boolean => { + const { target } = event as { target: HTMLElement }; + return Boolean( + !target?.classList?.contains('db-navigation-item-expand-button') && + target?.parentElement?.classList.contains('db-navigation-item') + ); +}; + +export default { + isEventTargetNavigationItem +}; From 91ba8bfe48eef2ed513c1560bae1deeaa961e713 Mon Sep 17 00:00:00 2001 From: Nicolas Merget <104347736+nmerget@users.noreply.github.com> Date: Thu, 11 Apr 2024 12:55:14 +0200 Subject: [PATCH 4/6] fix: issue with user-valid for form-components (#2496) --- .../src/components/checkbox/checkbox.scss | 2 +- .../src/components/radio/radio.scss | 2 +- .../src/styles/_form-components.scss | 55 ++++++++----------- 3 files changed, 26 insertions(+), 33 deletions(-) diff --git a/packages/components/src/components/checkbox/checkbox.scss b/packages/components/src/components/checkbox/checkbox.scss index f23458763d6..5c39c8c7db2 100644 --- a/packages/components/src/components/checkbox/checkbox.scss +++ b/packages/components/src/components/checkbox/checkbox.scss @@ -6,7 +6,7 @@ @use "../../styles/form-components"; .db-checkbox { - @extend %check-element; + @include form-components.set-default-check-element(check); input { border-radius: variables.$db-border-radius-sm; diff --git a/packages/components/src/components/radio/radio.scss b/packages/components/src/components/radio/radio.scss index dd94fd6d16a..19cd4a84af2 100644 --- a/packages/components/src/components/radio/radio.scss +++ b/packages/components/src/components/radio/radio.scss @@ -3,7 +3,7 @@ @use "../../styles/form-components"; .db-radio { - @extend %check-element; + @include form-components.set-default-check-element(radio); input { border-color: currentColor; diff --git a/packages/components/src/styles/_form-components.scss b/packages/components/src/styles/_form-components.scss index d902c9e92bf..01f1c0f3d62 100644 --- a/packages/components/src/styles/_form-components.scss +++ b/packages/components/src/styles/_form-components.scss @@ -136,33 +136,24 @@ $floating-label-size: calc( } } -@function get-validations($selector, $key) { - $has-selectors: ""; +// This doesn't contain text, search and password, because they don't have an auto-validation +$input-valid-types: "color", "date", "datetime-local", "email", "file", "hidden", + "month", "number", "range", "tel", "time", "url", "week"; +@function get-validations($selector, $key) { $validations: ":required"; - $user: "user-"; - - // Differentiating form elements - @if ($selector == select or $selector == check) { - $user: ""; - } @else { - // TODO: add validations for input & textarea as well - // https://developer.mozilla.org/en-US/docs/Learn/Forms/Form_validation#using_built-in_form_validation - $validations: ":required"; - } - // We need to do a more complicated check for non-typed-input-elements @if ($selector == input) { - $validations: ':not([type="text"], [type="password"], [type="search"]),:is([type="text"], [type="password"], [type="search"]):required'; - } - - // Check elements (radio and checkbox) should get selected by their native HTML tag `input` - @if ($selector == check) { - $selector: input; + // https://developer.mozilla.org/en-US/docs/Learn/Forms/Form_validation#using_built-in_form_validation + $validations: $validations + ", [minlength], [maxlength], [pattern]"; + @each $type in $input-valid-types { + $validations: $validations + ", [type=#{$type}]"; + } + } @else if($selector == textarea) { + $validations: $validations + ", [minlength], [maxlength]"; } - $has-selectors: $has-selectors + - "&:has(#{$selector}:not([aria-invalid]):is(#{$validations}):#{$user}#{$key}),"; + $has-selectors: "&:has(#{$selector}:not([aria-invalid]):is(#{$validations}):user-#{$key}),"; @return string.unquote(string.slice($has-selectors, 1, -2)); } @@ -173,17 +164,19 @@ $floating-label-size: calc( $boolean: "false"; } - #{get-validations($selector,$key)} { - @content; + $user: ""; + @if ($selector == check) { + $user: "user-"; } - @if ($selector == check) { + @if ($selector == check or $selector == radio) { $selector: input; - } - // :user-valid or :user-invalid workaround - @supports not selector(:user-#{$key}) { - &:has(#{$selector}:not([aria-invalid]):required:#{$key}) { + &:has(#{$selector}:not([aria-invalid]):required:#{$user}#{$key}) { + @content; + } + } @else { + #{get-validations($selector,$key)} { @content; } } @@ -367,13 +360,13 @@ $floating-label-size: calc( } } -%check-element { +@mixin set-default-check-element($selector) { @include set-required-label(input); - @include get-validity(check) { + @include get-validity($selector) { @include get-validity-color-check("valid"); } - @include get-validity(check, "invalid") { + @include get-validity($selector, "invalid") { @include get-validity-color-check("invalid"); } From ed6786c7b85a7ba43516083b8bd2c0a6452a3634 Mon Sep 17 00:00:00 2001 From: NicolasMerget Date: Thu, 11 Apr 2024 14:17:28 +0200 Subject: [PATCH 5/6] feat: add customValidity --- packages/components/docs/Validation.md | 6 ++- .../src/components/checkbox/checkbox.lite.tsx | 3 +- .../src/components/input/input.lite.tsx | 3 +- .../src/components/radio/radio.lite.tsx | 3 +- .../src/components/select/select.lite.tsx | 3 +- .../src/components/textarea/textarea.lite.tsx | 4 +- packages/components/src/shared/model.ts | 9 ++-- .../src/styles/_form-components.scss | 13 +++-- .../components/validation-example/index.tsx | 53 +++++++++++++++++++ .../pages/components/validation.mdx | 3 ++ showcases/patternhub/styles/globals.scss | 12 +++++ 11 files changed, 95 insertions(+), 17 deletions(-) create mode 100644 showcases/patternhub/components/validation-example/index.tsx diff --git a/packages/components/docs/Validation.md b/packages/components/docs/Validation.md index 3527129af39..150846e43d0 100644 --- a/packages/components/docs/Validation.md +++ b/packages/components/docs/Validation.md @@ -11,6 +11,8 @@ If you use some framework you can pass the props `invalidMessage` and `validMessage` to the component. If you use plain html you need to add 2 `.db-infotext` with `[data-semantic="successful"]` &`[data-semantic="critical"]` inside your form-element. -## Force valid/invalid +## Handle validation by your self -You can use `invalid="true/false"` or `data-invalid="true/false"` to overwrite the default behaviour of [`:user-valid`](https://developer.mozilla.org/en-US/docs/Web/CSS/:user-valid). +You can use `data-custom-validity|customValidity="'invalid' | 'valid' | 'no-validation'"` to disable [`:user-valid`](https://developer.mozilla.org/en-US/docs/Web/CSS/:user-valid). + +> **Note:** This may lead to problems and inconsistency, only use it if you know what you do! diff --git a/packages/components/src/components/checkbox/checkbox.lite.tsx b/packages/components/src/components/checkbox/checkbox.lite.tsx index 25f659304a6..4042d0223a0 100644 --- a/packages/components/src/components/checkbox/checkbox.lite.tsx +++ b/packages/components/src/components/checkbox/checkbox.lite.tsx @@ -121,6 +121,8 @@ export default function DBCheckbox(props: DBCheckboxProps) { data-variant={props.variant}> ) => diff --git a/packages/components/src/components/select/select.lite.tsx b/packages/components/src/components/select/select.lite.tsx index 4cf1d6c8fb9..fbf909d9c1d 100644 --- a/packages/components/src/components/select/select.lite.tsx +++ b/packages/components/src/components/select/select.lite.tsx @@ -108,8 +108,9 @@ export default function DBSelect(props: DBSelectProps) { data-icon={props.icon}>