Skip to content

Commit

Permalink
[feat](@svelteui/core]: Accordion component (#412)
Browse files Browse the repository at this point in the history
* [WIP]: accordion component

* [feat](@svelteui/core): move control to accordion Item and make styles functional

* [feat](@svelteui/core): support multiple for accordion

* [feat](@svelteui/core): wAI-ARIA support for Accordion

* [fix](@svelteui/core): fix Collapse types

* [feat](@svelteui/core): remove control component and cleanup cide

* [fix](@svelteui/core): remove unused import

* [feat](@svelteui/core): improve Accordion types

* [feat](@svelteui/core): remove icon from AccordionItem

* [fix](@svelteui/core): small fix on hover for Accordion

* [fix](@svelteui/core): small fix in Accordion style

* [feat](@svelteui/demos): accordion demos

* [feat](docs): accordion docs

* [test](@svelteui/core): unit test for Accordion
  • Loading branch information
BeeMargarida authored Jul 2, 2023
1 parent 0f4dc38 commit 7730ff4
Show file tree
Hide file tree
Showing 31 changed files with 1,413 additions and 2 deletions.
1 change: 1 addition & 0 deletions apps/docs/src/lib/components/Sidebar.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,7 @@
<li>
<strong><Dashboard /><Space w="md" />Data Display</strong>
<ul>
<li><a href={`${base}/core/accordion`}>Accordion</a></li>
<li><a href={`${base}/core/badge`}>Badge</a></li>
<li><a href={`${base}/core/card`}>Card</a></li>
<li><a href={`${base}/core/image`}>Image</a></li>
Expand Down
4 changes: 4 additions & 0 deletions apps/docs/src/lib/components/TopBar/data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,13 @@ export const searchLinks = [
{ title: 'SvelteUIProvider', link: `${base}/theming/svelteui-provider`, section: 'Theming' },
{ title: 'Utilities', link: `${base}/theming/stitches-utilities`, section: 'Theming' },

{ title: 'Accordion', link: `${base}/core/accordion`, section: 'Components' },
{ title: 'ActionIcon', link: `${base}/core/action-icon`, section: 'Components' },
{ title: 'Affix', link: `${base}/core/affix`, section: 'Components' },
{ title: 'Alert', link: `${base}/core/alert`, section: 'Components' },
{ title: 'Anchor', link: `${base}/core/anchor`, section: 'Components' },
{ title: 'AppShell', link: `${base}/core/app-shell`, section: 'Components' },
{ title: 'AspectRatio', link: `${base}/core/aspect-ratio`, section: 'Components' },
{ title: 'Badge', link: `${base}/core/badge`, section: 'Components' },
{ title: 'Box', link: `${base}/core/box`, section: 'Components' },
{ title: 'Burger', link: `${base}/core/burger`, section: 'Components' },
Expand All @@ -53,6 +55,7 @@ export const searchLinks = [
{ title: 'Collapse', link: `${base}/core/collapse`, section: 'Components' },
{ title: 'Container', link: `${base}/core/container`, section: 'Components' },
{ title: 'Divider', link: `${base}/core/divider`, section: 'Components' },
{ title: 'Flex', link: `${base}/core/flex`, section: 'Components' },
{ title: 'Grid', link: `${base}/core/grid`, section: 'Components' },
{ title: 'Group', link: `${base}/core/group`, section: 'Components' },
{ title: 'Image', link: `${base}/core/image`, section: 'Components' },
Expand All @@ -79,6 +82,7 @@ export const searchLinks = [
{ title: 'Stack', link: `${base}/core/stack`, section: 'Components' },
{ title: 'Switch', link: `${base}/core/switch`, section: 'Components' },
{ title: 'Tabs', link: `${base}/core/tabs`, section: 'Components' },
{ title: 'Textarea', link: `${base}/core/textarea`, section: 'Components' },
{ title: 'TextInput', link: `${base}/core/text-input`, section: 'Components' },
{ title: 'Text', link: `${base}/core/text`, section: 'Components' },
{ title: 'ThemeIcon', link: `${base}/core/theme-icon`, section: 'Components' },
Expand Down
9 changes: 9 additions & 0 deletions apps/docs/src/lib/data/main/homepage.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Accessibility, Dashboard, Box as BoxIcon } from 'radix-icons-svelte';
import TypeScript from '../../components/svgs/TypeScript.svelte';
import {
Accordion,
Container,
Center,
Collapse,
Expand Down Expand Up @@ -181,6 +182,14 @@ export const components = [
title: 'Switch',
color: '$blue600'
},
{
icon: BoxIcon,
component: Accordion,
link: 'core/accordion',
title: 'Accordion',
color: '$blue600',
content: 'BETA'
},
{
icon: BoxIcon,
component: Badge,
Expand Down
81 changes: 81 additions & 0 deletions apps/docs/src/routes/core/accordion/+page.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
---
title: Accordion
group: 'svelteuidev-core'
packageGroup: '@svelteuidev/core'
slug: /core/accordion/
category: 'data-display'
description: 'Shows and divides content into collapsible sections'
importCode: "import { Accordion } from '@svelteuidev/core';"
source: 'svelteui-core/src/components/Accordion/Accordion.svelte'
docs: 'core/accordion'
---

<script lang="ts">
import { Demo, AccordionDemos } from '@svelteuidev/demos';
import { Heading } from "$lib/components";
import { base } from '$app/paths';
</script>

<svelte:head>

<title>{title} - SvelteUI</title>
</svelte:head>

<Heading {title} {group} {packageGroup} {slug} {category} {description} {importCode} {source} {docs} />

## Usage

<Demo demo={AccordionDemos.configurator} />

## Customize Control

Control can be fully customizable by setting any king of element inside the `control` slot.

<Demo demo={AccordionDemos.custom} />

## Change chevron

<Demo demo={AccordionDemos.chevron} />

## Controlled

The accordion component can be controlled externally with the prop `value` (which can be binded) and with the `on:change` event.

<Demo demo={AccordionDemos.controlled} />

## Default open

It's possible to set default opened items with the prop `defaultValue`. When `multiple` is false, this value should be a string.

<Demo demo={AccordionDemos.defaultValue} />

When `multiple` is `true`, this should be provided as an arrays of strings.

<Demo demo={AccordionDemos.defaultValueMultiple} />

## Disabled items

<Demo demo={AccordionDemos.disabled} />

## Transition

It's possible to change the chevron transition duration by modifying the prop `transitionDuration` (in milliseconds).

<Demo demo={AccordionDemos.transitionDuration} />

To disable the transition completely, set `transitionDuration` to `0`.

<Demo demo={AccordionDemos.noTransition} />

## Data attributes

Each item exposes data attributes that can be used to style the component.

- `data-rotate` for the chevron, which tells if the chevron should rotate. If `disableChevronRotation` is set, it will always be false.
- `data-active` in `Accordion.Item` when it's content is expanded.

<Demo demo={AccordionDemos.data} />

## Accessibility

The accordion component follows the [WAI-ARIA recommendations](https://www.w3.org/WAI/ARIA/apg/patterns/accordion/examples/accordion/) on accessibility.
46 changes: 46 additions & 0 deletions packages/svelteui-core/src/components/Accordion/Accordion.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { HTMLAttributes } from 'svelte/elements';
import { Component } from '$lib/internal';
import type { DefaultProps, SvelteUINumberSize } from '$lib/styles';

export type AccordionVariant = 'default' | 'contained' | 'filled' | 'separated';

export type AccordionValue<Multiple> = Multiple extends true ? string[] : string | null;

export type AccordionContext<Multiple extends boolean = false> = Writable<{
currentValue?: AccordionValue<Multiple>;
variant?: AccordionVariant;
order?: 2 | 3 | 4 | 5 | 6;
radius?: SvelteUINumberSize | number;
chevron?: Component | HTMLOrSVGElement;
chevronPosition?: 'left' | 'right';
chevronSize?: string | number;
disableChevronRotation?: boolean;
transitionDuration?: number?;
updateActive: (value: string) => void;
isItemActive: (value: string) => boolean;
getControlsId: (value: string) => string;
getRegionId: (value: string) => string;
}>;

export interface AccordionProps<Multiple extends boolean = false>
extends DefaultProps,
HTMLAttributes<HTMLElement> {
variant?: AccordionVariant;
value?: AccordionValue<Multiple>;
defaultValue?: AccordionValue<Multiple>;
radius?: SvelteUINumberSize | number;
order?: 2 | 3 | 4 | 5 | 6;
multiple?: Multiple;
loop?: boolean;
id?: string;
chevron?: Component | HTMLOrSVGElement;
chevronPosition?: 'left' | 'right';
chevronSize?: string | number;
disableChevronRotation?: boolean;
transitionDuration?: number?;
}

export interface AccordionEvents<Multiple extends boolean = false> {
change: CustomEvent<AccordionValue<Multiple>>;
[evt: string]: CustomEvent<any>;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
<script lang="ts">
import { Meta, Template, Story } from '@storybook/addon-svelte-csf';
import { Accordion } from './index';
let value = 'typescript';
</script>

<Meta title="Components/Accordion" component={Accordion} />

<Template let:args>
<Accordion {...args} defaultValue="typescript">
<Accordion.Item value="typescript">
<div slot="control">Typescript Based</div>
Build type safe applications. All SvelteUI packages are built with TypeScript and support it by
default. All components and functions export types, are documented, and give developers autocomplete
features!
</Accordion.Item>
<Accordion.Item value="packed">
<div slot="control">Feature packed</div>
SvelteUI contains more than just components. With Actions, Transitions, and Utilities available
to you, development will be fun and easy!
</Accordion.Item>
<Accordion.Item value="accessible">
<div slot="control">Accessible and usable</div>
All components are accessible according to WAI-ARIA standards. On top of that, no annoying focus
ring. It will appear only when user navigates with keyboard.
</Accordion.Item>
</Accordion>
</Template>

<Story name="Accordion" id="accordionStory" />

<Story name="Multiple Tabs Open" id="accordionMultipleStory" args={{ multiple: true }} />

<Story name="Disabled Tabs" id="accordionDisabledStory">
<Accordion defaultValue="typescript">
<Accordion.Item value="typescript">
<div slot="control">Typescript Based</div>
Build type safe applications. All SvelteUI packages are built with TypeScript and support it by
default. All components and functions export types, are documented, and give developers autocomplete
features!
</Accordion.Item>
<Accordion.Item value="packed" disabled>
<div slot="control">Feature packed</div>
SvelteUI contains more than just components. With Actions, Transitions, and Utilities available
to you, development will be fun and easy!
</Accordion.Item>
<Accordion.Item value="accessible">
<div slot="control">Accessible and usable</div>
All components are accessible according to WAI-ARIA standards. On top of that, no annoying focus
ring. It will appear only when user navigates with keyboard.
</Accordion.Item>
</Accordion>
</Story>

<Story name="Multiple Tabs Open With Default" id="accordionMultipleDefaultStory">
<Accordion multiple={true} defaultValue={['packed', 'accessible']}>
<Accordion.Item value="typescript">
<div slot="control">Typescript Based</div>
Build type safe applications. All SvelteUI packages are built with TypeScript and support it by
default. All components and functions export types, are documented, and give developers autocomplete
features!
</Accordion.Item>
<Accordion.Item value="packed">
<div slot="control">Feature packed</div>
SvelteUI contains more than just components. With Actions, Transitions, and Utilities available
to you, development will be fun and easy!
</Accordion.Item>
<Accordion.Item value="accessible">
<div slot="control">Accessible and usable</div>
All components are accessible according to WAI-ARIA standards. On top of that, no annoying focus
ring. It will appear only when user navigates with keyboard.
</Accordion.Item>
</Accordion>
</Story>

<Story name="Controlled" id="accordionControlledStory">
<Accordion {value} on:change={(e) => (value = e.detail)}>
<Accordion.Item value="typescript">
<div slot="control">Typescript Based</div>
Build type safe applications. All SvelteUI packages are built with TypeScript and support it by
default. All components and functions export types, are documented, and give developers autocomplete
features!
</Accordion.Item>
<Accordion.Item value="packed">
<div slot="control">Feature packed</div>
SvelteUI contains more than just components. With Actions, Transitions, and Utilities available
to you, development will be fun and easy!
</Accordion.Item>
<Accordion.Item value="accessible">
<div slot="control">Accessible and usable</div>
All components are accessible according to WAI-ARIA standards. On top of that, no annoying focus
ring. It will appear only when user navigates with keyboard.
</Accordion.Item>
</Accordion>
</Story>
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { createStyles } from '$lib/styles';
import type { SvelteUINumberSize } from '$lib/styles';
import type { AccordionVariant } from './Accordion';

export interface AccordionStylesParams {
radius: SvelteUINumberSize;
variant: AccordionVariant;
}

export default createStyles((theme, { radius, variant }: AccordionStylesParams) => {
return {
root: {}
};
});
Loading

0 comments on commit 7730ff4

Please sign in to comment.