Skip to content

Commit 9db8f9c

Browse files
authored
Merge pull request #3820 from nextcloud/bugfix/3819
Improve filter popover accessibility
2 parents 7328a27 + 41ed0ce commit 9db8f9c

1 file changed

Lines changed: 107 additions & 102 deletions

File tree

src/components/Controls.vue

Lines changed: 107 additions & 102 deletions
Original file line numberDiff line numberDiff line change
@@ -70,107 +70,113 @@
7070
</form>
7171
</div>
7272
<div v-if="board" class="board-action-buttons">
73-
<Popover @show="filterVisible=true" @hide="filterVisible=false">
74-
<Actions slot="trigger" :title="t('deck', 'Apply filter')">
75-
<ActionButton v-if="isFilterActive" icon="icon-filter_set" />
76-
<ActionButton v-else icon="icon-filter" />
77-
</Actions>
73+
<div class="board-action-buttons__filter">
74+
<Popover container=".board-action-buttons__filter"
75+
:placement="'bottom-end'"
76+
:aria-label="t('deck', 'Active filters')"
77+
@show="filterVisible=true"
78+
@hide="filterVisible=false">
79+
<Actions slot="trigger" :title="t('deck', 'Apply filter')">
80+
<ActionButton v-if="isFilterActive" icon="icon-filter_set" />
81+
<ActionButton v-else icon="icon-filter" />
82+
</Actions>
7883

79-
<div v-if="filterVisible" class="filter">
80-
<h3>{{ t('deck', 'Filter by tag') }}</h3>
81-
<div v-for="label in labelsSorted" :key="label.id" class="filter--item">
82-
<input :id="label.id"
83-
v-model="filter.tags"
84-
type="checkbox"
85-
class="checkbox"
86-
:value="label.id"
87-
@change="setFilter">
88-
<label :for="label.id"><span class="label" :style="labelStyle(label)">{{ label.title }}</span></label>
89-
</div>
84+
<div v-if="filterVisible" class="filter">
85+
<h3>{{ t('deck', 'Filter by tag') }}</h3>
86+
<div v-for="label in labelsSorted" :key="label.id" class="filter--item">
87+
<input :id="label.id"
88+
v-model="filter.tags"
89+
type="checkbox"
90+
class="checkbox"
91+
:value="label.id"
92+
@change="setFilter">
93+
<label :for="label.id"><span class="label" :style="labelStyle(label)">{{ label.title }}</span></label>
94+
</div>
9095

91-
<h3>{{ t('deck', 'Filter by assigned user') }}</h3>
92-
<div class="filter--item">
93-
<input id="unassigned"
94-
v-model="filter.unassigned"
95-
type="checkbox"
96-
class="checkbox"
97-
value="unassigned"
98-
@change="setFilter"
99-
@click="beforeSetFilter">
100-
<label for="unassigned">{{ t('deck', 'Unassigned') }}</label>
101-
</div>
102-
<div v-for="user in board.users" :key="user.uid" class="filter--item">
103-
<input :id="user.uid"
104-
v-model="filter.users"
105-
type="checkbox"
106-
class="checkbox"
107-
:value="user.uid"
108-
@change="setFilter">
109-
<label :for="user.uid"><Avatar :user="user.uid" :size="24" :disable-menu="true" /> {{ user.displayname }}</label>
110-
</div>
96+
<h3>{{ t('deck', 'Filter by assigned user') }}</h3>
97+
<div class="filter--item">
98+
<input id="unassigned"
99+
v-model="filter.unassigned"
100+
type="checkbox"
101+
class="checkbox"
102+
value="unassigned"
103+
@change="setFilter"
104+
@click="beforeSetFilter">
105+
<label for="unassigned">{{ t('deck', 'Unassigned') }}</label>
106+
</div>
107+
<div v-for="user in board.users" :key="user.uid" class="filter--item">
108+
<input :id="user.uid"
109+
v-model="filter.users"
110+
type="checkbox"
111+
class="checkbox"
112+
:value="user.uid"
113+
@change="setFilter">
114+
<label :for="user.uid"><Avatar :user="user.uid" :size="24" :disable-menu="true" /> {{ user.displayname }}</label>
115+
</div>
111116

112-
<h3>{{ t('deck', 'Filter by due date') }}</h3>
117+
<h3>{{ t('deck', 'Filter by due date') }}</h3>
113118

114-
<div class="filter--item">
115-
<input id="overdue"
116-
v-model="filter.due"
117-
type="radio"
118-
class="radio"
119-
value="overdue"
120-
@change="setFilter"
121-
@click="beforeSetFilter">
122-
<label for="overdue">{{ t('deck', 'Overdue') }}</label>
123-
</div>
119+
<div class="filter--item">
120+
<input id="overdue"
121+
v-model="filter.due"
122+
type="radio"
123+
class="radio"
124+
value="overdue"
125+
@change="setFilter"
126+
@click="beforeSetFilter">
127+
<label for="overdue">{{ t('deck', 'Overdue') }}</label>
128+
</div>
124129

