Skip to content
This repository has been archived by the owner on Jan 3, 2024. It is now read-only.

New accordion component #911

Merged
merged 9 commits into from
Oct 28, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
5 changes: 5 additions & 0 deletions changelog/unreleased/accordion
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
Change: New accordion component implementation

We rewrote the accordion component to remove UIKit styles and align with our own styling. Some accessibility aspects are already implement, for example expanding and collapsing accordion items by pressing space or enter already works. More will come later on.

https://github.com/owncloud/owncloud-design-system/pull/911
83 changes: 83 additions & 0 deletions src/elements/OcAccordion.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
<template>
<ul class="oc-accordion">
<!-- @slot Many oc-accordion-item elements -->
<slot />
</ul>
</template>
<script>
export default {
name: "oc-accordion",
status: "review",
release: "1.0.0",
props: {
/**
* Allow multiple items to be expanded at the same time
*/
multiple: {
type: Boolean,
required: false,
default: false,
},
},
mounted() {
this.$on("toggle", id => this.$_ocAccordion_toggleItem(id))
this.$_ocAccordion_init()
},

methods: {
$_ocAccordion_toggleItem(id) {
const collapseOthers = !this.multiple

this.$children.forEach(child => {
const toggled = child.$_ocAccordionItem_id === id

if (toggled) {
return child.$data.expanded = !child.$data.expanded
}

if (collapseOthers) {
child.$data.expanded = false
}
})
},

$_ocAccordion_init() {
if (!this.multiple) {
let found = false

this.$children.forEach(child => {
if (!found && child.$props.expandedByDefault) {
return found = true
}

if (found) {
child.$data.expanded = false
}
})
}
}
}
}
</script>
<docs>
An accordion is a vertically stacked set of interactive headings that each contain a title, content snippet, or thumbnail representing a section of content.
The headings function as controls that enable users to reveal or hide their associated sections of content.
Accordions are commonly used to reduce the need to scroll when presenting multiple sections of content on a single page.

