Skip to content

Commit

Permalink
Add OcFilterChip and ItemFilter components (#8355)
Browse files Browse the repository at this point in the history
* Add OcFilterChip and ItemFilter components

* Improve variable naming

* Fix text truncation

* Focus input, add filterableAttributes prop, emit selectionChange event

* Add route query handling

* Add unit tests

* Adjust margins to match other drop menus

* Add changelog item

* Fix filter chip design

* OcFilterChip: rename filterName to filterLabel, remove snapshot tests

* Split changelot items, add mark.js to ItemFilter, use ID as route params

* Remove demo from users list
  • Loading branch information
JammingBen authored Feb 3, 2023
1 parent 85f4ac2 commit fa38fa0
Show file tree
Hide file tree
Showing 11 changed files with 647 additions and 11 deletions.
6 changes: 6 additions & 0 deletions changelog/unreleased/enhancement-add-item-filter-component
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
Enhancement: Add `ItemFilter` component

The `ItemFilter` component has been introduced to filter data. It fills the dropdown of `OcFilterChip` with content and can be fed with items, which then can be selected and filtered. The component also supports multi-selection and persists the current selection as route query.

https://github.com/owncloud/web/issues/8354
https://github.com/owncloud/web/pull/8355
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
Enhancement: Add `OcFilterChip` component

The `OcFilterChip` component has been added. It provides an (in-)active state and a button to clear the current filter.

https://github.com/owncloud/web/issues/8354
https://github.com/owncloud/web/pull/8355
7 changes: 6 additions & 1 deletion packages/design-system/src/components/OcDrop/OcDrop.vue
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ export default defineComponent({
default: undefined
}
},
emits: ['hideDrop', 'showDrop'],
data() {
return { tippy: null }
},
Expand Down Expand Up @@ -173,8 +174,12 @@ export default defineComponent({
content: 'describedby'
},
...(!this.isNested && {
onShow(instance) {
onShow: (instance) => {
this.$emit('showDrop')
hideAll({ exclude: instance })
},
onHide: () => {
this.$emit('hideDrop')
}
}),
popperOptions: this.popperOptions,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { defaultPlugins, mount } from 'web-test-helpers'
import OcFilterChip from './OcFilterChip.vue'

const selectors = {
filterChipLabel: '.oc-filter-chip-label',
clearBtn: '.oc-filter-chip-clear'
}

describe('OcFilterChip', () => {
it('renders the filterLabel when no selected item has been given', () => {
const filterLabel = 'Users'
const { wrapper } = getWrapper({ props: { filterLabel } })
expect(wrapper.find(selectors.filterChipLabel).text()).toEqual(filterLabel)
})
it('renders the first selected item name when selected items have been given', () => {
const selectedItemNames = ['Einstein', 'Marie']
const { wrapper } = getWrapper({ props: { selectedItemNames } })
expect(wrapper.find(selectors.filterChipLabel).text()).toEqual(selectedItemNames[0])
})
it('emits the "clearFilter"-event when clicking the clear-button', async () => {
const { wrapper } = getWrapper({ props: { selectedItemNames: ['Einstein', 'Marie'] } })
await wrapper.find(selectors.clearBtn).trigger('click')
expect(wrapper.emitted('clearFilter')).toBeTruthy()
})
})

const getWrapper = ({ props = {} } = {}) => {
return {
wrapper: mount(OcFilterChip, {
props: { filterLabel: 'Users', selectedItemNames: [], ...props },
global: { plugins: [...defaultPlugins()] }
})
}
}
112 changes: 112 additions & 0 deletions packages/design-system/src/components/OcFilterChip/OcFilterChip.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
<template>
<div class="oc-filter-chip oc-flex">
<oc-button
:id="id"
class="oc-filter-chip-button"
:class="{ 'oc-filter-chip-button-selected': filterActive }"
appearance="raw"
>
<oc-icon v-if="filterActive" name="check" size="small" color="var(--oc-color-text-inverse)" />
<span
class="oc-text-truncate oc-filter-chip-label"
v-text="!!selectedItemNames.length ? selectedItemNames[0] : filterLabel"
/>
<span v-if="selectedItemNames.length > 1" v-text="` +${selectedItemNames.length - 1}`" />
<oc-icon v-if="!filterActive" name="arrow-down-s" size="small" />
</oc-button>
<oc-drop
:toggle="'#' + id"
class="oc-filter-chip-drop"
mode="click"
padding-size="small"
@hide-drop="$emit('hideDrop')"
@show-drop="$emit('showDrop')"
>
<slot />
</oc-drop>
<oc-button
v-if="filterActive"
v-oc-tooltip="$gettext('Clear filter')"
class="oc-filter-chip-clear oc-px-xs"
appearance="raw"
@click="$emit('clearFilter')"
>
<oc-icon name="close" size="small" color="var(--oc-color-text-inverse)" />
</oc-button>
</div>
</template>

<script lang="ts">
import { computed, defineComponent } from 'vue'
import uniqueId from '../../utils/uniqueId'
export default defineComponent({
name: 'OcFilterChip',
status: 'ready',
release: '15.0.0',
props: {
id: {
type: String,
required: false,
default: () => uniqueId('oc-filter-chip-')
},
filterLabel: {
type: String,
required: true
},
selectedItemNames: {
type: Array,
required: false,
default: () => []
}
},
emits: ['clearFilter', 'hideDrop', 'showDrop'],
setup(props) {
const filterActive = computed(() => {
return !!props.selectedItemNames.length
})
return { filterActive }
}
})
</script>

<style lang="scss">
.oc-filter-chip {
&-button {
align-items: center;
background-color: var(--oc-color-background-default) !important;
color: var(--oc-color-text-muted) !important;
border: 1px solid var(--oc-color-text-muted);
border-radius: 99px !important;
box-sizing: border-box;
display: inline-flex;
gap: var(--oc-space-xsmall);
text-decoration: none;
font-size: var(--oc-font-size-xsmall);
line-height: 1rem;
max-width: 120px;
padding: var(--oc-space-xsmall) var(--oc-space-small) !important;
&-selected,
&-selected:hover {
background-color: var(--oc-color-swatch-primary-default) !important;
color: var(--oc-color-text-inverse) !important;
border-top-left-radius: 99px !important;
border-bottom-left-radius: 99px !important;
border-top-right-radius: 0px !important;
border-bottom-right-radius: 0px !important;
}
}
&-clear,
&-clear:hover {
margin-left: 1px;
background-color: var(--oc-color-swatch-primary-default) !important;
color: var(--oc-color-text-inverse) !important;
border-top-left-radius: 0px !important;
border-bottom-left-radius: 0px !important;
border-top-right-radius: 99px !important;
border-bottom-right-radius: 99px !important;
}
}
</style>

1 change: 1 addition & 0 deletions packages/design-system/src/components/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ export { default as OcContextualHelper } from './OcContextualHelper/OcContextual
export { default as OcDatepicker } from './OcDatepicker/OcDatepicker.vue'
export { default as OcDrop } from './OcDrop/OcDrop.vue'
export { default as OcDropzone } from './OcDropzone/OcDropzone.vue'
export { default as OcFilterChip } from './OcFilterChip/OcFilterChip.vue'
export { default as OcGhostElement } from './_OcGhostElement/_OcGhostElement.vue'
export { default as OcGrid } from './OcGrid/OcGrid.vue'
export { default as OcHiddenAnnouncer } from './OcHiddenAnnouncer/OcHiddenAnnouncer.vue'
Expand Down
2 changes: 2 additions & 0 deletions packages/web-pkg/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,10 @@
"peerDependencies": {
"axios": "^0.27.2",
"filesize": "^9.0.11",
"fuse.js": "^6.5.3",
"lodash-es": "^4.17.21",
"luxon": "^2.4.0",
"mark.js": "^8.11.1",
"popper-max-size-modifier": "^0.2.0",
"qs": "^6.10.3",
"uuid": "^9.0.0",
Expand Down
Loading

0 comments on commit fa38fa0

Please sign in to comment.