125-
<div class="filter--item">
126-
<input id="dueToday"
127-
v-model="filter.due"
128-
type="radio"
129-
class="radio"
130-
value="dueToday"
131-
@change="setFilter"
132-
@click="beforeSetFilter">
133-
<label for="dueToday">{{ t('deck', 'Next 24 hours') }}</label>
134-
</div>
130+
<div class="filter--item">
131+
<input id="dueToday"
132+
v-model="filter.due"
133+
type="radio"
134+
class="radio"
135+
value="dueToday"
136+
@change="setFilter"
137+
@click="beforeSetFilter">
138+
<label for="dueToday">{{ t('deck', 'Next 24 hours') }}</label>
139+
</div>
135140

136-
<div class="filter--item">
137-
<input id="dueWeek"
138-
v-model="filter.due"
139-
type="radio"
140-
class="radio"
141-
value="dueWeek"
142-
@change="setFilter"
143-
@click="beforeSetFilter">
144-
<label for="dueWeek">{{ t('deck', 'Next 7 days') }}</label>
145-
</div>
141+
<div class="filter--item">
142+
<input id="dueWeek"
143+
v-model="filter.due"
144+
type="radio"
145+
class="radio"
146+
value="dueWeek"
147+
@change="setFilter"
148+
@click="beforeSetFilter">
149+
<label for="dueWeek">{{ t('deck', 'Next 7 days') }}</label>
150+
</div>
146151

147-
<div class="filter--item">
148-
<input id="dueMonth"
149-
v-model="filter.due"
150-
type="radio"
151-
class="radio"
152-
value="dueMonth"
153-
@change="setFilter"
154-
@click="beforeSetFilter">
155-
<label for="dueMonth">{{ t('deck', 'Next 30 days') }}</label>
156-
</div>
152+
<div class="filter--item">
153+
<input id="dueMonth"
154+
v-model="filter.due"
155+
type="radio"
156+
class="radio"
157+
value="dueMonth"
158+
@change="setFilter"
159+
@click="beforeSetFilter">
160+
<label for="dueMonth">{{ t('deck', 'Next 30 days') }}</label>
161+
</div>
157162

158-
<div class="filter--item">
159-
<input id="noDue"
160-
v-model="filter.due"
161-
type="radio"
162-
class="radio"
163-
value="noDue"
164-
@change="setFilter"
165-
@click="beforeSetFilter">
166-
<label for="noDue">{{ t('deck', 'No due date') }}</label>
167-
</div>
163+
<div class="filter--item">
164+
<input id="noDue"
165+
v-model="filter.due"
166+
type="radio"
167+
class="radio"
168+
value="noDue"
169+
@change="setFilter"
170+
@click="beforeSetFilter">
171+
<label for="noDue">{{ t('deck', 'No due date') }}</label>
172+
</div>
168173

169-
<Button :disabled="!isFilterActive" @click="clearFilter">
170-
{{ t('deck', 'Clear filter') }}
171-
</Button>
172-
</div>
173-
</Popover>
174+
<Button :disabled="!isFilterActive" :wide="true" @click="clearFilter">
175+
{{ t('deck', 'Clear filter') }}
176+
</Button>
177+
</div>
178+
</Popover>
179+
</div>
174180

175181
<Actions>
176182
<ActionButton icon="icon-archive"
@@ -199,14 +205,14 @@
199205

200206
<script>
201207
import { mapState, mapGetters } from 'vuex'
202-
import { Actions, ActionButton, Popover, Avatar } from '@nextcloud/vue'
208+
import { Actions, ActionButton, Avatar, Button, Popover } from '@nextcloud/vue'
203209
import labelStyle from '../mixins/labelStyle'
204210
import CardCreateDialog from '../CardCreateDialog'
205211
206212
export default {
207213
name: 'Controls',
208214
components: {
209-
Actions, ActionButton, Popover, Avatar, CardCreateDialog,
215+
Actions, ActionButton, Avatar, Button, Popover, CardCreateDialog,
210216
},
211217
mixins: [labelStyle],
212218
props: {
@@ -399,12 +405,6 @@ export default {
399405
400406
.board-action-buttons {
401407
display: flex;
402-
button {
403-
border: 0;
404-
width: 44px;
405-
margin: 0 0 0 -1px;
406-
background-color: transparent;
407-
}
408408
}
409409
410410
.deck-search {
@@ -432,8 +432,9 @@ export default {
432432
}
433433
434434
.filter {
435-
width: 250px;
436-
max-height: 80vh;
435+
width: 240px;
436+
max-height: calc(100vh - 150px);
437+
position: relative;
437438
overflow: auto;
438439
padding: 8px;
439440
}
@@ -444,6 +445,10 @@ export default {
444445
}
445446
</style>
446447
<style lang="scss">
448+
.popover:focus {
449+
outline: 2px solid var(--color-main-text);
450+
}
451+
447452
.tooltip-inner.popover-inner {
448453
text-align: left;
449454
}

0 commit comments

Comments
 (0)