diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile
index 9f20426f..44bc1759 100644
--- a/.devcontainer/Dockerfile
+++ b/.devcontainer/Dockerfile
@@ -3,4 +3,10 @@ FROM mcr.microsoft.com/vscode/devcontainers/javascript-node:${VARIANT}
RUN npm install -g pnpm
+RUN apt-get update \
+ && apt-get -y install --no-install-recommends \
+ python3-pip \
+ && apt-get clean -y \
+ && rm -rf /var/lib/apt/lists/*
+
ENV PATH="${PATH}:./node_modules/.bin"
diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json
index 15d1b9f3..06e7f6ee 100644
--- a/.devcontainer/devcontainer.json
+++ b/.devcontainer/devcontainer.json
@@ -1,27 +1,26 @@
{
- "name": "homepage",
- "build": {
- "dockerfile": "Dockerfile",
- "args": {
- "VARIANT": "18-bullseye"
- }
- },
- "customizations": {
- "vscode": {
- "extensions": [
- "dbaeumer.vscode-eslint",
- "mhutchie.git-graph",
- "streetsidesoftware.code-spell-checker",
- ],
- "settings": {
- "eslint.format.enable": true,
- "eslint.lintTask.enable": true,
- "eslint.packageManager": "pnpm"
- }
- }
- },
- "postCreateCommand": ".devcontainer/setup.sh",
- "forwardPorts": [
- 3000
- ]
+ "name": "homepage",
+ "build": {
+ "dockerfile": "Dockerfile",
+ "args": {
+ "VARIANT": "18-bullseye",
+ },
+ },
+ "customizations": {
+ "vscode": {
+ "extensions": [
+ "dbaeumer.vscode-eslint",
+ "mhutchie.git-graph",
+ "streetsidesoftware.code-spell-checker",
+ "esbenp.prettier-vscode",
+ ],
+ "settings": {
+ "eslint.format.enable": true,
+ "eslint.lintTask.enable": true,
+ "eslint.packageManager": "pnpm",
+ },
+ },
+ },
+ "postCreateCommand": ".devcontainer/setup.sh",
+ "forwardPorts": [3000],
}
diff --git a/.devcontainer/setup.sh b/.devcontainer/setup.sh
index 70bf96cf..ea5d2fe9 100755
--- a/.devcontainer/setup.sh
+++ b/.devcontainer/setup.sh
@@ -3,6 +3,8 @@
# Install Node packages
pnpm install
+python3 -m pip install -r requirements.txt
+
# Copy in skeleton configuration if there is no existing configuration
if [ ! -d "config/" ]; then
echo "Adding skeleton config"
diff --git a/.github/dependabot.yml b/.github/dependabot.yml
index ba1b1fb0..e68f9e4f 100644
--- a/.github/dependabot.yml
+++ b/.github/dependabot.yml
@@ -13,3 +13,7 @@ updates:
directory: "/"
schedule:
interval: "monthly"
+ - package-ecosystem: "pip"
+ directory: "/"
+ schedule:
+ interval: "monthly"
diff --git a/.github/workflows/repo-maintenance.yml b/.github/workflows/repo-maintenance.yml
index d590cf91..d1f7e4fd 100644
--- a/.github/workflows/repo-maintenance.yml
+++ b/.github/workflows/repo-maintenance.yml
@@ -27,7 +27,7 @@ jobs:
stale-issue-message: >
This issue has been automatically marked as stale because it has not had
recent activity. It will be closed if no further activity occurs. Thank you
- for your contributions.
+ for your contributions. See our [contributing guidelines](https://github.com/gethomepage/homepage/blob/main/CONTRIBUTING.md#automatic-respoistory-maintenance) for more details.
lock-threads:
name: 'Lock Old Threads'
runs-on: ubuntu-latest
@@ -42,14 +42,17 @@ jobs:
This issue has been automatically locked since there
has not been any recent activity after it was closed.
Please open a new discussion for related concerns.
+ See our [contributing guidelines](https://github.com/gethomepage/homepage/blob/main/CONTRIBUTING.md#automatic-respoistory-maintenance) for more details.
pr-comment: >
This pull request has been automatically locked since there
has not been any recent activity after it was closed.
Please open a new discussion for related concerns.
+ See our [contributing guidelines](https://github.com/gethomepage/homepage/blob/main/CONTRIBUTING.md#automatic-respoistory-maintenance) for more details.
discussion-comment: >
This discussion has been automatically locked since there
has not been any recent activity after it was closed.
Please open a new discussion for related concerns.
+ See our [contributing guidelines](https://github.com/gethomepage/homepage/blob/main/CONTRIBUTING.md#automatic-respoistory-maintenance) for more details.
close-answered-discussions:
name: 'Close Answered Discussions'
runs-on: ubuntu-latest
@@ -89,7 +92,7 @@ jobs:
}`;
const commentVariables = {
discussion: discussion.id,
- body: 'This discussion has been automatically closed because it was marked as answered.',
+ body: 'This discussion has been automatically closed because it was marked as answered. See our [contributing guidelines](https://github.com/gethomepage/homepage/blob/main/CONTRIBUTING.md#automatic-respoistory-maintenance) for more details.',
}
await github.graphql(addCommentMutation, commentVariables)
@@ -179,7 +182,85 @@ jobs:
}`;
const commentVariables = {
discussion: discussion.id,
- body: 'This discussion has been automatically closed due to inactivity.',
+ body: 'This discussion has been automatically closed due to inactivity. See our [contributing guidelines](https://github.com/gethomepage/homepage/blob/main/CONTRIBUTING.md#automatic-respoistory-maintenance) for more details.',
+ }
+ await github.graphql(addCommentMutation, commentVariables);
+
+ const closeDiscussionMutation = `mutation($discussion:ID!, $reason:DiscussionCloseReason!) {
+ closeDiscussion(input:{discussionId:$discussion, reason:$reason}) {
+ clientMutationId
+ }
+ }`;
+ const closeVariables = {
+ discussion: discussion.id,
+ reason: "OUTDATED",
+ }
+ await github.graphql(closeDiscussionMutation, closeVariables);
+
+ await sleep(1000);
+ }
+ }
+ close-unsupported-feature-requests:
+ name: 'Close Unsupported Feature Requests'
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/github-script@v7
+ with:
+ script: |
+ function sleep(ms) {
+ return new Promise(resolve => setTimeout(resolve, ms));
+ }
+
+ const CUTOFF_1_DAYS = 180;
+ const CUTOFF_1_COUNT = 5;
+ const CUTOFF_2_DAYS = 365;
+ const CUTOFF_2_COUNT = 10;
+
+ const cutoff1Date = new Date();
+ cutoff1Date.setDate(cutoff1Date.getDate() - CUTOFF_1_DAYS);
+ const cutoff2Date = new Date();
+ cutoff2Date.setDate(cutoff2Date.getDate() - CUTOFF_2_DAYS);
+
+ const query = `query(
+ $owner:String!,
+ $name:String!,
+ $featureRequestsCategory:ID!,
+ ) {
+ repository(owner:$owner, name:$name){
+ discussions(
+ categoryId:$featureRequestsCategory,
+ last:100,
+ states:[OPEN],
+ ) {
+ nodes {
+ id,
+ number,
+ updatedAt,
+ upvoteCount,
+ }
+ },
+ }
+ }`;
+ const variables = {
+ owner: context.repo.owner,
+ name: context.repo.repo,
+ featureRequestsCategory: "DIC_kwDOH31rQM4CRErS"
+ }
+ const result = await github.graphql(query, variables);
+
+ for (const discussion of result.repository.discussions.nodes) {
+ const discussionDate = new Date(discussion.updatedAt);
+ if ((discussionDate < cutoff1Date && discussion.upvoteCount < CUTOFF_1_COUNT) ||
+ (discussionDate < cutoff2Date && discussion.upvoteCount < CUTOFF_2_COUNT)) {
+ console.log(`Closing discussion #${discussion.number} (${discussion.id}), last updated at ${discussion.updatedAt} with votes ${discussion.upvoteCount}`);
+ const addCommentMutation = `mutation($discussion:ID!, $body:String!) {
+ addDiscussionComment(input:{discussionId:$discussion, body:$body}) {
+ clientMutationId
+ }
+ }`;
+ const commentVariables = {
+ discussion: discussion.id,
+ body: 'This discussion has been automatically closed due to lack of community support. See our [contributing guidelines](https://github.com/gethomepage/homepage/blob/main/CONTRIBUTING.md#automatic-respoistory-maintenance) for more details.',
}
await github.graphql(addCommentMutation, commentVariables);
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 51ca1f83..f2361c43 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -51,3 +51,18 @@ By contributing, you agree that your contributions will be licensed under its GN
## References
This document was adapted from the open-source contribution guidelines for [Facebook's Draft](https://github.com/facebook/draft-js/blob/main/CONTRIBUTING.md)
+
+# Automatic Respoistory Maintenance
+
+The homepage team appreciates all effort and interest from the community in filing bug reports, creating feature requests, sharing ideas and helping other community members. That said, in an effort to keep the repository organized and managebale the project uses automatic handling of certain areas:
+
+- Issues that cannot be reproduced will be marked 'stale' after 7 days of inactivity and closed after 14 further days of inactivity.
+- Issues, pull requests and discussions that are closed will be locked after 30 days of inactivity.
+- Discussions with a marked answer will be automatically closed.
+- Discussions in the 'General' or 'Support' categories will be closed after 180 days of inactivity.
+- Feature requests that do not meet the following thresholds will be closed: 5 "up-votes" after 180 days of inactivity or 10 "up-votes" after 365 days.
+
+In all cases, threads can be re-opened by project maintainers and, of course, users can always create a new discussion for related concerns.
+Finally, remember that all information remains searchable and 'closed' feature requests can still serve as inspiration for new features.
+
+Thank you all for your contributions.
diff --git a/docs/configs/services.md b/docs/configs/services.md
index 010950eb..490356fc 100644
--- a/docs/configs/services.md
+++ b/docs/configs/services.md
@@ -101,7 +101,7 @@ To use a local icon, first create a Docker mount to `/app/public/icons` and then
## Ping
-Services may have an optional `ping` property that allows you to monitor the availability of an external host. As of v0.8.0, the ping feature attempts to use a true (ICMP) ping command on the underlying host.
+Services may have an optional `ping` property that allows you to monitor the availability of an external host. As of v0.8.0, the ping feature attempts to use a true (ICMP) ping command on the underlying host. Currently, only IPv4 is supported.
```yaml
- Group A:
diff --git a/docs/configs/settings.md b/docs/configs/settings.md
index d3e9a837..9ee86a85 100644
--- a/docs/configs/settings.md
+++ b/docs/configs/settings.md
@@ -229,6 +229,26 @@ disableCollapse: true
By default the feature is enabled.
+### Initially collapsed sections
+
+You can initially collapse sections by adding the `initiallyCollapsed` option to the layout group.
+
+```yaml
+layout:
+ Section A:
+ initiallyCollapsed: true
+```
+
+This can also be set globaly using the `groupsInitiallyCollapsed` option.
+
+```yaml
+groupsInitiallyCollapsed: true
+```
+
+The value set on a group will overwrite the global setting.
+
+By default the feature is disabled.
+
### Use Equal Height Cards
You can enable equal height cards for groups of services, this will make all cards in a row the same height.
diff --git a/docs/more/development.md b/docs/more/development.md
index b1252112..8e3fac13 100644
--- a/docs/more/development.md
+++ b/docs/more/development.md
@@ -51,6 +51,7 @@ To ensure cohesiveness of various widgets, the following should be used as a gui
- Please only submit widgets that have been requested and have at least 10 'up-votes'. The purpose of this requirement is to avoid the addition (and maintenance) of service widgets that might only benefit a small number of users.
- Widgets should be only one row of blocks
-- Widgets should be no more than 4 blocks wide
+- Widgets should be no more than 4 blocks wide and generally conform to the styling / design choices of other widgets
- Minimize the number of API calls
- Avoid the use of custom proxy unless absolutely necessary
+- Widgets should be 'read-only', as in they should not make write changes using the relevant tool's API. Homepage widgets are designed to surface information, not to be a (usually worse) replacement for the tool itself.
diff --git a/docs/widgets/info/glances.md b/docs/widgets/info/glances.md
index e6fc2a61..b7fd7efd 100644
--- a/docs/widgets/info/glances.md
+++ b/docs/widgets/info/glances.md
@@ -17,6 +17,7 @@ The Glances widget allows you to monitor the resources (CPU, memory, storage, te
cputemp: true # disabled by default
uptime: true # disabled by default
disk: / # disabled by default, use mount point of disk(s) in glances. Can also be a list (see below)
+ diskUnits: bytes # optional, bytes (default) or bbytes. Only applies to disk
expanded: true # show the expanded view
label: MyMachine # optional
```
diff --git a/docs/widgets/info/resources.md b/docs/widgets/info/resources.md
index 35f2177b..b4f85d69 100644
--- a/docs/widgets/info/resources.md
+++ b/docs/widgets/info/resources.md
@@ -22,6 +22,7 @@ _Note: unfortunately, the package used for getting CPU temp ([systeminformation]
uptime: true
units: imperial # only used by cpu temp
refresh: 3000 # optional, in ms
+ diskUnits: bytes # optional, bytes (default) or bbytes. Only applies to disk
```
You can also pass a `label` option, which allows you to group resources under named sections,
diff --git a/docs/widgets/services/customapi.md b/docs/widgets/services/customapi.md
index d392f0a9..7f26f80f 100644
--- a/docs/widgets/services/customapi.md
+++ b/docs/widgets/services/customapi.md
@@ -16,6 +16,8 @@ widget:
password: password # auth - optional
method: GET # optional, e.g. POST
headers: # optional, must be object, see below
+ requestBody: # optional, can be string or object, see below
+ display: # optional, default to block, see below
mappings:
- field: key # needs to be YAML string or object
label: Field 1
@@ -43,6 +45,15 @@ widget:
locale: nl # optional
style: short # optional - defaults to "long". Allowed values: `["long", "short", "narrow"]`.
numeric: auto # optional - defaults to "always". Allowed values `["always", "auto"]`.
+ - field: key
+ label: Field 6
+ format: text
+ additionalField: # optional
+ field:
+ hourly:
+ time: other key
+ color: theme # optional - defaults to "". Allowed values: `["theme", "adaptive", "black", "white"]`.
+ format: date # optional
```
Supported formats for the values are `text`, `number`, `float`, `percent`, `bytes`, `bitrate`, `date` and `relativeDate`.
@@ -93,7 +104,7 @@ mappings:
## Data Transformation
-You can manipulate data with the following tools `remap`, `scale` and `suffix`, for example:
+You can manipulate data with the following tools `remap`, `scale`, `prefix` and `suffix`, for example:
```yaml
- field: key4
@@ -110,7 +121,42 @@ You can manipulate data with the following tools `remap`, `scale` and `suffix`,
label: Power
format: float
scale: 0.001 # can be number or string e.g. 1/16
- suffix: kW
+ suffix: "kW"
+- field: key6
+ label: Price
+ format: float
+ prefix: "$"
+```
+
+## List View
+
+You can change the default block view to a list view by setting the `display` option to `list`.
+
+The list view can optionally display an additional field next to the primary field.
+
+`additionalField`: Similar to `field`, but only used in list view. Displays additional information for the mapping object on the right.
+
+`field`: Defined the same way as other custom api widget fields.
+
+`color`: Allowed options: `"theme", "adaptive", "black", "white"`. The option `adaptive` will apply a color using the value of the `additionalField`, green for positive numbers, red for negative numbers.
+
+```yaml
+- field: key
+ label: Field
+ format: text
+ remap:
+ - value: 0
+ to: None
+ - value: 1
+ to: Connected
+ - any: true # will map all other values
+ to: Unknown
+ additionalField:
+ field:
+ hourly:
+ time: key
+ color: theme
+ format: date
```
## Custom Headers
@@ -121,3 +167,16 @@ Pass custom headers using the `headers` option, for example:
headers:
X-API-Token: token
```
+
+## Custom Request Body
+
+Pass custom request body using the `requestBody` option in either a string or object format. Objects will automatically be converted to a JSON string.
+
+```yaml
+requestBody:
+ foo: bar
+# or
+requestBody: "{\"foo\":\"bar\"}"
+```
+
+Both formats result in `{"foo":"bar"}` being sent as the request body. Don't forget to set your `Content-Type` headers!
diff --git a/docs/widgets/services/gitea.md b/docs/widgets/services/gitea.md
new file mode 100644
index 00000000..bf75aa69
--- /dev/null
+++ b/docs/widgets/services/gitea.md
@@ -0,0 +1,17 @@
+---
+title: Gitea
+description: Gitea Widget Configuration
+---
+
+Learn more about [Gitea](https://gitea.com).
+
+API token requires `notifications` and `repository` permissions. See the [gitea documentation](https://docs.gitea.com/development/api-usage#generating-and-listing-api-tokens) for details on generating tokens.
+
+Allowed fields: ["notifications", "issues", "pulls"]
+
+```yaml
+widget:
+ type: gitea
+ url: http://gitea.host.or.ip:port
+ key: giteaapitoken
+```
diff --git a/docs/widgets/services/glances.md b/docs/widgets/services/glances.md
index d8f9e9ca..134dcb5f 100644
--- a/docs/widgets/services/glances.md
+++ b/docs/widgets/services/glances.md
@@ -18,6 +18,7 @@ widget:
username: user # optional if auth enabled in Glances
password: pass # optional if auth enabled in Glances
metric: cpu
+ diskUnits: bytes # optional, bytes (default) or bbytes. Only applies to disk
```
_Please note, this widget does not need an `href`, `icon` or `description` on its parent service. To achieve the same effect as the examples above, see as an example:_
diff --git a/docs/widgets/services/moonraker.md b/docs/widgets/services/moonraker.md
index 6de62ec6..2ee1a4e2 100644
--- a/docs/widgets/services/moonraker.md
+++ b/docs/widgets/services/moonraker.md
@@ -12,3 +12,12 @@ widget:
type: moonraker
url: http://moonraker.host.or.ip:port
```
+
+If your moonraker instance has an active authorization and your homepage ip isn't whitelisted you need to add your api key ([Authorization Documentation](https://moonraker.readthedocs.io/en/latest/web_api/#authorization)).
+
+```yaml
+widget:
+ type: moonraker
+ url: http://moonraker.host.or.ip:port
+ key: api_keymoonraker
+```
diff --git a/docs/widgets/services/planit.md b/docs/widgets/services/planit.md
new file mode 100644
index 00000000..d1cebfaa
--- /dev/null
+++ b/docs/widgets/services/planit.md
@@ -0,0 +1,15 @@
+---
+title: Plant-it
+description: Plant-it Widget Configuration
+---
+
+Learn more about [Plantit](https://github.com/MDeLuise/plant-it).
+
+API key can be created from the REST API.
+
+```yaml
+widget:
+ type: plantit
+ url: http://plant-it.host.or.ip:port # api port
+ key: plantit-api-key
+```
diff --git a/docs/widgets/services/truenas.md b/docs/widgets/services/truenas.md
index 6d747ef1..24350490 100644
--- a/docs/widgets/services/truenas.md
+++ b/docs/widgets/services/truenas.md
@@ -9,6 +9,8 @@ Allowed fields: `["load", "uptime", "alerts"]`.
To create an API Key, follow [the official TrueNAS documentation](https://www.truenas.com/docs/scale/scaletutorials/toptoolbar/managingapikeys/).
+A detailed pool listing is disabled by default, but can be enabled with the `enablePools` option.
+
```yaml
widget:
type: truenas
@@ -16,4 +18,5 @@ widget:
username: user # not required if using api key
password: pass # not required if using api key
key: yourtruenasapikey # not required if using username / password
+ enablePools: true # optional, defaults to false
```
diff --git a/mkdocs.yml b/mkdocs.yml
index 572d36b4..c6ecfda9 100644
--- a/mkdocs.yml
+++ b/mkdocs.yml
@@ -57,6 +57,7 @@ nav:
- widgets/services/gamedig.md
- widgets/services/gatus.md
- widgets/services/ghostfolio.md
+ - widgets/services/gitea.md
- widgets/services/glances.md
- widgets/services/gluetun.md
- widgets/services/gotify.md
@@ -103,6 +104,7 @@ nav:
- widgets/services/photoprism.md
- widgets/services/pialert.md
- widgets/services/pihole.md
+ - widgets/services/plantit.md
- widgets/services/plex-tautulli.md
- widgets/services/plex.md
- widgets/services/portainer.md
diff --git a/public/locales/en/common.json b/public/locales/en/common.json
index 0d67c3f9..7081ca7a 100755
--- a/public/locales/en/common.json
+++ b/public/locales/en/common.json
@@ -853,5 +853,16 @@
"wled": {
"deviceName": "Device Name",
"deviceState": "Device State"
+ },
+ "plantit": {
+ "events": "Events",
+ "plants": "Plants",
+ "photos": "Photos",
+ "species": "Species"
+ },
+ "gitea": {
+ "notifications": "Notifications",
+ "issues": "Issues",
+ "pulls": "Pull Requests"
}
}
\ No newline at end of file
diff --git a/public/locales/it/common.json b/public/locales/it/common.json
index 6f10baf7..e61fff8b 100644
--- a/public/locales/it/common.json
+++ b/public/locales/it/common.json
@@ -803,5 +803,11 @@
"netdata": {
"warnings": "Warnings",
"criticals": "Criticals"
+ },
+ "plantit": {
+ "events": "Eventi",
+ "plants": "Piante",
+ "species": "Specie",
+ "images": "Immagini"
}
}
diff --git a/src/components/bookmarks/group.jsx b/src/components/bookmarks/group.jsx
index c5e6a2f1..b13aeac1 100644
--- a/src/components/bookmarks/group.jsx
+++ b/src/components/bookmarks/group.jsx
@@ -1,4 +1,4 @@
-import { useRef } from "react";
+import { useRef, useEffect } from "react";
import classNames from "classnames";
import { Disclosure, Transition } from "@headlessui/react";
import { MdKeyboardArrowDown } from "react-icons/md";
@@ -7,8 +7,13 @@ import ErrorBoundary from "components/errorboundry";
import List from "components/bookmarks/list";
import ResolvedIcon from "components/resolvedicon";
-export default function BookmarksGroup({ bookmarks, layout, disableCollapse }) {
+export default function BookmarksGroup({ bookmarks, layout, disableCollapse, groupsInitiallyCollapsed }) {
const panel = useRef();
+
+ useEffect(() => {
+ if (layout?.initiallyCollapsed ?? groupsInitiallyCollapsed) panel.current.style.height = `0`;
+ }, [layout, groupsInitiallyCollapsed]);
+
return (
-
+
{({ open }) => (
<>
{layout?.header !== false && (
diff --git a/src/components/filecontent.jsx b/src/components/filecontent.jsx
deleted file mode 100644
index 1dd6266a..00000000
--- a/src/components/filecontent.jsx
+++ /dev/null
@@ -1,10 +0,0 @@
-import useSWR from "swr";
-
-export default function FileContent({ path, loadingValue, errorValue, emptyValue = "" }) {
- const fetcher = (url) => fetch(url).then((res) => res.text());
- const { data, error, isLoading } = useSWR(`/api/config/${path}`, fetcher);
-
- if (error) return errorValue;
- if (isLoading) return loadingValue;
- return data || emptyValue;
-}
diff --git a/src/components/quicklaunch.jsx b/src/components/quicklaunch.jsx
index 23f7cef9..aaa40493 100644
--- a/src/components/quicklaunch.jsx
+++ b/src/components/quicklaunch.jsx
@@ -120,7 +120,7 @@ export default function QuickLaunch({
});
if (showSearchSuggestions && searchProvider.suggestionUrl) {
- if (searchString.trim() !== searchSuggestions[0]) {
+ if (searchString.trim() !== searchSuggestions[0]?.trim()) {
fetch(
`/api/search/searchSuggestion?query=${encodeURIComponent(searchString)}&providerName=${
searchProvider.name ?? "Custom"
diff --git a/src/components/services/group.jsx b/src/components/services/group.jsx
index bcc3ce5d..cdbb89f3 100644
--- a/src/components/services/group.jsx
+++ b/src/components/services/group.jsx
@@ -1,4 +1,4 @@
-import { useRef } from "react";
+import { useRef, useEffect } from "react";
import classNames from "classnames";
import { Disclosure, Transition } from "@headlessui/react";
import { MdKeyboardArrowDown } from "react-icons/md";
@@ -6,9 +6,21 @@ import { MdKeyboardArrowDown } from "react-icons/md";
import List from "components/services/list";
import ResolvedIcon from "components/resolvedicon";
-export default function ServicesGroup({ group, services, layout, fiveColumns, disableCollapse, useEqualHeights }) {
+export default function ServicesGroup({
+ group,
+ services,
+ layout,
+ fiveColumns,
+ disableCollapse,
+ useEqualHeights,
+ groupsInitiallyCollapsed,
+}) {
const panel = useRef();
+ useEffect(() => {
+ if (layout?.initiallyCollapsed ?? groupsInitiallyCollapsed) panel.current.style.height = `0`;
+ }, [layout, groupsInitiallyCollapsed]);
+
return (