Accordion List

I’d like to get a List-Card that acts like an accordion. But all I get is that the list items behave like an accordion, not the list itself:


I would’ve expected that it’s enough to click “accordian list” in “edit list card”

But since that didn’t work I tried to activate it by going to the list items and then “edit accordion code”. But that only gets an accordion symbol of these items.

component: oh-list-card
config:
  title: Accordion List - Test
  accordionList: true
slots:
  default:
    - component: oh-list-item
      config:
        title: A
      slots:
        accordion:
          - component: oh-list-card
            config:
              accordionList: false
              noBorder: true
              noShadow: true
              outline: true
    - component: oh-list-item
      config:
        title: B
      slots:
        accordion: []
    - component: oh-list-item
      config:
        title: C
      slots:
        accordion: []

I’m sure the solution is very simple, I just didn’t find it. Looking forward to your help :slight_smile:

1 Like

Had similar question myself a while back. AccordionList is not just an overall parameter of the entire list widget, it is specific to each level/item. The accordionList: true config has to be in the direct parent widget (that is, the one where you are populating the accodion slot) of any of the accordion items. It is not sufficient just to label then entire list widget as an accordion list.

As for the chevrons, if you want those items to still be the parents of accordion items but not display the chevron, there’s a no-chevron parameter that I think you can set to true in order to have those removed.

I’m not sure I understand what you’re saying :frowning: (too inexperienced, I’m afraid)
The way I see it, the list-item doesn’t have a parent, it is the parent. And the kids are “A”, “B” and “C”. And there the only thing is “Edit Accordion Code” which seems to get me the chevrons I don’t want.
Adding “no-chevron: true” didn’t help either:

component: oh-list-card
config:
  title: Accordion List - Test
  accordionList: true
slots:
  default:
    - component: oh-list-item
      config:
        title: A
      slots:
        accordion:
          - component: oh-list-card
            config:
              accordionList: true
              noBorder: true
              noShadow: true
              outline: true
              no-chevron: true 
    - component: oh-list-item
      config:
        title: B
      slots:
        accordion: []
    - component: oh-list-item
      config:
        title: C
      slots:
        accordion: []

I hope it’s not asking too much (you’ve already been very helpful in my other post), could you change the example code so it’d work? I could transfer the rest and probably understand it then, too.

Here’s

I think the thing that is confusing you is that there is a difference between the list-card and the actual list widget. All of the oh specific cards are just an f7 card (that is a container object with some basic slots, header, footer, etc. and some default background settings) with some direct access to widget that is by default rendered in the card’s content area. So when creating an oh-list-card you start with a card and an empty list widget but still must populate that list widget with at least one visible list item. The title and many other config parameters are for the card they are not the first row of the actual list. The following YAML: demonstrates some of this

component: oh-list-card
config:
  title: This is just the card title not the list widget
  accordionList: true
slots:
  default:
    - component: oh-list-item
      config:
        title: At least one list item must be visible for the list widget to be drawn
      slots:
        accordion:
          - component: oh-list
            config: {}
            slots:
              default:
              - component: oh-list-item
                config:
                  title: A
              - component: oh-list-item
                config:
                  title: B
              - component: oh-list-item
                config:
                  title: C
              - component: oh-list
                config:
                  accordionList: true
                  thisDoesNotWork: This list is not rendered because it has no child items and there is nothing to show
              - component: oh-list
                config:
                  accordionList: true
                slots:
                  default:
                    - component: oh-list-item
                      config:
                        title: But you can nest accordions lists as long as there are items to be drawn
                      slots:
                        accordion:
                          - component: oh-list
                            config: {}
                            slots:
                              default:
                                - component: oh-list-item
                                  config:
                                    title: E
                                - component: oh-list-item
                                  config:
                                    title: F
                                - component: oh-list-item
                                  config:
                                    title: G

Produces


With a first level accordion

And a second level accordion

But the empty oh-list is not rendered anywhere because it has no items to display (thisDoesNotWork parameter doesn’t cause a YAML or rendering error because unrecognized keys are simply ignored).

6 Likes

Took me a while, but now (I think) I understand it. Good you gave the example with the code, otherwise I wouldn’t have had a chance :wink:
One question: what are the “{}” good for? I noticed it doesn’t work without them, but what’s their function?

And maybe one (hopefully last) thing I can’t get done which would make things look nicer: I how can I align nested objects? Here’s (Alignment in nested objects) the entire description but nobody could help me so far …

