Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor(accordion): adapt native standard for behaviour #3218

Merged
merged 30 commits into from
Nov 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
ea1dc79
refactor: first try
mfranzke Sep 22, 2024
626defa
refactor: another try
mfranzke Sep 22, 2024
2c32f13
chore: update implementation for accordion
nmerget Sep 24, 2024
0e6f635
chore: update sr snapshots
nmerget Sep 24, 2024
f36eb20
fix: issue with initOpenIndex
nmerget Sep 25, 2024
07882a6
fix: issue with typescript
nmerget Sep 25, 2024
6ec5788
Merge branch 'main' into 1630-accordion-adapt-native-standard
mfranzke Sep 25, 2024
3733abf
fix: issue from PR
nmerget Oct 31, 2024
194fee4
chore: update from main
nmerget Oct 31, 2024
61e0842
fix: issue from stencil typescript
nmerget Oct 31, 2024
0825356
fix: issue with accordion item
nmerget Oct 31, 2024
b0839d1
fix: issue with component test for accordion item
nmerget Oct 31, 2024
1861da7
fix: issue with component test for vue
nmerget Oct 31, 2024
681d428
Update accordion-a11y.spec.ts
mfranzke Nov 19, 2024
90bfe09
chore: update from main
nmerget Nov 19, 2024
58ab085
Merge remote-tracking branch 'origin/1630-accordion-adapt-native-stan…
nmerget Nov 19, 2024
fe8cfc1
Merge branch 'main' into 1630-accordion-adapt-native-standard
mfranzke Nov 19, 2024
9e4e319
Update accordion-a11y.spec.ts
mfranzke Nov 19, 2024
3a5058f
Update accordion-item-a11y.spec.ts
mfranzke Nov 19, 2024
f9d2ab6
fix: issue with linting
nmerget Nov 19, 2024
2d3c79f
Merge remote-tracking branch 'origin/1630-accordion-adapt-native-stan…
nmerget Nov 19, 2024
5aa4277
Update accordion.scss
mfranzke Nov 19, 2024
1c07a91
Merge remote-tracking branch 'origin/1630-accordion-adapt-native-stan…
nmerget Nov 19, 2024
bf37c15
Update accordion-item.scss
mfranzke Nov 19, 2024
87c1e7b
Update accordion-item.spec.tsx
mfranzke Nov 19, 2024
d70f0f1
Update packages/components/src/components/accordion/accordion.lite.tsx
nmerget Nov 19, 2024
98d050e
chore: update snapshots
nmerget Nov 19, 2024
6439579
Merge branch 'main' into 1630-accordion-adapt-native-standard
mfranzke Nov 19, 2024
6144f25
chore: update snapshots
nmerget Nov 19, 2024
c7f5019
Merge remote-tracking branch 'origin/1630-accordion-adapt-native-stan…
nmerget Nov 19, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions packages/components/scripts/post-build/angular.ts
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,10 @@ export default (tmp?: boolean) => {
{
from: '} from "../../utils"',
to: ', enableCustomElementAttributePassing } from "../../utils"'
},
{
from: /this.ref.nativeElement/g,
to: 'this.ref?.nativeElement'
nmerget marked this conversation as resolved.
Show resolved Hide resolved
}
];

Expand Down
10 changes: 1 addition & 9 deletions packages/components/scripts/post-build/components.ts
Original file line number Diff line number Diff line change
Expand Up @@ -195,15 +195,7 @@ export const getComponents = (): Component[] => [
},

