The flow of a transaction is like this:
- Users create and submit transactions
- Nodes gossip the transaction in the network, validating them, adding them to their mempools and forwarding them
- Miners select transactions from their mempools to build block templates
- A new block is found that includes the submitted transaction and the transaction is confirmed
Miners mine continuously—each of them is always trying to find a new block. A block is found when a block header hashes to something that is lower than the difficulty target. There is no way to know whether a block header candidate is a valid block until it is hashed. Since every miner is trying to pay themselves the mining reward, they all have unique block templates (as their transactions don't match, the Merkle root of the transactions differs). This means that every miner is searching previously unseen block candidates and finding a new block essentially comes down to an independent random event with a minuscule chance for every block template. Sometimes it only takes seconds for a new block to be found, and sometimes it takes more than an hour. The difficulty retargets automatically to keep the expected block interval to about 10 minutes.
Miners continuously update their block templates whenever they hear about new transactions. As you said, they pick the transactions with the highest feerates, to build the block template that will collect the most fees.
When miners hear about new blocks, they briefly use an empty block template that doesn't include any transactions. The miners do this to avoid including a transaction that the previous block already included which would make their new block be in conflict and invalid. As soon as the miners have processed the previous block, they build a new block template with transactions again and use that. So, we usually only see empty blocks if a new block is found seconds after a previous block, or if there are actually no transactions waiting to be confirmed.