That’s just an empty array. I can’t tell you why it’s needed in some cases but not in others. As you noted, the oh-list doesn’t render without the config: key, but if you don’t need to set any config parameters then you can just use {} to pass an empty set to it and that’s sufficient to keep the renderer happy.

Hello,

i hope its ok to place my question here:
Is ist also possible to configure if an accordion list is opened oder closed by default or depending on a state?
For example i would like to create an accordion list with several scene triggers sorted by daytime. So when its morning (depending on item state) the morning part should be opened and all others closed.

Thanks in advance,
Alex

Yes it is - you can use accordionItemOpened: true as a config parameter on the f7-/oh-list-item component.

3 Likes

Does anyone know if it’s possible to use an accordion list in a repeater?

I have a working accordion list, but when I try to use it in an oh-repeater I can’t open it.

component: f7-card
config:
  style:
    border-radius: 20px
    margin-bottom: 40px
  themeDark: false
  title: Tracked Mobile Devices
slots:
  default:
    - component: oh-list
      config:
        accordionList: true
      slots:
        default:
          - component: oh-repeater
            config:
              fetchMetadata: semantics, widgetOrder
              for: item
              fragment: true
              itemTags: TrackedDevice
              sourceType: itemsWithTags
            slots:
              default:
                - component: oh-list-item
                  config:
                    title: device // will populate with the real name later
                    icon: f7:chart_bar_circle_fill
                  slots:
                    accordion:
                      - component: oh-list
                        config: {}
                        slots:
                          default:
                            - component: oh-toggle-item
                              config:
                                icon: f7:globe
                                iconColor: green
                                title: Tracking
                            - component: oh-toggle-item
                              config:
                                icon: f7:globe
                                iconColor: blue
                                title: GeoCoding
                            - component: oh-toggle-item
                              config:
                                icon: f7:globe
                                iconColor: purple
                                title: GeoFencing

You must add accordionList: true to the repeater config. The list item will only render the accordion content if it’s direct parent has this property; in this case, the repeater.

Boom! Thank you!

Do you know if it’s possible to make these accordions close when another is opened? So that only one is open at a time?

Technically yes. In practice, particularly if you have a lot of accordions, it would be extremely cumbersome.

Each list item has an accordionItemOpened property which takes a boolean value and true opens the accordion and false closes it. This property can be linked to a widget variable and the action of some other widget can then easily control the open closed state of the item. Within the limited function of the custom widget system building up the logic to control each accordion from the action of each other one would quickly get absurd.

Not sure what I did, but this widget is doing what I wanted, ie only one accordion opens at a time

uid: widget_MobileDevices
tags: []
props:
  parameters: []
  parameterGroups: []
timestamp: Apr 24, 2022, 3:27:19 PM
component: f7-card
config:
  style:
    border-radius: 20px
    margin-bottom: 40px
  title: Mobile Devices
