Skip to content

Commit ecade96

Browse files
authored
Fix <input> adding unwanted width when options selected (#315)
* update readme coverage badges + unit tests for utils.ts * cleanup Examples.md markup * set minimal width on input when options are selected in MultiSelect component (closes #174)
1 parent 5ebd21c commit ecade96

File tree

6 files changed

+271
-33
lines changed

6 files changed

+271
-33
lines changed

readme.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,9 +36,9 @@
3636

3737
## 🧪 &thinsp; Coverage
3838

39-
| Statements | Branches | Lines |
40-
| ------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------ | -------------------------------------------------------------------------------- |
41-
| ![Statements](https://img.shields.io/badge/statements-97.94%25-brightgreen.svg?style=flat) | ![Branches](https://img.shields.io/badge/branches-79.39%25-red.svg?style=flat) | ![Lines](https://img.shields.io/badge/lines-97.94%25-brightgreen.svg?style=flat) |
39+
| Statements | Branches | Lines |
40+
| ------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------- | -------------------------------------------------------------------------------- |
41+
| ![Statements](https://img.shields.io/badge/statements-92.81%25-brightgreen.svg?style=flat) | ![Branches](https://img.shields.io/badge/branches-81.57%25-yellow.svg?style=flat) | ![Lines](https://img.shields.io/badge/lines-92.81%25-brightgreen.svg?style=flat) |
4242

4343
## 🔨 &thinsp; Installation
4444

src/lib/MultiSelect.svelte

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -971,6 +971,12 @@
971971
cursor: inherit; /* needed for disabled state */
972972
border-radius: 0; /* reset ul.selected > li */
973973
}
974+
975+
/* When options are selected, placeholder is hidden in which case we minimize input width to avoid adding unnecessary width to div.multiselect */
976+
:is(div.multiselect > ul.selected > input:not(:placeholder-shown)) {
977+
min-width: 1px; /* Minimal width to remain interactive */
978+
}
979+
974980
/* don't wrap ::placeholder rules in :is() as it seems to be overpowered by browser defaults i.t.o. specificity */
975981
div.multiselect > ul.selected > input::placeholder {
976982
padding-left: 5pt;

src/site/Examples.md

Lines changed: 16 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
## 🚀 &thinsp; Getting Started
22

3-
Simple examples to get you started:
4-
5-
### Basic Multi-Select
3+
<label for="fruits">Pick your favorite fruits <span>basic multi-select</span></label>
64

75
```svelte example
86
<script>
@@ -12,13 +10,12 @@ Simple examples to get you started:
1210
let selected = $state([])
1311
</script>
1412
15-
<h3>Pick your favorite fruits:</h3>
16-
<MultiSelect bind:selected options={fruits} placeholder="Choose fruits..." />
13+
<MultiSelect id="fruits" bind:selected options={fruits} placeholder="Choose fruits..." />
1714
1815
<p>You selected: {JSON.stringify(selected)}</p>
1916
```
2017

21-
### Single Select
18+
<label for="color">Pick one color <span>single-select with <code>maxSelect={1}</code></span></label>
2219

2320
```svelte example
2421
<script>
@@ -28,13 +25,12 @@ Simple examples to get you started:
2825
let value = $state(null)
2926
</script>
3027
31-
<h3>Pick one color:</h3>
32-
<MultiSelect bind:value options={colors} maxSelect={1} placeholder="Choose a color..." />
28+
<MultiSelect id="color" bind:value options={colors} maxSelect={1} placeholder="Choose a color..." />
3329
3430
<p>You selected: {JSON.stringify(value)}</p>
3531
```
3632

37-
### Object Options
33+
<label for="countries">Where have you lived? <span>options prop as array of objects</span></label>
3834

3935
```svelte example
4036
<script>
@@ -50,14 +46,13 @@ Simple examples to get you started:
5046
let selected = $state([])
5147
</script>
5248
53-
<h3>Where have you lived?</h3>
54-
<MultiSelect bind:selected options={countries} placeholder="Select countries..." />
49+
<MultiSelect id="countries" bind:selected options={countries} placeholder="Select countries..." />
5550
5651
<p>Selected countries: {selected.map(c => c.label).join(', ')}</p>
5752
<p>Country codes: {selected.map(c => c.value).join(', ')}</p>
5853
```
5954

60-
### With User-Created Options
55+
<label for="skills">Add your skills (you can define new ones) <span>user-created options</span></label>
6156

6257
```svelte example
6358
<script>
@@ -67,7 +62,6 @@ Simple examples to get you started:
6762
let selected = $state([])
6863
</script>
6964
70-
<h3>Add your skills (you can create new ones):</h3>
7165
<MultiSelect
7266
bind:selected
7367
options={initial_tags}
@@ -78,11 +72,9 @@ Simple examples to get you started:
7872
<p>Your skills: {JSON.stringify(selected)}</p>
7973
```
8074

81-
---
82-
83-
## 👇 &thinsp; Advanced Examples
75+
## 🔍 &thinsp; Advanced Examples
8476

85-
<label for="fav-languages">Favorite programming languages? <span>multi select with custom snippet</span></label>
77+
<label for="fav-languages">Favorite programming languages? <span>multi-select with custom snippet</span></label>
8678

8779
```svelte example collapsible repl="https://svelte.dev/repl/e3b88f59f62b498d943ecf7756ab75d7" stackblitz="src/site/Examples.md"
8880
<script lang="ts">
@@ -93,8 +85,6 @@ Simple examples to get you started:
9385
let selected: string[] = $state([])
9486
</script>
9587
96-
selected = {JSON.stringify(selected) || `[]`}
97-
9888
<MultiSelect
9989
id="fav-languages"
10090
options={languages}
@@ -105,9 +95,11 @@ selected = {JSON.stringify(selected) || `[]`}
10595
<LanguageSnippet {idx} {option} gap="1ex" />
10696
{/snippet}
10797
</MultiSelect>
98+
99+
selected = {JSON.stringify(selected) || `[]`}
108100
```
109101

110-
<label for="fav-ml-tool">Favorite machine learning tool? <span>single select with loading indicator on text input</span></label>
102+
<label for="fav-ml-tool">Favorite machine learning framework? <span>single-select with loading indicator on text input</span></label>
111103

112104
```svelte example collapsible repl="https://svelte.dev/repl/79e22e1905c94456aa21564b4d5f8759" stackblitz="src/site/Examples.md"
113105
<script lang="ts">
@@ -125,8 +117,6 @@ selected = {JSON.stringify(selected) || `[]`}
125117
})
126118
</script>
127119
128-
value = {JSON.stringify(value) || `null`}
129-
130120
<MultiSelect
131121
id="fav-ml-tool"
132122
maxSelect={1}
@@ -137,6 +127,8 @@ value = {JSON.stringify(value) || `null`}
137127
{loading}
138128
placeholder="Favorite machine learning tool?"
139129
/>
130+
131+
value = {JSON.stringify(value) || `null`}
140132
```
141133

142134
<label for="confetti-select">Chance of Confetti <span>max select with custom filter function and callback on item selection</span></label>
@@ -246,22 +238,16 @@ value = {JSON.stringify(value) || `null`}
246238
maxOptions <input type="range" min=0 max={30} bind:value={maxOptions}>
247239
{maxOptions} <small>(0 means no limit)</small>
248240
</label>
241+
```
249242

250243
<style>
251244
label {
252245
display: flex;
246+
margin: 1em 0 1ex;
253247
align-items: center;
254248
gap: 5pt;
255249
font-weight: normal;
256250
}
257-
</style>
258-
```
259-
260-
<style>
261-
label:not(:first-of-type) {
262-
padding-top: 2em;
263-
display: block;
264-
}
265251
label span {
266252
font-weight: 100;
267253
margin-left: 1em;

tests/MultiSelect.test.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -764,3 +764,30 @@ test.describe(`portal feature`, () => {
764764
await expect(modal_content).toBeHidden()
765765
})
766766
})
767+
768+
test(`input width minimizes when options are selected`, async ({ page }) => {
769+
await page.goto(`/ui`, { waitUntil: `networkidle` })
770+
const input = page.locator(`#foods input[autocomplete]`)
771+
772+
// Normal width when no selection (placeholder shown)
773+
const init_input_width = await input.evaluate(
774+
(el) => getComputedStyle(el).minWidth,
775+
)
776+
expect(init_input_width).toBe(`32px`)
777+
778+
await input.click() // Select any option to hide placeholder
779+
await page.click(`text=🍌 Banana`)
780+
781+
// Minimal width when selection exists (placeholder hidden)
782+
const input_width_w_selected = await input.evaluate(
783+
(el) => getComputedStyle(el).minWidth,
784+
)
785+
expect(input_width_w_selected).toBe(`1px`)
786+
787+
// Width is reset when option is removed
788+
await page.click(`button[title='Remove 🍌 Banana']`)
789+
const input_width_w_no_selected = await input.evaluate(
790+
(el) => getComputedStyle(el).minWidth,
791+
)
792+
expect(input_width_w_no_selected).toBe(init_input_width)
793+
})

0 commit comments

Comments
 (0)