Repository: grommet/grommet (JavaScript)

Issue:
v2 Select: optionally tell Drop within select to respect the width of the main Select target
Today if the content inside the Drop is bigger than the Target, the drop will expand. it would be nice to have a way to tell the Drop to wrap and fix position so that the Select options are always aligned to the end of the target.


Discussion:
Definitely not ideal, but the solution I resolved to using for this was to get the dimensions of the target element and set the max width with the style prop `{{maxWidth: inputTarget.clientWidth}}`. Definitely not ideal but it seems to work for the moment ! 

In case anyone else stumbles across this !
Taking a look at this issue



Patch:
diff --git a/src/js/components/Drop/DropContainer.js b/src/js/components/Drop/DropContainer.js
index c6a912bde7e..f37db0393af 100644
--- a/src/js/components/Drop/DropContainer.js
+++ b/src/js/components/Drop/DropContainer.js
@@ -62,7 +62,6 @@ const DropContainer = forwardRef(
       portalId,
     ]);
     const dropRef = useRef();
-
     useEffect(() => {
       // We try to preserve the maxHeight as changing it causes any scroll
       // position to be lost. We set the maxHeight on mount and if the window
@@ -85,12 +84,17 @@ const DropContainer = forwardRef(
           const targetRect = findVisibleParent(target).getBoundingClientRect();
           const containerRect = container.getBoundingClientRect();
           // determine width
-          const width = Math.min(
-            stretch
-              ? Math.max(targetRect.width, containerRect.width)
-              : containerRect.width,
-            windowWidth,
-          );
+          let width;
+          if (stretch) {
+            width = Math.min(
+              stretch === 'align'
+                ? Math.min(targetRect.width, containerRect.width)
+                : Math.max(targetRect.width, containerRect.width),
+              windowWidth,
+            );
+          } else {
+            width = Math.min(containerRect.width, windowWidth);
+          }
           // set left position
           let left;
           if (align.left) {
diff --git a/src/js/components/Drop/README.md b/src/js/components/Drop/README.md
index d41d38df2db..603aabb85f6 100644
--- a/src/js/components/Drop/README.md
+++ b/src/js/components/Drop/README.md
@@ -96,12 +96,14 @@ boolean
 
 **stretch**
 
-Whether the drop element should be stretched to at least match the
-      width of the target element. The default is true because
-      that is what most uses of Drop want, like Select and Menu. Defaults to `true`.
+If set to true the drop element will be stretched to at least match the
+      width of the target element. If set to align the width of the drop element
+      will be restricted to the width of the target element. The default is true
+      because that is what most uses of Drop want, like Select and Menu. Defaults to `true`.
 
 ```
 boolean
+align
 ```
 
 **target**
diff --git a/src/js/components/Drop/doc.js b/src/js/components/Drop/doc.js
index aed83488907..8ee8a6f609b 100644
--- a/src/js/components/Drop/doc.js
+++ b/src/js/components/Drop/doc.js
@@ -54,11 +54,12 @@ export const doc = Drop => {
     restrictFocus: PropTypes.bool
       .description('Whether the drop should control focus.')
       .defaultValue(false),
-    stretch: PropTypes.bool
+    stretch: PropTypes.oneOfType([PropTypes.bool, PropTypes.oneOf(['align'])])
       .description(
-        `Whether the drop element should be stretched to at least match the
-      width of the target element. The default is true because
-      that is what most uses of Drop want, like Select and Menu.`,
+        `If set to true the drop element will be stretched to at least match the
+      width of the target element. If set to align the width of the drop element
+      will be restricted to the width of the target element. The default is true
+      because that is what most uses of Drop want, like Select and Menu.`,
       )
       .defaultValue(true),
     target: PropTypes.object.description(
diff --git a/src/js/components/Drop/index.d.ts b/src/js/components/Drop/index.d.ts
index f3185cffded..463659334ee 100644
--- a/src/js/components/Drop/index.d.ts
+++ b/src/js/components/Drop/index.d.ts
@@ -23,7 +23,7 @@ export interface DropProps {
     | string;
   responsive?: boolean;
   restrictFocus?: boolean;
-  stretch?: boolean;
+  stretch?: boolean | 'align';
   target?: object;
   trapFocus?: boolean;
   plain?: boolean;
diff --git a/src/js/components/DropButton/DropButton.js b/src/js/components/DropButton/DropButton.js
index d5d589606ee..082f6eabb17 100644
--- a/src/js/components/DropButton/DropButton.js
+++ b/src/js/components/DropButton/DropButton.js
@@ -31,7 +31,6 @@ const DropButton = forwardRef(
         setShow(open);
       }
     }, [open, show]);
-
     const onDropClose = useCallback(
       event => {
         // if the user has clicked on our Button, don't do anything here,


Test patch:
diff --git a/src/js/components/Drop/__tests__/Drop-test.js b/src/js/components/Drop/__tests__/Drop-test.js
index 72d38210245..c9c686a6379 100644
--- a/src/js/components/Drop/__tests__/Drop-test.js
+++ b/src/js/components/Drop/__tests__/Drop-test.js
@@ -36,6 +36,7 @@ class TestInput extends Component {
       theme,
       elevation,
       containerTarget,
+      message = 'this is a test',
       ...rest
     } = this.props;
     const { showDrop } = this.state;
@@ -48,7 +49,7 @@ class TestInput extends Component {
           target={this.inputRef.current}
           {...rest}
         >
-          this is a test
+          {message}
         </Drop>
       );
     }
@@ -117,7 +118,13 @@ describe('Drop', () => {
 
   test('no stretch', () => {
     render(<TestInput stretch={false} />);
+    expectPortal('drop-node').toMatchSnapshot();
+  });
 
+  test('stretch = align', () => {
+    const message =
+      'test test test test test test test test test test test test test test';
+    render(<TestInput stretch="align" message={message} />);
     expectPortal('drop-node').toMatchSnapshot();
   });
 
diff --git a/src/js/components/Drop/__tests__/__snapshots__/Drop-test.js.snap b/src/js/components/Drop/__tests__/__snapshots__/Drop-test.js.snap
index 4e3ce1d9b1d..5f9465f4437 100644
--- a/src/js/components/Drop/__tests__/__snapshots__/Drop-test.js.snap
+++ b/src/js/components/Drop/__tests__/__snapshots__/Drop-test.js.snap
@@ -1767,6 +1767,124 @@ exports[`Drop should have no accessibility violations 1`] = `
 </div>
 `;
 
+exports[`Drop stretch = align 1`] = `
+.c0 {
+  display: -webkit-box;
+  display: -webkit-flex;
+  display: -ms-flexbox;
+  display: flex;
+  box-sizing: border-box;
+  max-width: 100%;
+  min-width: 0;
+  min-height: 0;
+  -webkit-flex-direction: column;
+  -ms-flex-direction: column;
+  flex-direction: column;
+  overflow: auto;
+  box-shadow: 0px 2px 4px rgba(0,0,0,0.20);
+}
+
+.c1 {
+  font-size: 18px;
+  line-height: 24px;
+  box-sizing: border-box;
+  -webkit-text-size-adjust: 100%;
+  -ms-text-size-adjust: 100%;
+  -moz-osx-font-smoothing: grayscale;
+  -webkit-font-smoothing: antialiased;
+  border-radius: 0px;
+  position: fixed;
+  z-index: 20;
+  outline: none;
+  background-color: #ffffff;
+  color: #444444;
+  opacity: 0;
+  -webkit-transform-origin: top left;
+  -ms-transform-origin: top left;
+  transform-origin: top left;
+  -webkit-animation: kPQHBD 0.1s forwards;
+  animation: kPQHBD 0.1s forwards;
+  -webkit-animation-delay: 0.01s;
+  animation-delay: 0.01s;
+}
+
+@media screen and (-ms-high-contrast:active),(-ms-high-contrast:none) {
+  .c1 {
+    display: -webkit-box;
+    display: -webkit-flex;
+    display: -ms-flexbox;
+    display: flex;
+    -webkit-align-items: stretch;
+    -webkit-box-align: stretch;
+    -ms-flex-align: stretch;
+    align-items: stretch;
+  }
+}
+
+<div
+  class="c0 c1"
+  data-g-portal-id="0"
+  id="drop-node"
+  style="left: 0px; width: 0.1px; top: 0px; max-height: 768px;"
+  tabindex="-1"
+>
+  test test test test test test test test test test test test test test
+</div>
+`;
+
+exports[`Drop stretch = align 2`] = `
+".glPSfV {
+  display: -webkit-box;
+  display: -webkit-flex;
+  display: -ms-flexbox;
+  display: flex;
+  box-sizing: border-box;
+  max-width: 100%;
+  min-width: 0;
+  min-height: 0;
+  -webkit-flex-direction: column;
+  -ms-flex-direction: column;
+  flex-direction: column;
+  overflow: auto;
+  box-shadow: 0px 2px 4px rgba(0, 0, 0, 0.2);
+}
+.kQAKuZ {
+  font-size: 18px;
+  line-height: 24px;
+  box-sizing: border-box;
+  -webkit-text-size-adjust: 100%;
+  -ms-text-size-adjust: 100%;
+  -moz-osx-font-smoothing: grayscale;
+  -webkit-font-smoothing: antialiased;
+  border-radius: 0px;
+  position: fixed;
+  z-index: 20;
+  outline: none;
+  background-color: #ffffff;
+  color: #444444;
+  opacity: 0;
+  -webkit-transform-origin: top left;
+  -ms-transform-origin: top left;
+  transform-origin: top left;
+  -webkit-animation: kPQHBD 0.1s forwards;
+  animation: kPQHBD 0.1s forwards;
+  -webkit-animation-delay: 0.01s;
+  animation-delay: 0.01s;
+}
+@media screen and (-ms-high-contrast: active), (-ms-high-contrast: none) {
+  .kQAKuZ {
+    display: -webkit-box;
+    display: -webkit-flex;
+    display: -ms-flexbox;
+    display: flex;
+    -webkit-align-items: stretch;
+    -webkit-box-align: stretch;
+    -ms-flex-align: stretch;
+    align-items: stretch;
+  }
+}"
+`;
+
 exports[`Drop theme elevation renders 1`] = `
 .c0 {
   display: -webkit-box;
diff --git a/src/js/components/__tests__/__snapshots__/README-test.js.snap b/src/js/components/__tests__/__snapshots__/README-test.js.snap
index 1ebc50a17b3..3cf6c858e3e 100644
--- a/src/js/components/__tests__/__snapshots__/README-test.js.snap
+++ b/src/js/components/__tests__/__snapshots__/README-test.js.snap
@@ -7279,12 +7279,14 @@ boolean
 
 **stretch**
 
-Whether the drop element should be stretched to at least match the
-      width of the target element. The default is true because
-      that is what most uses of Drop want, like Select and Menu. Defaults to \`true\`.
+If set to true the drop element will be stretched to at least match the
+      width of the target element. If set to align the width of the drop element
+      will be restricted to the width of the target element. The default is true
+      because that is what most uses of Drop want, like Select and Menu. Defaults to \`true\`.
 
 \`\`\`
 boolean
+align
 \`\`\`
 
 **target**
diff --git a/src/js/components/__tests__/__snapshots__/components-test.js.snap b/src/js/components/__tests__/__snapshots__/components-test.js.snap
index a886e49d77d..566266dc6e1 100644
--- a/src/js/components/__tests__/__snapshots__/components-test.js.snap
+++ b/src/js/components/__tests__/__snapshots__/components-test.js.snap
@@ -3227,10 +3227,12 @@ string",
       },
       Object {
         "defaultValue": true,
-        "description": "Whether the drop element should be stretched to at least match the
-      width of the target element. The default is true because
-      that is what most uses of Drop want, like Select and Menu.",
-        "format": "boolean",
+        "description": "If set to true the drop element will be stretched to at least match the
+      width of the target element. If set to align the width of the drop element
+      will be restricted to the width of the target element. The default is true
+      because that is what most uses of Drop want, like Select and Menu.",
+        "format": "boolean
+align",
         "name": "stretch",
       },
       Object {