Skip to content

Commit 50af080

Browse files
Merge pull request #46448 from nextcloud/bugfix/noid/fix-missing-dashboard-widget-icon
fix(dashboard): Unify widget icon colors and document it's behaviour
2 parents a2ded20 + 37d3312 commit 50af080

File tree

10 files changed

+54
-22
lines changed

10 files changed

+54
-22
lines changed

apps/dashboard/lib/Controller/DashboardController.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
use OCP\AppFramework\Http\Attribute\OpenAPI;
1616
use OCP\AppFramework\Http\TemplateResponse;
1717
use OCP\AppFramework\Services\IInitialState;
18+
use OCP\Dashboard\IIconWidget;
1819
use OCP\Dashboard\IManager;
1920
use OCP\Dashboard\IWidget;
2021
use OCP\EventDispatcher\IEventDispatcher;
@@ -54,6 +55,7 @@ public function index(): TemplateResponse {
5455
'id' => $widget->getId(),
5556
'title' => $widget->getTitle(),
5657
'iconClass' => $widget->getIconClass(),
58+
'iconUrl' => $widget instanceof IIconWidget ? $widget->getIconUrl() : '',
5759
'url' => $widget->getUrl()
5860
];
5961
}, $this->dashboardManager->getWidgets());

apps/dashboard/src/DashboardApp.vue

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,12 @@
2424
class="panel">
2525
<div class="panel--header">
2626
<h2>
27-
<span :aria-labelledby="`panel-${panels[panelId].id}--header--icon--description`"
27+
<img v-if="apiWidgets[panels[panelId].id].icon_url"
28+
:alt="apiWidgets[panels[panelId].id].title + ' icon'"
29+
:src="apiWidgets[panels[panelId].id].icon_url"
30+
aria-hidden="true">
31+
<span v-else
32+
:aria-labelledby="`panel-${panels[panelId].id}--header--icon--description`"
2833
aria-hidden="true"
2934
:class="apiWidgets[panels[panelId].id].icon_class"
3035
role="img" />
@@ -97,7 +102,11 @@
97102
:checked="isActive(panel)"
98103
@input="updateCheckbox(panel, $event.target.checked)">
99104
<label :for="'panel-checkbox-' + panel.id" :class="{ draggable: isActive(panel) }">
100-
<span :class="panel.iconClass" aria-hidden="true" />
105+
<img v-if="panel.iconUrl"
106+
:alt="panel.title + ' icon'"
107+
:src="panel.iconUrl"
108+
aria-hidden="true">
109+
<span v-else :class="panel.iconClass" aria-hidden="true" />
101110
{{ panel.title }}
102111
</label>
103112
</li>
@@ -554,6 +563,8 @@ export default {
554563
overflow: hidden;
555564
text-overflow: ellipsis;
556565
cursor: grab;
566+
567+
img,
557568
span {
558569
background-size: 32px;
559570
width: 32px;
@@ -564,6 +575,10 @@ export default {
564575
margin-top: -6px;
565576
margin-left: 6px;
566577
}
578+
579+
img {
580+
filter: var(--background-invert-if-dark);
581+
}
567582
}
568583
}
569584
@@ -651,6 +666,7 @@ export default {
651666
text-overflow: ellipsis;
652667
white-space: nowrap;
653668
669+
img,
654670
span {
655671
position: absolute;
656672
top: 16px;
@@ -659,6 +675,10 @@ export default {
659675
background-size: 24px;
660676
}
661677
678+
img {
679+
filter: var(--background-invert-if-dark);
680+
}
681+
662682
&:hover {
663683
border-color: var(--color-primary-element);
664684
}
Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,4 @@
11
/*!
22
* SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors
33
* SPDX-License-Identifier: AGPL-3.0-or-later
4-
*//*!
5-
* SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors
6-
* SPDX-License-Identifier: AGPL-3.0-or-later
7-
*//*!
8-
* SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors
9-
* SPDX-License-Identifier: AGPL-3.0-or-later
10-
*/.icon-user-status{background-image:url("../img/app.svg")}.icon-user-status-dark{background-image:url("../img/app-dark.svg")}/*# sourceMappingURL=user-status-menu.css.map */
4+
*/.icon-user-status{background-image:url("../img/app.svg")}.icon-user-status-dark{background-image:url("../img/app-dark.svg");filter:var(--background-invert-if-dark)}/*# sourceMappingURL=user-status-menu.css.map */

apps/user_status/css/user-status-menu.css.map

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

apps/user_status/css/user-status-menu.scss

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,11 @@
22
* SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors
33
* SPDX-License-Identifier: AGPL-3.0-or-later
44
*/
5-
@use 'variables';
6-
@import 'functions';
7-
85
.icon-user-status {
96
background-image: url("../img/app.svg");
107
}
118

129
.icon-user-status-dark {
1310
background-image: url("../img/app-dark.svg");
14-
11+
filter: var(--background-invert-if-dark);
1512
}

dist/dashboard-main.js

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dist/dashboard-main.js.map

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

lib/private/Dashboard/Manager.php

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,18 +25,23 @@ class Manager implements IManager {
2525
/** @var array<string, IWidget> */
2626
private array $widgets = [];
2727

28-
private ContainerInterface $serverContainer;
2928
private ?IAppManager $appManager = null;
3029

31-
public function __construct(ContainerInterface $serverContainer) {
32-
$this->serverContainer = $serverContainer;
30+
public function __construct(
31+
private ContainerInterface $serverContainer,
32+
private LoggerInterface $logger,
33+
) {
3334
}
3435

3536
private function registerWidget(IWidget $widget): void {
3637
if (array_key_exists($widget->getId(), $this->widgets)) {
3738
throw new InvalidArgumentException('Dashboard widget with this id has already been registered');
3839
}
3940

41+
if (!preg_match('/^[a-z][a-z0-9\-_]*$/', $widget->getId())) {
42+
$this->logger->debug('Deprecated dashboard widget ID provided: "' . $widget->getId() . '" [ ' . get_class($widget) . ' ]. Please use a-z, 0-9, - and _ only, starting with a-z');
43+
}
44+
4045
$this->widgets[$widget->getId()] = $widget;
4146
}
4247

lib/public/Dashboard/IIconWidget.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,9 @@
1414
*/
1515
interface IIconWidget extends IWidget {
1616
/**
17-
* Get the absolute url for the widget icon
17+
* Get the absolute url for the widget icon (should be colored black or not have a color)
18+
*
19+
* The icon will be inverted automatically in mobile clients and when using dark mode
1820
*
1921
* @return string
2022
* @since 25.0.0

lib/public/Dashboard/IWidget.php

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,12 @@
1515
*/
1616
interface IWidget {
1717
/**
18-
* @return string Unique id that identifies the widget, e.g. the app id
18+
* Get a unique identifier for the widget
19+
*
20+
* To ensure uniqueness, it is recommended to user the app id or start with the
21+
* app id followed by a dash.
22+
*
23+
* @return string Unique id that identifies the widget, e.g. the app id. Only use alphanumeric characters, dash and underscore
1924
* @since 20.0.0
2025
*/
2126
public function getId(): string;
@@ -33,6 +38,13 @@ public function getTitle(): string;
3338
public function getOrder(): int;
3439

3540
/**
41+
* CSS class that shows the widget icon (should be colored black or not have a color)
42+
*
43+
* The icon will be inverted automatically in mobile clients and when using dark mode.
44+
* Therefore, it is NOT recommended to use a css class that sets the background with:
45+
* `var(--icon-…)` as those will adapt to dark/bright mode in the web and still be inverted
46+
* resulting in a dark icon on dark background.
47+
*
3648
* @return string css class that displays an icon next to the widget title
3749
* @since 20.0.0
3850
*/

0 commit comments

Comments
 (0)