{
name: 'tag',
overwrites: {
angular: [
{
from: /this.ref.nativeElement/g,
to: 'this.ref?.nativeElement'
}
]
}
name: 'tag'
},
{
name: 'checkbox',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,23 +39,25 @@ export default function DBAccordionItem(props: DBAccordionItemProps) {
// jscpd:ignore-end

return (
<details
ref={ref}
id={state._id}
class={cls('db-accordion-item', props.className)}
aria-disabled={getBooleanAsString(props.disabled)}
open={state._open}
name={props.name}>
<summary onClick={(event) => state.toggle(event)}>
<Show when={props.headlinePlain}>{props.headlinePlain}</Show>
<Show when={!props.headlinePlain}>
<Slot name="headline" />
</Show>
</summary>
<div>
<Show when={props.content}>{props.content}</Show>
<Show when={!props.content}>{props.children}</Show>
</div>
</details>
<li id={state._id} class={cls('db-accordion-item', props.className)}>
<details
aria-disabled={getBooleanAsString(props.disabled)}
ref={ref}
open={state._open}>
<summary onClick={(event) => state.toggle(event)}>
<Show when={props.headlinePlain}>
{props.headlinePlain}
</Show>
<Show when={!props.headlinePlain}>
<Slot name="headline" />
</Show>
</summary>
<div>
<Show when={props.content} else={props.children}>
{props.content}
</Show>
</div>
</details>
</li>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,33 @@ $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;
Expand Down Expand Up @@ -54,17 +70,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;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ const testA11y = () => {
await mount(comp);
const accessibilityScanResults = await new AxeBuilder({ page })
.include('.db-accordion-item')
// Showcase uses <li> outside of <ul> in this case
// TODO: Let's investigate whether we could prevent this deactivation later on
.disableRules(['listitem'])
.analyze();

expect(accessibilityScanResults.violations).toEqual([]);
Expand Down
4 changes: 0 additions & 4 deletions packages/components/src/components/accordion-item/model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,6 @@ export type DBAccordionItemDefaultProps = {
* Title of the accordion-item as plain text
*/
headlinePlain?: string;
/**
* Set details name for exclusive accordions, see https://chromestatus.com/feature/6710427028815872
*/
name?: string;
};

export type DBAccordionItemProps = DBAccordionItemDefaultProps &
Expand Down
121 changes: 57 additions & 64 deletions packages/components/src/components/accordion/accordion.lite.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,18 +9,20 @@ import {
} 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 { DEFAULT_ID } from '../../shared/constants';

useMetadata({});

export default function DBAccordion(props: DBAccordionProps) {
const ref = useRef<HTMLDivElement>(null);
const ref = useRef<HTMLUListElement>(null);
// jscpd:ignore-start
const state = useStore<DBAccordionState>({
openItems: [],
clickedId: '',
_id: DEFAULT_ID,
_name: '',
initialized: false,
_initOpenIndexDone: false,
convertItems(
items: unknown[] | string | undefined
): DBAccordionItemDefaultProps[] {
Expand All @@ -35,89 +37,80 @@ export default function DBAccordion(props: DBAccordionProps) {
}

return [];
},
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);
}
}
});

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<HTMLDetailsElement>(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<HTMLDetailsElement>(childDetails).forEach(
(details: HTMLDetailsElement) => {
details.open = state.openItems.includes(details.id);
for (const details of Array.from<HTMLDetailsElement>(
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) {
nmerget marked this conversation as resolved.
Show resolved Hide resolved
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<HTMLDetailsElement>(childDetails).forEach(
(details: HTMLDetailsElement, index: number) => {
if (initOpenIndex.includes(index)) {
details.open = true;
}
}
);
}
}
state._initOpenIndexDone = false;
}
}, [state.openItems]);
}, [ref, state._initOpenIndexDone, props.initOpenIndex]);

return (
<div
<ul
ref={ref}
id={props.id}
id={state._id}
class={cls('db-accordion', props.className)}
data-variant={props.variant}>
<Show when={!props.items}>{props.children}</Show>
Expand All @@ -133,6 +126,6 @@ export default function DBAccordion(props: DBAccordionProps) {
)}
</For>
</Show>
</div>
</ul>
);
}
4 changes: 4 additions & 0 deletions packages/components/src/components/accordion/accordion.scss
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ The styling for the spacings between the items is defined in the accordion, wher
The spacings are not part of the styling of the accordion items themselves.
*/
.db-accordion {
padding: 0;
margin: 0;
list-style-type: "";

/*
default variant: prop variant is not set
*/
Expand Down
14 changes: 10 additions & 4 deletions packages/components/src/components/accordion/accordion.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,16 @@ const openAccordion: any = (

const actionAccordion: any = (
<DBAccordion behaviour="single">
<DBAccordionItem data-testid="item1" headline="Test">
<DBAccordionItem data-testid="item1" headlinePlain="Test">
<DBButton data-testid="button">Click me</DBButton>
</DBAccordionItem>
<DBAccordionItem data-testid="item2" headline="Test 2">
<DBAccordionItem data-testid="item2" headlinePlain="Test 2">
<DBTextarea data-testid="textarea" />
</DBAccordionItem>
<DBAccordionItem disabled={true} data-testid="item3" headline="Test 3">
<DBAccordionItem
disabled={true}
data-testid="item3"
headlinePlain="Test 3">
<DBButton data-testid="button2">Click me</DBButton>
</DBAccordionItem>
</DBAccordion>
Expand Down Expand Up @@ -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 }) => {
Expand Down
Loading
Loading