Q:

How to make an auto-sized Swing Popup with wrapped text?

I am trying to make a popup window that will contain just a single label. Ideally, I would specify a maximum width for the popup, and a string to display, and the popup would automatically size itself such that the text would wrap if it exceeds the specified max width, and it would be the correct height. Without complex usage of FontMetrics to brute-force through the calculations, is it possible to do such a thing in Swing? Here is an example of what I'm trying to do:
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.FlowLayout;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.Popup;
import javax.swing.PopupFactory;
import javax.swing.SpringLayout;

public class PopupTest extends JFrame {
    private static final String FOX_MESSAGE = "<html>The lithe, quick, brown fox jumps " +
                                              "happily and steadily over the fat, slobbery, " +
                                              "lazy dogs</html>";
    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {
            public void run() {
                try {
                    PopupTest frame = new PopupTest();
                    frame.setVisible(true);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });
    }

    public PopupTest() {
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setBounds(100, 100, 450, 300);
        final JPanel contentPane = new JPanel();
        setContentPane(contentPane);
        contentPane.setLayout(new FlowLayout(FlowLayout.CENTER, 5, 5));

        final JButton btnLaunch = new JButton("Launch popup");
        btnLaunch.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent arg0) {
                Point btnLoc = btnLaunch.getLocationOnScreen();
                FoxPopup popup = new FoxPopup(FOX_MESSAGE, 200);
                popup.launch(contentPane, btnLoc.x, btnLoc.y + btnLaunch.getHeight());              
            }
        });
        contentPane.add(btnLaunch);
    }

    public class FoxPopup extends JPanel {
        private static final int ARBITRARILY_TALL = 9999;
        private Popup popup;

        public FoxPopup(String message, int width) {
            setBackground(Color.pink);
//          SpringLayout springLayout = new SpringLayout();
//          setLayout(springLayout);

            JLabel lblDescription = new JLabel(message);
//          springLayout.putConstraint(SpringLayout.NORTH, lblDescription, 5, SpringLayout.NORTH, this);
//          springLayout.putConstraint(SpringLayout.WEST, lblDescription, 5, SpringLayout.WEST, this);
//          springLayout.putConstraint(SpringLayout.EAST, lblDescription, -5, SpringLayout.EAST, this);
            add(lblDescription);
//          setPreferredSize(new Dimension(width, 65));
            setMaximumSize(new Dimension(width, ARBITRARILY_TALL));
        }

        public void launch(Component owner, int x, int y) {
            popup = PopupFactory.getSharedInstance().getPopup(owner, this, x, y);
            popup.show();
        }
    }
}

If I compile and run the above code, then I get a wide popup (wider than the 200 pixel max that I have requested) with all the text on a single line. If I uncomment all of the commented lines, then I get the desired behavior, but I must specify both the width and height in the setPreferredSize() call, and so it is not robust to handle different input messages.

Edit with solution based on Andrew Thompson's answer below
public class PopupTest extends JFrame {
    private static final String FOX_MESSAGE = "The lithe, quick, brown fox jumps " +
                                              "happily and steadily over the fat, slobbery, " +
                                              "lazy dogs";
    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {
            public void run() {
                try {
                    PopupTest frame = new PopupTest();
                    frame.setVisible(true);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });
    }

    public PopupTest() {
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setBounds(100, 100, 450, 300);
        final JPanel contentPane = new JPanel();
        setContentPane(contentPane);
        contentPane.setLayout(new FlowLayout(FlowLayout.CENTER, 5, 5));

        final JButton btnLaunch = new JButton("Launch popup");
        btnLaunch.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent arg0) {
                Point btnLoc = btnLaunch.getLocationOnScreen();
                FoxPopup popup = new FoxPopup(FOX_MESSAGE, 200);
                popup.launch(contentPane, btnLoc.x, btnLoc.y + btnLaunch.getHeight());              
            }
        });
        contentPane.add(btnLaunch);
    }

    public static class FoxPopup extends JPanel {
        private Popup popup;

        public FoxPopup(String message, int width) {
            setBackground(Color.pink);

            JLabel lblDescription = new JLabel(formatMessage(message, width));
            add(lblDescription);
        }

        private static String formatMessage(String message, int width) {
            return String.format("<html><body style='width: %d'>%s</body></html>", width, message);
        }

        public void launch(Component owner, int x, int y) {
            popup = PopupFactory.getSharedInstance().getPopup(owner, this, x, y);
            popup.show();
        }
    }
}

A:

See FixedWidthText which uses CSS to fix the width:
 

