I'd recommend solving this in pretty much the usual way: with a scripting language. (This could be a text-based language like LUA, a nodegraph based visual scripting tool, pre-digested bytecode, etc.)
ECS exists to help make the things you do hundreds, thousands, or even more times each frame execute as efficiently as possible.
But one-off level scripts like this are not that kind of thing, almost by definition. You'll usually have only a few dozen to deal with, and many will be event-driven like the button example, rather than updating continually. So it's totally OK that they're not componentized — as long as you're careful not to do anything hideously wasteful in these scripts, they shouldn't be a major drain on your framerate.
I'd recommend having one of your ECS systems be your scripting engine. The data it operates on are virtual machine state vectors, representing the current state of execution of each custom script. When this system updates, it runs your scripting virtual machine to tick these scripts forward a frame, doing whatever custom stuff they want to do.
Your buttons, as part of their data, can specify a script entry point that should be fired when they're interacted with. Your button interaction system processes the interaction, and if it needs to call a new script it logs a message for the scripting system, saying "spin up a new VM state at this entry point and start executing it" (optionally with a few IDs pre-populated on the stack so you can refer to which button was pressed & who pressed it)
The scripting system, when its turn comes in the update order, picks up that message and starts executing the script. If it finishes in one frame it can immediately recycle the VM state for the next script to be called. Otherwise the script can yield to resume later (to control staged transitions over time and the like), and the system can keep its state on the back burner to resume on next frame's update.
This gives you the flexibility to combine high-performance, data-oriented components and systems for the stuff you do uniformly/frequently, with flexible/designer-friendly scripts for the stuff that's more one-off or needs frequent adjustment as your game's design evolves.
If you find you're doing something complex & heavyweight enough or frequently enough in one of these scripts that it is impacting performance, then that's a good candidate to pull it out and make it into its own component(s)/system(s) in native code - something you can do incrementally as needed, as you learn more about your game's performance characteristics.