The accordion component is using `oc-accordion-item` as its children.
To see documentation on how to use this component, see [oc-accordion-item](/#/Elements/oc-accordion-item).

```jsx
<oc-accordion :multiple=false class="uk-width-1-2">
<oc-accordion-item :expandedByDefault=true title="My accordion item" icon="folder">
<p>
I am the content of this accordion
</p>
</oc-accordion-item>
<oc-accordion-item title="Something else with content" description="And a subtitle" icon="file">
<p>Enter some text!</p>
<oc-text-input label="Text"></oc-text-input>
</oc-accordion-item>
</oc-accordion>
```
</docs>
128 changes: 128 additions & 0 deletions src/elements/OcAccordionItem.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
<template>
<li :id="$_ocAccordionItem_id" class="oc-accordion-item">
<component :is="'h' + headingLevel" :id="$_ocAccordionItem_titleId" class="oc-accordion-title">
<oc-button
variation="raw"
justify-content="space-between"
class="uk-text-left uk-width-1-1"
:aria-expanded="expanded"
:aria-controls="$_ocAccordionItem_contentId"
@click="$_ocAccordionItem_toggleExpanded"
@keydown.space="$_ocAccordionItem_toggleExpanded"
@keydown.enter="$_ocAccordionItem_toggleExpanded"
>
<div class="uk-width-1-1">
<oc-grid flex>
<oc-icon v-if="icon" :name="icon" class="oc-mr-s" aria-hidden="true" />
<div class="uk-width-expand" v-text="title" />
<oc-icon name="expand_more" class="oc-ml-xs" :class="{'rotate': expanded}" size="large" />
</oc-grid>
<oc-grid v-if="description">
<div v-if="icon" class="oc-icon-m oc-mr-s" />
<div class="uk-text-meta">{{ description }}</div>
</oc-grid>
</div>
</oc-button>
</component>
<div class="oc-accordion-content" :aria-labelledby="$_ocAccordionItem_titleId" :id="$_ocAccordionItem_contentId" role="region">
<!-- @slot Content of the accordion item -->
<slot v-if="expanded" />
</div>
</li>
</template>
<script>
import * as _uniqueId from "../utils/uniqueId"
export default {
name: "oc-accordion-item",
status: "review",
release: "1.0.0",
props: {
/**
* Icon to be displayed on the left side of the accordion title.
*/
icon: {
type: String,
required: false,
default: null,
},
/**
* Title to be displayed.
*/
title: {
type: String,
required: true,
},
/**
* Description of the accordion item to be displayed below the accordion title.
*/
description: {
type: String,
required: false,
default: null
},
/**
* Asserts whether the accordion item should be expanded by default. If the accordion doesn't allow multiple items
* to be expanded, but multiple items have only the first one to be found will actually be expanded.
*/
expandedByDefault: {
type: Boolean,
required: false,
default: false
},
/**
* Id of the accordion item. If not specified, a unique id will be generated.
*/
id: {
type: String,
required: false,
default: null,
},
/**
* Id of the accordion title. If not specified, a unique id will be generated.
*/
titleId: {
type: String,
required: false,
default: null
},
/**
* Id of the content of the accordion item. If not specified, a unique id will be generated.
*/
contentId: {
type: String,
required: false,
default: null,
},
/**
* Heading level of the accordion title. Defaults to 3 (i.e. `h3`).
*/
headingLevel: {
type: String,
required: false,
default: "3"
}
},
data: () => ({
expanded: false
}),
computed: {
$_ocAccordionItem_id() {
return this.id || _uniqueId("oc-accordion-id-")
},
$_ocAccordionItem_titleId() {
return this.titleId || _uniqueId("oc-accordion-title-")
},
$_ocAccordionItem_contentId() {
return this.contentId || _uniqueId("oc-accordion-content-")
},
},
mounted() {
this.expanded = this.expandedByDefault
},
methods: {
$_ocAccordionItem_toggleExpanded() {
this.$parent.$emit("toggle", this.$_ocAccordionItem_id)
}
}
}
</script>
34 changes: 0 additions & 34 deletions src/elements/_OcAccordionItem.vue

This file was deleted.

57 changes: 0 additions & 57 deletions src/patterns/OcAccordion.vue

This file was deleted.

5 changes: 3 additions & 2 deletions src/styles/_owncloud.scss
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@
@import '../../node_modules/uikit/src/scss/variables-theme';
@import '../../node_modules/uikit/src/scss/mixins-theme';

// // 4. Import UIkit.
// 3. Import UIkit.
@import '../../node_modules/uikit/src/scss/uikit-theme';

// 3. Your custom mixin overwrites.
// 4. Your custom mixin overwrites.
@import 'theme/helper';
@import 'theme/oc-spacing';
@import 'theme/background';
Expand Down Expand Up @@ -51,3 +51,4 @@
@import 'theme/oc-modal';
@import 'theme/oc-sidebar';
@import 'theme/oc-radio';
@import 'theme/oc-accordion';
39 changes: 39 additions & 0 deletions src/styles/theme/oc-accordion.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
.oc-accordion {
list-style: none;
padding: 0;

&-item + &-item {
margin-top: $space-medium;
}

&-title {
@extend .oc-mb-rm;

display: block;
font-size: $medium-font-size;

> .oc-button,
> .oc-button:hover,
> .oc-button:focus {
color: $color;
outline: none;
text-decoration: none;

.oc-icon {
transition-duration: 0.3s;

&.rotate {
transform: rotate(180deg);
}

> svg {
fill: $color;
}
}
}
}

&-content {
margin-top: $space-small;
}
}
2 changes: 1 addition & 1 deletion src/tokens/font-size.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ props:
small-font-size:
value: "0.875rem"
font-size:
value: "15px"
value: "1rem"
medium-font-size:
value: "1.25rem"
large-font-size:
Expand Down