slots:
  default:
    - component: oh-list
      config:
        accordionList: true
      slots:
        default:
          - component: oh-repeater
            config:
              accordionList: true
              fetchMetadata: semantics
              for: item
              fragment: false
              itemTags: TrackedDevice
              sourceType: itemsWithTags
            slots:
              default:
                - component: oh-list-item
                  config:
                    badge: '=(items[loop.item.name + "_Tracking"].state == "OFF") ? "UNTRACKED" : (items[loop.item.name + "_HomeWiFi"].state == "ON" || Number.parseFloat(items[loop.item.name + "_DistanceFromHome"].state) < Number.parseFloat(items.MobileDevices_GeoFenceDistance.state)) ? "HOME": (Number.parseFloat(items[loop.item.name + "_DistanceFromHome"].state) < 1000 && items[loop.item.name + "_HomeWiFi"].state == "OFF" && items[loop.item.name + "_Address"].state != "OFFLINE") ? "< 1km AWAY": (items[loop.item.name + "_Address"].state == "OFFLINE") ? "OFFLINE": (items[loop.item.name + "_Address"].state == "MAMOS" || items[loop.item.name + "_Address"].state == "TEACH JAMES") ? items[loop.item.name + "_Address"].state: (Number.parseFloat(items[loop.item.name + "_DistanceFromHome"].state) * 0.001).toFixed(2) + " km AWAY"'
                    badge-color: '=(items[loop.item.name + "_Tracking"].state == "OFF") ? "black" : (items[loop.item.name + "_HomeWiFi"].state == "ON" || Number.parseFloat(items[loop.item.name + "_DistanceFromHome"].state) < Number.parseFloat(items.MobileDevices_GeoFenceDistance.state) ) ? "green": (items[props.address].state == "OFFLINE") ? "black": "red"'
                    footer: '=(items[loop.item.name + "_Address"].state != "OFFLINE" && items[loop.item.name + "_Address"].state != "HOME" && items[loop.item.name + "_Address"].state != "MAMOS" && items[loop.item.name + "_Address"].state != "TEACH JAMES") ? items[loop.item.name + "_Address"].state: " "'
                    icon: ="material:" + loop.item.category.split('-')[0]
                    iconColor: =loop.item.category.split('-')[1]
                    title: =loop.item.label
                  slots:
                    accordion:
                      - component: f7-list
                        config:
                          style:
                            margin-left: 20px
                        slots:
                          default:
                            - component: oh-list-item
                              config:
                                badge: =items[loop.item.name + "_Battery"].displayState
                                badgeColor: '=(Number(items[loop.item.name + "_Battery"].state) > 30) ? "green" : (Number(items[loop.item.name + "_Battery"].state) < 10 ) ? "red" : "yellow"'
                                icon: '=(Number(items[loop.item.name + "_Battery"].state) > 30) ? "f7:battery_100" : (Number(items[loop.item.name + "_Battery"].state) < 10 ) ? "f7:battery_0" : "f7:battery_25"'
                                iconColor: =loop.item.category.split("-")[1]
                                item: =loop.item.name + "_Battery"
                                title: Battery
                            - component: oh-toggle-item
                              config:
                                color: =loop.item.category.split('-')[1]
                                footer: Enable/Disable basic tracking
                                icon: f7:map_pin_ellipse
                                iconColor: '=(items[loop.item.name + "_Tracking"].state == "ON") ? loop.item.category.split("-")[1] : "gray" '
                                item: =loop.item.name + "_Tracking"
                                title: Tracking
                            - component: oh-toggle-item
                              config:
                                color: =loop.item.category.split('-')[1]
                                footer: Display address. Counts towards Google API request count
                                icon: f7:map
                                iconColor: '=(items[loop.item.name + "_GeoCoding"].state == "ON") ? loop.item.category.split("-")[1] : "gray" '
                                item: =loop.item.name + "_GeoCoding"
                                style:
                                  opacity: '=(items[loop.item.name + "_Tracking"].state == "ON") ? "100%" : "50%" '
                                title: GeoCoding
                            - component: oh-toggle-item
                              config:
                                color: =loop.item.category.split('-')[1]
                                footer: Reccommend one device per person
                                icon: f7:skew
                                iconColor: '=(items[loop.item.name + "_GeoFencing"].state == "ON") ? loop.item.category.split("-")[1] : "gray" '
                                item: =loop.item.name + "_GeoFencing"
                                style:
                                  opacity: '=(items[loop.item.name + "_Tracking"].state == "ON") ? "100%" : "50%" '
                                title: GeoFencing
                            - component: oh-list-item
                              config:
                                action: command
                                actionCommand: ON
                                actionItem: =loop.item.name + "_FindMyPhone"
                                color: =loop.item.category.split('-')[1]
                                listButton: true
                                title: Find My Device
          - component: oh-list-item
            config:
              action: navigate
              actionPage: page:page_map_MobileDevices
              icon: f7:map
              iconColor: blue
              title: View on Map
          - component: oh-list-item
            config:
              icon: f7:chart_bar_circle_fill
              title: Chart
            slots:
              accordion:
                - component: oh-list
                  config: {}
                  slots:
                    default:
                      - component: widget:widget_grafana
                        config:
                          dashboard: openhab-states
                          gID: pr3PCVwnk
                          oID: "1"
                          pID: "4"
                          refresh: 1m
                          timerange: from=now-1w&to=now;1w,from=now-2d&to=now;2d,from=now-1d&to=now;24h,from=now-12h&to=now;12h,from=now-6h&to=now;6h

Interesting. Apparently, by setting fragment to false (which I almost never do in my repeater lists). the added container around the list items automatically applies the limitation of only one expanded accordion at a time. That’s good to know.

Learn something new everyday…

I don’t think so.
I deleted that because I don’t know what it is and it didn’t seem to make any difference, and it still works

fragment: false and deleting the property are equivalent. If you include fragment: true (which I use on 99% of my repeater lists) instead you’ll see that the accordions stay open even if you open other ones.

Most of what the fragment property does is under the hood: the repeater item’s will be wrapped in an extra <div> container if you leave fragment out or set it to false, but if you set it to true they are placed in a vue fragment which doesn’t render as any type of container in the end.